Coverage Report

Created: 2026-05-16 07:03

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/mupdf/source/pdf/pdf-page.c
Line
Count
Source
1
// Copyright (C) 2004-2026 Artifex Software, Inc.
2
//
3
// This file is part of MuPDF.
4
//
5
// MuPDF is free software: you can redistribute it and/or modify it under the
6
// terms of the GNU Affero General Public License as published by the Free
7
// Software Foundation, either version 3 of the License, or (at your option)
8
// any later version.
9
//
10
// MuPDF is distributed in the hope that it will be useful, but WITHOUT ANY
11
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
12
// FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
13
// details.
14
//
15
// You should have received a copy of the GNU Affero General Public License
16
// along with MuPDF. If not, see <https://www.gnu.org/licenses/agpl-3.0.en.html>
17
//
18
// Alternative licensing terms are available from the licensor.
19
// For commercial licensing, see <https://www.artifex.com/> or contact
20
// Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco,
21
// CA 94129, USA, for further information.
22
23
#include "mupdf/fitz.h"
24
#include "pdf-annot-imp.h"
25
26
#include <stdlib.h>
27
#include <string.h>
28
#include <limits.h>
29
30
static void pdf_adjust_page_labels(fz_context *ctx, pdf_document *doc, int index, int adjust);
31
32
void
33
pdf_set_page_tree_cache(fz_context *ctx, pdf_document *doc, int enabled)
34
0
{
35
0
  if (enabled)
36
0
  {
37
0
    doc->use_page_tree_map = 1;
38
0
  }
39
0
  else
40
0
  {
41
0
    pdf_drop_page_tree_internal(ctx, doc);
42
0
    doc->use_page_tree_map = 0;
43
0
  }
44
0
}
45
46
int
47
pdf_count_pages(fz_context *ctx, pdf_document *doc)
48
1.17k
{
49
1.17k
  int pages;
50
1.17k
  if (doc->is_fdf)
51
0
    return 0;
52
  /* FIXME: We should reset linear_page_count to 0 when editing starts
53
   * (or when linear loading ends) */
54
1.17k
  if (doc->linear_page_count != 0)
55
0
    pages = doc->linear_page_count;
56
1.17k
  else
57
1.17k
    pages = pdf_to_int(ctx, pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/Pages/Count"));
58
1.17k
  if (pages < 0)
59
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "Invalid number of pages");
60
  /* set an upper bound to cope with malicious /Count entries; as there
61
   * can't possibly be more pages than objects in the file! */
62
1.17k
  if (pages >= pdf_xref_len(ctx, doc))
63
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "Invalid number of pages");
64
1.17k
  return pages;
65
1.17k
}
66
67
int pdf_count_pages_imp(fz_context *ctx, fz_document *doc, int chapter)
68
767
{
69
767
  return pdf_count_pages(ctx, (pdf_document*)doc);
70
767
}
71
72
static int
73
pdf_load_page_tree_imp(fz_context *ctx, pdf_document *doc, pdf_obj *node, int idx, pdf_cycle_list *cycle_up)
74
420
{
75
420
  pdf_cycle_list cycle;
76
420
  pdf_obj *type = pdf_dict_get(ctx, node, PDF_NAME(Type));
77
420
  if (pdf_name_eq(ctx, type, PDF_NAME(Pages)))
78
115
  {
79
115
    pdf_obj *kids = pdf_dict_get(ctx, node, PDF_NAME(Kids));
80
115
    int i, n = pdf_array_len(ctx, kids);
81
115
    if (pdf_cycle(ctx, &cycle, cycle_up, node))
82
0
      fz_throw(ctx, FZ_ERROR_FORMAT, "cycle in page tree");
83
479
    for (i = 0; i < n; ++i)
84
364
      idx = pdf_load_page_tree_imp(ctx, doc, pdf_array_get(ctx, kids, i), idx, &cycle);
85
115
  }
86
305
  else if (pdf_name_eq(ctx, type, PDF_NAME(Page)))
87
300
  {
88
300
    if (idx >= doc->map_page_count)
89
0
      fz_throw(ctx, FZ_ERROR_FORMAT, "too many kids in page tree");
90
300
    doc->rev_page_map[idx].page = idx;
91
300
    doc->rev_page_map[idx].object = pdf_to_num(ctx, node);
92
300
    doc->fwd_page_map[idx] = pdf_keep_obj(ctx, node);
93
300
    ++idx;
94
300
  }
95
5
  else
96
5
  {
97
5
    fz_throw(ctx, FZ_ERROR_FORMAT, "non-page object in page tree");
98
5
  }
99
415
  return idx;
100
420
}
101
102
static int
103
cmp_rev_page_map(const void *va, const void *vb)
104
487
{
105
487
  const pdf_rev_page_map *a = va;
106
487
  const pdf_rev_page_map *b = vb;
107
487
  return a->object - b->object;
108
487
}
109
110
void
111
pdf_load_page_tree(fz_context *ctx, pdf_document *doc)
112
0
{
113
  /* Noop now. */
114
0
}
115
116
void
117
pdf_drop_page_tree_internal(fz_context *ctx, pdf_document *doc)
118
124
{
119
124
  int i;
120
124
  fz_free(ctx, doc->rev_page_map);
121
124
  doc->rev_page_map = NULL;
122
124
  if (doc->fwd_page_map)
123
410
    for (i = 0; i < doc->map_page_count; i++)
124
354
      pdf_drop_obj(ctx, doc->fwd_page_map[i]);
125
124
  fz_free(ctx, doc->fwd_page_map);
126
124
  doc->fwd_page_map = NULL;
127
124
  doc->map_page_count = 0;
128
124
}
129
130
static void
131
pdf_load_page_tree_internal(fz_context *ctx, pdf_document *doc)
132
56
{
133
56
  int in_op = 0;
134
135
56
  fz_var(in_op);
136
137
  /* Check we're not already loaded. */
138
56
  if (doc->fwd_page_map != NULL)
139
0
    return;
140
141
  /* At this point we're trusting that only 1 thread should be doing
142
   * stuff that hits the document at a time. */
143
112
  fz_try(ctx)
144
112
  {
145
56
    int idx;
146
147
56
    doc->map_page_count = pdf_count_pages(ctx, doc);
148
56
    while (1)
149
56
    {
150
56
      doc->rev_page_map = Memento_label(fz_calloc(ctx, doc->map_page_count, sizeof(pdf_rev_page_map)), "pdf_rev_page_map");
151
56
      doc->fwd_page_map = Memento_label(fz_calloc(ctx, doc->map_page_count, sizeof(pdf_obj *)), "pdf_fwd_page_map");
152
56
      idx = pdf_load_page_tree_imp(ctx, doc, pdf_dict_getp(ctx, pdf_trailer(ctx, doc), "Root/Pages"), 0, NULL);
153
56
      if (idx < doc->map_page_count)
154
0
      {
155
        /* The document claims more pages that it has. Fix that. */
156
0
        fz_warn(ctx, "Document claims to have %d pages, but only has %d.", doc->map_page_count, idx);
157
        /* This put drops the page tree! */
158
0
        pdf_begin_implicit_operation(ctx, doc);
159
0
        in_op = 1;
160
0
        pdf_dict_putp_drop(ctx, pdf_trailer(ctx, doc), "Root/Pages/Count", pdf_new_int(ctx, idx));
161
0
        doc->map_page_count = idx;
162
0
        pdf_end_operation(ctx, doc);
163
0
        in_op = 0;
164
0
        continue;
165
0
      }
166
56
      break;
167
56
    }
168
56
    if (doc->map_page_count > 0)
169
51
      qsort(doc->rev_page_map, doc->map_page_count, sizeof *doc->rev_page_map, cmp_rev_page_map);
170
56
  }
171
112
  fz_catch(ctx)
172
5
  {
173
5
    if (in_op)
174
0
      pdf_abandon_operation(ctx, doc);
175
5
    pdf_drop_page_tree_internal(ctx, doc);
176
5
    fz_rethrow(ctx);
177
5
  }
178
56
}
179
180
void
181
pdf_drop_page_tree(fz_context *ctx, pdf_document *doc)
182
0
{
183
  /* Historical entry point. Now does nothing. We drop 'just in time'. */
184
0
}
185
186
static pdf_obj *
187
pdf_lookup_page_loc_imp(fz_context *ctx, pdf_document *doc, pdf_obj *node, int *skip, pdf_obj **parentp, int *indexp)
188
70
{
189
70
  pdf_mark_list mark_list;
190
70
  pdf_obj *kids;
191
70
  pdf_obj *hit = NULL;
192
70
  int i, len;
193
194
70
  pdf_mark_list_init(ctx, &mark_list);
195
196
140
  fz_try(ctx)
197
140
  {
198
70
    do
199
140
    {
200
140
      kids = pdf_dict_get(ctx, node, PDF_NAME(Kids));
201
140
      len = pdf_array_len(ctx, kids);
202
203
140
      if (len == 0)
204
0
        fz_throw(ctx, FZ_ERROR_FORMAT, "malformed page tree");
205
206
140
      if (pdf_mark_list_push(ctx, &mark_list, node))
207
0
        fz_throw(ctx, FZ_ERROR_FORMAT, "cycle in page tree");
208
209
345
      for (i = 0; i < len; i++)
210
345
      {
211
345
        pdf_obj *kid = pdf_array_get(ctx, kids, i);
212
345
        pdf_obj *type = pdf_dict_get(ctx, kid, PDF_NAME(Type));
213
345
        if (type ? pdf_name_eq(ctx, type, PDF_NAME(Pages)) : pdf_dict_get(ctx, kid, PDF_NAME(Kids)) && !pdf_dict_get(ctx, kid, PDF_NAME(MediaBox)))
214
120
        {
215
120
          int count = pdf_dict_get_int(ctx, kid, PDF_NAME(Count));
216
120
          if (*skip < count)
217
70
          {
218
70
            node = kid;
219
70
            break;
220
70
          }
221
50
          else
222
50
          {
223
50
            *skip -= count;
224
50
          }
225
120
        }
226
225
        else
227
225
        {
228
225
          if (type ? !pdf_name_eq(ctx, type, PDF_NAME(Page)) : !pdf_dict_get(ctx, kid, PDF_NAME(MediaBox)))
229
55
            fz_warn(ctx, "non-page object in page tree (%s)", pdf_to_name(ctx, type));
230
225
          if (*skip == 0)
231
70
          {
232
70
            if (parentp) *parentp = node;
233
70
            if (indexp) *indexp = i;
234
70
            hit = kid;
235
70
            break;
236
70
          }
237
155
          else
238
155
          {
239
155
            (*skip)--;
240
155
          }
241
225
        }
242
345
      }
243
140
    }
244
    /* If i < len && hit != NULL the desired page was found in the
245
    Kids array, done. If i < len && hit == NULL the found page tree
246
    node contains a Kids array that contains the desired page, loop
247
    back to top to extract it. When i == len the Kids array has been
248
    exhausted without finding the desired page, give up.
249
    */
250
140
    while (hit == NULL && i < len);
251
70
  }
252
140
  fz_always(ctx)
253
70
  {
254
70
    pdf_mark_list_free(ctx, &mark_list);
255
70
  }
256
70
  fz_catch(ctx)
257
0
  {
258
0
    fz_rethrow(ctx);
259
0
  }
260
261
70
  return hit;
262
70
}
263
264
pdf_obj *
265
pdf_lookup_page_loc(fz_context *ctx, pdf_document *doc, int needle, pdf_obj **parentp, int *indexp)
266
70
{
267
70
  pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root));
268
70
  pdf_obj *node = pdf_dict_get(ctx, root, PDF_NAME(Pages));
269
70
  int skip = needle;
270
70
  pdf_obj *hit;
271
272
70
  if (!node)
273
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot find page tree");
274
275
70
  hit = pdf_lookup_page_loc_imp(ctx, doc, node, &skip, parentp, indexp);
276
70
  if (!hit)
277
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "cannot find page %d in page tree", needle+1);
278
70
  return hit;
279
70
}
280
281
pdf_obj *
282
pdf_lookup_page_obj(fz_context *ctx, pdf_document *doc, int needle)
283
354
{
284
354
  if (doc->fwd_page_map == NULL && doc->use_page_tree_map)
285
56
  {
286
112
    fz_try(ctx)
287
112
      pdf_load_page_tree_internal(ctx, doc);
288
112
    fz_catch(ctx)
289
5
    {
290
5
      doc->use_page_tree_map = 0;
291
5
      fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
292
5
      fz_report_error(ctx);
293
5
      fz_warn(ctx, "Page tree load failed. Falling back to slow lookup");
294
5
    }
295
56
  }
296
297
354
  if (doc->fwd_page_map)
298
284
  {
299
284
    if (needle < 0 || needle >= doc->map_page_count)
300
0
      fz_throw(ctx, FZ_ERROR_FORMAT, "cannot find page %d in page tree", needle+1);
301
284
    if (doc->fwd_page_map[needle] != NULL)
302
284
      return doc->fwd_page_map[needle];
303
284
  }
304
305
70
  return pdf_lookup_page_loc(ctx, doc, needle, NULL, NULL);
306
354
}
307
308
static int
309
pdf_count_pages_before_kid(fz_context *ctx, pdf_document *doc, pdf_obj *parent, int kid_num)
310
0
{
311
0
  pdf_obj *kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids));
312
0
  int i, total = 0, len = pdf_array_len(ctx, kids);
313
0
  for (i = 0; i < len; i++)
314
0
  {
315
0
    pdf_obj *kid = pdf_array_get(ctx, kids, i);
316
0
    if (pdf_to_num(ctx, kid) == kid_num)
317
0
      return total;
318
0
    if (pdf_name_eq(ctx, pdf_dict_get(ctx, kid, PDF_NAME(Type)), PDF_NAME(Pages)))
319
0
    {
320
0
      pdf_obj *count = pdf_dict_get(ctx, kid, PDF_NAME(Count));
321
0
      int n = pdf_to_int(ctx, count);
322
0
      if (!pdf_is_int(ctx, count) || n < 0 || INT_MAX - total <= n)
323
0
        fz_throw(ctx, FZ_ERROR_FORMAT, "illegal or missing count in pages tree");
324
0
      total += n;
325
0
    }
326
0
    else
327
0
      total++;
328
0
  }
329
0
  return -1; // the page we're looking for is not in the page tree (it has been deleted)
330
0
}
331
332
static int
333
pdf_lookup_page_number_slow(fz_context *ctx, pdf_document *doc, pdf_obj *node)
334
0
{
335
0
  pdf_mark_list mark_list;
336
0
  int needle = pdf_to_num(ctx, node);
337
0
  int total = 0;
338
0
  int n;
339
0
  pdf_obj *parent;
340
341
0
  if (!pdf_name_eq(ctx, pdf_dict_get(ctx, node, PDF_NAME(Type)), PDF_NAME(Page)))
342
0
  {
343
0
    fz_warn(ctx, "invalid page object");
344
0
    return -1;
345
0
  }
346
347
0
  pdf_mark_list_init(ctx, &mark_list);
348
0
  parent = pdf_dict_get(ctx, node, PDF_NAME(Parent));
349
0
  fz_try(ctx)
350
0
  {
351
0
    while (pdf_is_dict(ctx, parent))
352
0
    {
353
0
      if (pdf_mark_list_push(ctx, &mark_list, parent))
354
0
        fz_throw(ctx, FZ_ERROR_FORMAT, "cycle in page tree (parents)");
355
0
      n = pdf_count_pages_before_kid(ctx, doc, parent, needle);
356
357
      // Page was not found in page tree!
358
0
      if (n < 0)
359
0
      {
360
0
        total = -1;
361
0
        break;
362
0
      }
363
0
      if (INT_MAX - total <= n)
364
0
        fz_throw(ctx, FZ_ERROR_FORMAT, "illegal or missing count in pages tree");
365
366
0
      total += n;
367
0
      needle = pdf_to_num(ctx, parent);
368
0
      parent = pdf_dict_get(ctx, parent, PDF_NAME(Parent));
369
0
    }
370
0
  }
371
0
  fz_always(ctx)
372
0
    pdf_mark_list_free(ctx, &mark_list);
373
0
  fz_catch(ctx)
374
0
    fz_rethrow(ctx);
375
376
0
  return total;
377
0
}
378
379
static int
380
pdf_lookup_page_number_fast(fz_context *ctx, pdf_document *doc, int needle)
381
0
{
382
0
  int l = 0;
383
0
  int r = doc->map_page_count - 1;
384
0
  while (l <= r)
385
0
  {
386
0
    int m = (l + r) >> 1;
387
0
    int c = needle - doc->rev_page_map[m].object;
388
0
    if (c < 0)
389
0
      r = m - 1;
390
0
    else if (c > 0)
391
0
      l = m + 1;
392
0
    else
393
0
      return doc->rev_page_map[m].page;
394
0
  }
395
0
  return -1;
396
0
}
397
398
int
399
pdf_lookup_page_number(fz_context *ctx, pdf_document *doc, pdf_obj *page)
400
0
{
401
0
  if (doc->rev_page_map == NULL && doc->use_page_tree_map)
402
0
  {
403
0
    fz_try(ctx)
404
0
      pdf_load_page_tree_internal(ctx, doc);
405
0
    fz_catch(ctx)
406
0
    {
407
0
      doc->use_page_tree_map = 0;
408
0
      fz_report_error(ctx);
409
0
      fz_warn(ctx, "Page tree load failed. Falling back to slow lookup.");
410
0
    }
411
0
  }
412
413
0
  if (doc->rev_page_map)
414
0
    return pdf_lookup_page_number_fast(ctx, doc, pdf_to_num(ctx, page));
415
0
  else
416
0
    return pdf_lookup_page_number_slow(ctx, doc, page);
417
0
}
418
419
static void
420
pdf_flatten_inheritable_page_item(fz_context *ctx, pdf_obj *page, pdf_obj *key)
421
0
{
422
0
  pdf_obj *val = pdf_dict_get_inheritable(ctx, page, key);
423
0
  if (val)
424
0
    pdf_dict_put(ctx, page, key, val);
425
0
}
426
427
void
428
pdf_flatten_inheritable_page_items(fz_context *ctx, pdf_obj *page)
429
0
{
430
0
  pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(MediaBox));
431
0
  pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(CropBox));
432
0
  pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(Rotate));
433
0
  pdf_flatten_inheritable_page_item(ctx, page, PDF_NAME(Resources));
434
0
}
435
436
/* We need to know whether to install a page-level transparency group */
437
438
/*
439
 * Object memo flags - allows us to secretly remember "a memo" (a bool) in an
440
 * object, and to read back whether there was a memo, and if so, what it was.
441
 */
442
enum
443
{
444
  PDF_FLAGS_MEMO_BM = 0,
445
  PDF_FLAGS_MEMO_OP = 1
446
};
447
448
static int pdf_resources_use_blending(fz_context *ctx, pdf_obj *rdb, pdf_cycle_list *cycle_up);
449
450
static int
451
pdf_extgstate_uses_blending(fz_context *ctx, pdf_obj *dict)
452
205
{
453
205
  pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(BM));
454
205
  if (obj && !pdf_name_eq(ctx, obj, PDF_NAME(Normal)))
455
20
    return 1;
456
185
  return 0;
457
205
}
458
459
int
460
pdf_pattern_uses_blending(fz_context *ctx, pdf_obj *dict, pdf_cycle_list *cycle_up)
461
1.11k
{
462
1.11k
  pdf_obj *obj;
463
1.11k
  pdf_cycle_list cycle;
464
1.11k
  if (pdf_cycle(ctx, &cycle, cycle_up, dict))
465
0
    return 0;
466
1.11k
  obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources));
467
1.11k
  if (pdf_resources_use_blending(ctx, obj, &cycle))
468
1.10k
    return 1;
469
14
  obj = pdf_dict_get(ctx, dict, PDF_NAME(ExtGState));
470
14
  return pdf_extgstate_uses_blending(ctx, obj);
471
1.11k
}
472
473
static int
474
pdf_xobject_uses_blending(fz_context *ctx, pdf_obj *dict, pdf_cycle_list *cycle_up)
475
1.34k
{
476
1.34k
  pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources));
477
1.34k
  pdf_cycle_list cycle;
478
1.34k
  if (pdf_cycle(ctx, &cycle, cycle_up, dict))
479
0
    return 0;
480
1.34k
  if (pdf_name_eq(ctx, pdf_dict_getp(ctx, dict, "Group/S"), PDF_NAME(Transparency)))
481
14
    return 1;
482
1.33k
  if (pdf_name_eq(ctx, pdf_dict_get(ctx, dict, PDF_NAME(Subtype)), PDF_NAME(Image)) &&
483
1.12k
    pdf_dict_get(ctx, dict, PDF_NAME(SMask)) != NULL)
484
1.11k
    return 1;
485
216
  return pdf_resources_use_blending(ctx, obj, &cycle);
486
1.33k
}
487
488
static int
489
pdf_resources_use_blending(fz_context *ctx, pdf_obj *rdb, pdf_cycle_list *cycle_up)
490
2.29k
{
491
2.29k
  pdf_cycle_list cycle;
492
2.29k
  pdf_obj *obj;
493
2.29k
  int i, n, useBM = 0;
494
495
2.29k
  if (!rdb)
496
114
    return 0;
497
498
  /* Have we been here before and remembered an answer? */
499
2.18k
  if (pdf_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_BM, &useBM))
500
143
    return useBM;
501
502
  /* stop on cyclic resource dependencies */
503
2.03k
  if (pdf_cycle(ctx, &cycle, cycle_up, rdb))
504
0
    return 0;
505
506
2.03k
  obj = pdf_dict_get(ctx, rdb, PDF_NAME(ExtGState));
507
2.03k
  n = pdf_dict_len(ctx, obj);
508
2.20k
  for (i = 0; i < n; i++)
509
191
    if (pdf_extgstate_uses_blending(ctx, pdf_dict_get_val(ctx, obj, i)))
510
20
      goto found;
511
512
2.01k
  obj = pdf_dict_get(ctx, rdb, PDF_NAME(Pattern));
513
2.01k
  n = pdf_dict_len(ctx, obj);
514
2.02k
  for (i = 0; i < n; i++)
515
30
    if (pdf_pattern_uses_blending(ctx, pdf_dict_get_val(ctx, obj, i), &cycle))
516
23
      goto found;
517
518
1.99k
  obj = pdf_dict_get(ctx, rdb, PDF_NAME(XObject));
519
1.99k
  n = pdf_dict_len(ctx, obj);
520
2.15k
  for (i = 0; i < n; i++)
521
1.34k
    if (pdf_xobject_uses_blending(ctx, pdf_dict_get_val(ctx, obj, i), &cycle))
522
1.19k
      goto found;
523
804
  if (0)
524
0
  {
525
1.23k
found:
526
1.23k
    useBM = 1;
527
1.23k
  }
528
529
2.03k
  pdf_set_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_BM, useBM);
530
2.03k
  return useBM;
531
804
}
532
533
static int pdf_resources_use_overprint(fz_context *ctx, pdf_obj *rdb, pdf_cycle_list *cycle_up);
534
535
static int
536
pdf_extgstate_uses_overprint(fz_context *ctx, pdf_obj *dict)
537
2.14k
{
538
2.14k
  pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(OP));
539
2.14k
  if (obj && pdf_to_bool(ctx, obj))
540
10
    return 1;
541
2.13k
  return 0;
542
2.14k
}
543
544
static int
545
pdf_pattern_uses_overprint(fz_context *ctx, pdf_obj *dict, pdf_cycle_list *cycle_up)
546
1.22k
{
547
1.22k
  pdf_obj *obj;
548
1.22k
  pdf_cycle_list cycle;
549
1.22k
  if (pdf_cycle(ctx, &cycle, cycle_up, dict))
550
0
    return 0;
551
1.22k
  obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources));
552
1.22k
  if (pdf_resources_use_overprint(ctx, obj, &cycle))
553
0
    return 1;
554
1.22k
  obj = pdf_dict_get(ctx, dict, PDF_NAME(ExtGState));
555
1.22k
  return pdf_extgstate_uses_overprint(ctx, obj);
556
1.22k
}
557
558
static int
559
pdf_xobject_uses_overprint(fz_context *ctx, pdf_obj *dict, pdf_cycle_list *cycle_up)
560
3.72k
{
561
3.72k
  pdf_obj *obj = pdf_dict_get(ctx, dict, PDF_NAME(Resources));
562
3.72k
  pdf_cycle_list cycle;
563
3.72k
  if (pdf_cycle(ctx, &cycle, cycle_up, dict))
564
0
    return 0;
565
3.72k
  return pdf_resources_use_overprint(ctx, obj, &cycle);
566
3.72k
}
567
568
static int
569
pdf_resources_use_overprint(fz_context *ctx, pdf_obj *rdb, pdf_cycle_list *cycle_up)
570
5.91k
{
571
5.91k
  pdf_cycle_list cycle;
572
5.91k
  pdf_obj *obj;
573
5.91k
  int i, n, useOP = 0;
574
575
5.91k
  if (!rdb)
576
3.67k
    return 0;
577
578
  /* Have we been here before and remembered an answer? */
579
2.23k
  if (pdf_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_OP, &useOP))
580
182
    return useOP;
581
582
  /* stop on cyclic resource dependencies */
583
2.05k
  if (pdf_cycle(ctx, &cycle, cycle_up, rdb))
584
0
    return 0;
585
586
2.05k
  obj = pdf_dict_get(ctx, rdb, PDF_NAME(ExtGState));
587
2.05k
  n = pdf_dict_len(ctx, obj);
588
2.95k
  for (i = 0; i < n; i++)
589
918
    if (pdf_extgstate_uses_overprint(ctx, pdf_dict_get_val(ctx, obj, i)))
590
10
      goto found;
591
592
2.04k
  obj = pdf_dict_get(ctx, rdb, PDF_NAME(Pattern));
593
2.04k
  n = pdf_dict_len(ctx, obj);
594
3.26k
  for (i = 0; i < n; i++)
595
1.22k
    if (pdf_pattern_uses_overprint(ctx, pdf_dict_get_val(ctx, obj, i), &cycle))
596
0
      goto found;
597
598
2.04k
  obj = pdf_dict_get(ctx, rdb, PDF_NAME(XObject));
599
2.04k
  n = pdf_dict_len(ctx, obj);
600
5.76k
  for (i = 0; i < n; i++)
601
3.72k
    if (pdf_xobject_uses_overprint(ctx, pdf_dict_get_val(ctx, obj, i), &cycle))
602
0
      goto found;
603
2.04k
  if (0)
604
0
  {
605
10
found:
606
10
    useOP = 1;
607
10
  }
608
609
2.05k
  pdf_set_obj_memo(ctx, rdb, PDF_FLAGS_MEMO_OP, useOP);
610
2.05k
  return useOP;
611
2.04k
}
612
613
fz_transition *
614
pdf_page_presentation(fz_context *ctx, pdf_page *page, fz_transition *transition, float *duration)
615
0
{
616
0
  pdf_obj *obj, *transdict;
617
618
0
  *duration = pdf_dict_get_real(ctx, page->obj, PDF_NAME(Dur));
619
620
0
  transdict = pdf_dict_get(ctx, page->obj, PDF_NAME(Trans));
621
0
  if (!transdict)
622
0
    return NULL;
623
624
0
  obj = pdf_dict_get(ctx, transdict, PDF_NAME(D));
625
626
0
  transition->duration = pdf_to_real_default(ctx, obj, 1);
627
628
0
  transition->vertical = !pdf_name_eq(ctx, pdf_dict_get(ctx, transdict, PDF_NAME(Dm)), PDF_NAME(H));
629
0
  transition->outwards = !pdf_name_eq(ctx, pdf_dict_get(ctx, transdict, PDF_NAME(M)), PDF_NAME(I));
630
  /* FIXME: If 'Di' is None, it should be handled differently, but
631
   * this only affects Fly, and we don't implement that currently. */
632
0
  transition->direction = (pdf_dict_get_int(ctx, transdict, PDF_NAME(Di)));
633
  /* FIXME: Read SS for Fly when we implement it */
634
  /* FIXME: Read B for Fly when we implement it */
635
636
0
  obj = pdf_dict_get(ctx, transdict, PDF_NAME(S));
637
0
  if (pdf_name_eq(ctx, obj, PDF_NAME(Split)))
638
0
    transition->type = FZ_TRANSITION_SPLIT;
639
0
  else if (pdf_name_eq(ctx, obj, PDF_NAME(Blinds)))
640
0
    transition->type = FZ_TRANSITION_BLINDS;
641
0
  else if (pdf_name_eq(ctx, obj, PDF_NAME(Box)))
642
0
    transition->type = FZ_TRANSITION_BOX;
643
0
  else if (pdf_name_eq(ctx, obj, PDF_NAME(Wipe)))
644
0
    transition->type = FZ_TRANSITION_WIPE;
645
0
  else if (pdf_name_eq(ctx, obj, PDF_NAME(Dissolve)))
646
0
    transition->type = FZ_TRANSITION_DISSOLVE;
647
0
  else if (pdf_name_eq(ctx, obj, PDF_NAME(Glitter)))
648
0
    transition->type = FZ_TRANSITION_GLITTER;
649
0
  else if (pdf_name_eq(ctx, obj, PDF_NAME(Fly)))
650
0
    transition->type = FZ_TRANSITION_FLY;
651
0
  else if (pdf_name_eq(ctx, obj, PDF_NAME(Push)))
652
0
    transition->type = FZ_TRANSITION_PUSH;
653
0
  else if (pdf_name_eq(ctx, obj, PDF_NAME(Cover)))
654
0
    transition->type = FZ_TRANSITION_COVER;
655
0
  else if (pdf_name_eq(ctx, obj, PDF_NAME(Uncover)))
656
0
    transition->type = FZ_TRANSITION_UNCOVER;
657
0
  else if (pdf_name_eq(ctx, obj, PDF_NAME(Fade)))
658
0
    transition->type = FZ_TRANSITION_FADE;
659
0
  else
660
0
    transition->type = FZ_TRANSITION_NONE;
661
662
0
  return transition;
663
0
}
664
665
fz_rect
666
pdf_bound_page(fz_context *ctx, pdf_page *page, fz_box_type box)
667
354
{
668
354
  fz_matrix page_ctm;
669
354
  fz_rect rect;
670
354
  pdf_page_transform_box(ctx, page, &rect, &page_ctm, box);
671
354
  return fz_transform_rect(rect, page_ctm);
672
354
}
673
674
static fz_rect
675
pdf_bound_page_imp(fz_context *ctx, fz_page *page, fz_box_type box)
676
354
{
677
354
  return pdf_bound_page(ctx, (pdf_page*)page, box);
678
354
}
679
680
void
681
pdf_set_page_box(fz_context *ctx, pdf_page *page, fz_box_type box, fz_rect rect)
682
0
{
683
0
  fz_matrix page_ctm, inv_page_ctm;
684
0
  fz_rect page_rect;
685
0
  pdf_page_transform_box(ctx, page, NULL, &page_ctm, box);
686
0
  inv_page_ctm = fz_invert_matrix(page_ctm);
687
0
  page_rect = fz_transform_rect(rect, inv_page_ctm);
688
689
0
  switch (box)
690
0
  {
691
0
  case FZ_MEDIA_BOX:
692
0
    pdf_dict_put_rect(ctx, page->obj, PDF_NAME(MediaBox), page_rect);
693
0
    break;
694
0
  case FZ_CROP_BOX:
695
0
    pdf_dict_put_rect(ctx, page->obj, PDF_NAME(CropBox), page_rect);
696
0
    break;
697
0
  case FZ_BLEED_BOX:
698
0
    pdf_dict_put_rect(ctx, page->obj, PDF_NAME(BleedBox), page_rect);
699
0
    break;
700
0
  case FZ_TRIM_BOX:
701
0
    pdf_dict_put_rect(ctx, page->obj, PDF_NAME(TrimBox), page_rect);
702
0
    break;
703
0
  case FZ_ART_BOX:
704
0
    pdf_dict_put_rect(ctx, page->obj, PDF_NAME(ArtBox), page_rect);
705
0
    break;
706
0
  case FZ_UNKNOWN_BOX:
707
0
    fz_throw(ctx, FZ_ERROR_UNSUPPORTED, "unknown page box type: %d", box);
708
0
  }
709
0
}
710
711
fz_link *
712
pdf_load_links(fz_context *ctx, pdf_page *page)
713
0
{
714
0
  return fz_keep_link(ctx, page->links);
715
0
}
716
717
static fz_link *
718
pdf_load_links_imp(fz_context *ctx, fz_page *page)
719
0
{
720
0
  return pdf_load_links(ctx, (pdf_page*)page);
721
0
}
722
723
pdf_obj *
724
pdf_page_resources(fz_context *ctx, pdf_page *page)
725
2.39k
{
726
2.39k
  return pdf_dict_get_inheritable(ctx, page->obj, PDF_NAME(Resources));
727
2.39k
}
728
729
pdf_obj *
730
pdf_page_contents(fz_context *ctx, pdf_page *page)
731
354
{
732
354
  return pdf_dict_get(ctx, page->obj, PDF_NAME(Contents));
733
354
}
734
735
pdf_obj *
736
pdf_page_group(fz_context *ctx, pdf_page *page)
737
96
{
738
96
  return pdf_dict_get(ctx, page->obj, PDF_NAME(Group));
739
96
}
740
741
void
742
pdf_page_obj_transform_box(fz_context *ctx, pdf_obj *pageobj, fz_rect *outbox, fz_matrix *page_ctm, fz_box_type box)
743
1.41k
{
744
1.41k
  pdf_obj *obj;
745
1.41k
  fz_rect usedbox, tempbox, cropbox, mediabox;
746
1.41k
  float userunit = 1;
747
1.41k
  int rotate;
748
749
1.41k
  if (!outbox)
750
0
    outbox = &tempbox;
751
752
1.41k
  userunit = pdf_dict_get_real_default(ctx, pageobj, PDF_NAME(UserUnit), 1);
753
754
1.41k
  obj = pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(MediaBox));
755
1.41k
  mediabox = pdf_to_rect(ctx, obj);
756
757
1.41k
  obj = NULL;
758
1.41k
  if (box == FZ_ART_BOX)
759
0
    obj = pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(ArtBox));
760
1.41k
  if (box == FZ_TRIM_BOX)
761
0
    obj = pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(TrimBox));
762
1.41k
  if (box == FZ_BLEED_BOX)
763
0
    obj = pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(BleedBox));
764
1.41k
  if (box == FZ_CROP_BOX || !obj)
765
1.41k
    obj = pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(CropBox));
766
1.41k
  if (box == FZ_MEDIA_BOX || !obj)
767
473
    usedbox = mediabox;
768
937
  else
769
937
  {
770
    // never use a box larger than fits the paper (mediabox)
771
937
    usedbox = fz_intersect_rect(mediabox, pdf_to_rect(ctx, obj));
772
937
  }
773
774
1.41k
  if (fz_is_empty_rect(usedbox))
775
28
    usedbox = fz_make_rect(0, 0, 612, 792);
776
1.41k
  usedbox.x0 = fz_min(usedbox.x0, usedbox.x1);
777
1.41k
  usedbox.y0 = fz_min(usedbox.y0, usedbox.y1);
778
1.41k
  usedbox.x1 = fz_max(usedbox.x0, usedbox.x1);
779
1.41k
  usedbox.y1 = fz_max(usedbox.y0, usedbox.y1);
780
1.41k
  if (usedbox.x1 - usedbox.x0 < 1 || usedbox.y1 - usedbox.y0 < 1)
781
0
    usedbox = fz_unit_rect;
782
783
1.41k
  *outbox = usedbox;
784
785
  /* Snap page rotation to 0, 90, 180 or 270 */
786
1.41k
  rotate = pdf_dict_get_inheritable_int(ctx, pageobj, PDF_NAME(Rotate));
787
1.41k
  if (rotate < 0)
788
0
    rotate = 360 - ((-rotate) % 360);
789
1.41k
  if (rotate >= 360)
790
0
    rotate = rotate % 360;
791
1.41k
  rotate = 90*((rotate + 45)/90);
792
1.41k
  if (rotate >= 360)
793
0
    rotate = 0;
794
795
  /* Compute transform from fitz' page space (upper left page origin, y descending, 72 dpi)
796
   * to PDF user space (arbitrary page origin, y ascending, UserUnit dpi). */
797
798
  /* Make left-handed and scale by UserUnit */
799
1.41k
  *page_ctm = fz_scale(userunit, -userunit);
800
801
  /* Rotate */
802
1.41k
  *page_ctm = fz_pre_rotate(*page_ctm, -rotate);
803
804
  /* Always use CropBox to set origin to top left */
805
1.41k
  obj = pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(CropBox));
806
1.41k
  if (!pdf_is_array(ctx, obj))
807
473
    obj = pdf_dict_get_inheritable(ctx, pageobj, PDF_NAME(MediaBox));
808
1.41k
  cropbox = pdf_to_rect(ctx, obj);
809
1.41k
  cropbox = fz_intersect_rect(cropbox, mediabox);
810
1.41k
  if (fz_is_empty_rect(cropbox))
811
28
    cropbox = fz_make_rect(0, 0, 612, 792);
812
1.41k
  cropbox.x0 = fz_min(cropbox.x0, cropbox.x1);
813
1.41k
  cropbox.y0 = fz_min(cropbox.y0, cropbox.y1);
814
1.41k
  cropbox.x1 = fz_max(cropbox.x0, cropbox.x1);
815
1.41k
  cropbox.y1 = fz_max(cropbox.y0, cropbox.y1);
816
1.41k
  if (cropbox.x1 - cropbox.x0 < 1 || cropbox.y1 - cropbox.y0 < 1)
817
0
    cropbox = fz_unit_rect;
818
819
  /* Translate page origin of CropBox to 0,0 */
820
1.41k
  cropbox = fz_transform_rect(cropbox, *page_ctm);
821
1.41k
  *page_ctm = fz_concat(*page_ctm, fz_translate(-cropbox.x0, -cropbox.y0));
822
1.41k
}
823
824
void
825
pdf_page_obj_transform(fz_context *ctx, pdf_obj *pageobj, fz_rect *page_cropbox, fz_matrix *page_ctm)
826
0
{
827
0
  pdf_page_obj_transform_box(ctx, pageobj, page_cropbox, page_ctm, FZ_CROP_BOX);
828
0
}
829
830
void
831
pdf_page_transform_box(fz_context *ctx, pdf_page *page, fz_rect *page_cropbox, fz_matrix *page_ctm, fz_box_type box)
832
1.41k
{
833
1.41k
  pdf_page_obj_transform_box(ctx, page->obj, page_cropbox, page_ctm, box);
834
1.41k
}
835
836
void
837
pdf_page_transform(fz_context *ctx, pdf_page *page, fz_rect *cropbox, fz_matrix *ctm)
838
1.05k
{
839
1.05k
  pdf_page_transform_box(ctx, page, cropbox, ctm, FZ_CROP_BOX);
840
1.05k
}
841
842
static void
843
find_seps(fz_context *ctx, fz_separations **seps, pdf_obj *obj, pdf_mark_list *clearme)
844
0
{
845
0
  int i, n;
846
0
  pdf_obj *nameobj, *cols;
847
848
0
  if (!obj)
849
0
    return;
850
851
  // Already seen this ColorSpace...
852
0
  if (pdf_mark_list_push(ctx, clearme, obj))
853
0
    return;
854
855
0
  nameobj = pdf_array_get(ctx, obj, 0);
856
0
  if (pdf_name_eq(ctx, nameobj, PDF_NAME(Separation)))
857
0
  {
858
0
    fz_colorspace *cs;
859
0
    const char *name = pdf_array_get_name(ctx, obj, 1);
860
861
    /* Skip 'special' colorants. */
862
0
    if (!strcmp(name, "Black") ||
863
0
      !strcmp(name, "Cyan") ||
864
0
      !strcmp(name, "Magenta") ||
865
0
      !strcmp(name, "Yellow") ||
866
0
      !strcmp(name, "All") ||
867
0
      !strcmp(name, "None"))
868
0
      return;
869
870
0
    n = fz_count_separations(ctx, *seps);
871
0
    for (i = 0; i < n; i++)
872
0
    {
873
0
      if (!strcmp(name, fz_separation_name(ctx, *seps, i)))
874
0
        return; /* Got that one already */
875
0
    }
876
877
0
    fz_try(ctx)
878
0
      cs = pdf_load_colorspace(ctx, obj);
879
0
    fz_catch(ctx)
880
0
    {
881
0
      fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
882
0
      fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
883
0
      fz_report_error(ctx);
884
0
      return; /* ignore broken colorspace */
885
0
    }
886
0
    fz_try(ctx)
887
0
    {
888
0
      if (!*seps)
889
0
        *seps = fz_new_separations(ctx, 0);
890
0
      fz_add_separation(ctx, *seps, name, cs, 0);
891
0
    }
892
0
    fz_always(ctx)
893
0
      fz_drop_colorspace(ctx, cs);
894
0
    fz_catch(ctx)
895
0
      fz_rethrow(ctx);
896
0
  }
897
0
  else if (pdf_name_eq(ctx, nameobj, PDF_NAME(Indexed)))
898
0
  {
899
0
    find_seps(ctx, seps, pdf_array_get(ctx, obj, 1), clearme);
900
0
  }
901
0
  else if (pdf_name_eq(ctx, nameobj, PDF_NAME(DeviceN)))
902
0
  {
903
    /* If the separation colorants exists for this DeviceN color space
904
     * add those prior to our search for DeviceN color */
905
0
    cols = pdf_dict_get(ctx, pdf_array_get(ctx, obj, 4), PDF_NAME(Colorants));
906
0
    n = pdf_dict_len(ctx, cols);
907
0
    for (i = 0; i < n; i++)
908
0
      find_seps(ctx, seps, pdf_dict_get_val(ctx, cols, i), clearme);
909
0
  }
910
0
}
911
912
static void
913
find_devn(fz_context *ctx, fz_separations **seps, pdf_obj *obj, pdf_mark_list *clearme)
914
0
{
915
0
  int i, j, n, m;
916
0
  pdf_obj *arr;
917
0
  pdf_obj *nameobj = pdf_array_get(ctx, obj, 0);
918
919
0
  if (!obj)
920
0
    return;
921
922
  // Already seen this ColorSpace...
923
0
  if (pdf_mark_list_push(ctx, clearme, obj))
924
0
    return;
925
926
0
  if (!pdf_name_eq(ctx, nameobj, PDF_NAME(DeviceN)))
927
0
    return;
928
929
0
  arr = pdf_array_get(ctx, obj, 1);
930
0
  m = pdf_array_len(ctx, arr);
931
0
  for (j = 0; j < m; j++)
932
0
  {
933
0
    fz_colorspace *cs;
934
0
    const char *name = pdf_array_get_name(ctx, arr, j);
935
936
    /* Skip 'special' colorants. */
937
0
    if (!strcmp(name, "Black") ||
938
0
      !strcmp(name, "Cyan") ||
939
0
      !strcmp(name, "Magenta") ||
940
0
      !strcmp(name, "Yellow") ||
941
0
      !strcmp(name, "All") ||
942
0
      !strcmp(name, "None"))
943
0
      continue;
944
945
0
    n = fz_count_separations(ctx, *seps);
946
0
    for (i = 0; i < n; i++)
947
0
    {
948
0
      if (!strcmp(name, fz_separation_name(ctx, *seps, i)))
949
0
        break; /* Got that one already */
950
0
    }
951
952
0
    if (i == n)
953
0
    {
954
0
      fz_try(ctx)
955
0
        cs = pdf_load_colorspace(ctx, obj);
956
0
      fz_catch(ctx)
957
0
      {
958
0
        fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
959
0
        fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
960
0
        fz_report_error(ctx);
961
0
        continue; /* ignore broken colorspace */
962
0
      }
963
0
      fz_try(ctx)
964
0
      {
965
0
        if (!*seps)
966
0
          *seps = fz_new_separations(ctx, 0);
967
0
        fz_add_separation(ctx, *seps, name, cs, j);
968
0
      }
969
0
      fz_always(ctx)
970
0
        fz_drop_colorspace(ctx, cs);
971
0
      fz_catch(ctx)
972
0
        fz_rethrow(ctx);
973
0
    }
974
0
  }
975
0
}
976
977
typedef void (res_finder_fn)(fz_context *ctx, fz_separations **seps, pdf_obj *obj, pdf_mark_list *clearme);
978
979
static void
980
scan_page_seps(fz_context *ctx, pdf_obj *res, fz_separations **seps, res_finder_fn *fn, pdf_mark_list *clearme)
981
0
{
982
0
  pdf_obj *dict;
983
0
  pdf_obj *obj;
984
0
  int i, n;
985
986
0
  if (!res)
987
0
    return;
988
989
  // Already seen this Resources...
990
0
  if (pdf_mark_list_push(ctx, clearme, res))
991
0
    return;
992
993
0
  dict = pdf_dict_get(ctx, res, PDF_NAME(ColorSpace));
994
0
  n = pdf_dict_len(ctx, dict);
995
0
  for (i = 0; i < n; i++)
996
0
  {
997
0
    obj = pdf_dict_get_val(ctx, dict, i);
998
0
    fn(ctx, seps, obj, clearme);
999
0
  }
1000
1001
0
  dict = pdf_dict_get(ctx, res, PDF_NAME(Shading));
1002
0
  n = pdf_dict_len(ctx, dict);
1003
0
  for (i = 0; i < n; i++)
1004
0
  {
1005
0
    obj = pdf_dict_get_val(ctx, dict, i);
1006
0
    fn(ctx, seps, pdf_dict_get(ctx, obj, PDF_NAME(ColorSpace)), clearme);
1007
0
  }
1008
1009
0
  dict = pdf_dict_get(ctx, res, PDF_NAME(Pattern));
1010
0
  n = pdf_dict_len(ctx, dict);
1011
0
  for (i = 0; i < n; i++)
1012
0
  {
1013
0
    pdf_obj *obj2;
1014
0
    obj = pdf_dict_get_val(ctx, dict, i);
1015
0
    obj2 = pdf_dict_get(ctx, obj, PDF_NAME(Shading));
1016
0
    fn(ctx, seps, pdf_dict_get(ctx, obj2, PDF_NAME(ColorSpace)), clearme);
1017
0
  }
1018
1019
0
  dict = pdf_dict_get(ctx, res, PDF_NAME(XObject));
1020
0
  n = pdf_dict_len(ctx, dict);
1021
0
  for (i = 0; i < n; i++)
1022
0
  {
1023
0
    obj = pdf_dict_get_val(ctx, dict, i);
1024
    // Already seen this XObject...
1025
0
    if (!pdf_mark_list_push(ctx, clearme, obj))
1026
0
    {
1027
0
      fn(ctx, seps, pdf_dict_get(ctx, obj, PDF_NAME(ColorSpace)), clearme);
1028
      /* Recurse on XObject forms. */
1029
0
      scan_page_seps(ctx, pdf_dict_get(ctx, obj, PDF_NAME(Resources)), seps, fn, clearme);
1030
0
    }
1031
0
  }
1032
0
}
1033
1034
fz_separations *
1035
pdf_page_separations(fz_context *ctx, pdf_page *page)
1036
0
{
1037
0
  pdf_obj *res = pdf_page_resources(ctx, page);
1038
0
  pdf_mark_list clearme;
1039
0
  fz_separations *seps = NULL;
1040
1041
0
  pdf_mark_list_init(ctx, &clearme);
1042
0
  fz_try(ctx)
1043
0
  {
1044
    /* Run through and look for separations first. This is
1045
     * because separations are simplest to deal with, and
1046
     * because DeviceN may be implemented on top of separations.
1047
     */
1048
0
    scan_page_seps(ctx, res, &seps, find_seps, &clearme);
1049
0
  }
1050
0
  fz_always(ctx)
1051
0
    pdf_mark_list_free(ctx, &clearme);
1052
0
  fz_catch(ctx)
1053
0
  {
1054
0
    fz_drop_separations(ctx, seps);
1055
0
    fz_rethrow(ctx);
1056
0
  }
1057
1058
0
  pdf_mark_list_init(ctx, &clearme);
1059
0
  fz_try(ctx)
1060
0
  {
1061
    /* Now run through again, and look for DeviceNs. These may
1062
     * have spot colors in that aren't defined in terms of
1063
     * separations. */
1064
0
    scan_page_seps(ctx, res, &seps, find_devn, &clearme);
1065
0
  }
1066
0
  fz_always(ctx)
1067
0
    pdf_mark_list_free(ctx, &clearme);
1068
0
  fz_catch(ctx)
1069
0
  {
1070
0
    fz_drop_separations(ctx, seps);
1071
0
    fz_rethrow(ctx);
1072
0
  }
1073
1074
0
  return seps;
1075
0
}
1076
1077
int
1078
pdf_page_uses_overprint(fz_context *ctx, pdf_page *page)
1079
0
{
1080
0
  return page ? page->overprint : 0;
1081
0
}
1082
1083
static void
1084
pdf_drop_page_imp(fz_context *ctx, fz_page *page_)
1085
354
{
1086
354
  pdf_page *page = (pdf_page*)page_;
1087
354
  pdf_annot *widget;
1088
354
  pdf_annot *annot;
1089
354
  pdf_link *link;
1090
1091
354
  link = (pdf_link *) page->links;
1092
354
  while (link)
1093
0
  {
1094
0
    link->page = NULL;
1095
0
    link = (pdf_link *) link->super.next;
1096
0
  }
1097
354
  fz_drop_link(ctx, page->links);
1098
354
  page->links = NULL;
1099
1100
354
  annot = page->annots;
1101
442
  while (annot)
1102
88
  {
1103
88
    annot->page = NULL;
1104
88
    annot = annot->next;
1105
88
  }
1106
354
  pdf_drop_annots(ctx, page->annots);
1107
354
  page->annots = NULL;
1108
1109
354
  widget = page->widgets;
1110
931
  while (widget)
1111
577
  {
1112
577
    widget->page = NULL;
1113
577
    widget = widget->next;
1114
577
  }
1115
354
  pdf_drop_widgets(ctx, page->widgets);
1116
354
  page->widgets = NULL;
1117
354
  pdf_drop_obj(ctx, page->obj);
1118
354
  page->obj = NULL;
1119
354
  page->doc = NULL;
1120
354
}
1121
1122
static void pdf_run_page_contents_imp(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix ctm, fz_cookie *cookie)
1123
354
{
1124
354
  pdf_run_page_contents(ctx, (pdf_page*)page, dev, ctm, cookie);
1125
354
}
1126
1127
static void pdf_run_page_annots_imp(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix ctm, fz_cookie *cookie)
1128
350
{
1129
350
  pdf_run_page_annots(ctx, (pdf_page*)page, dev, ctm, cookie);
1130
350
}
1131
1132
static void pdf_run_page_widgets_imp(fz_context *ctx, fz_page *page, fz_device *dev, fz_matrix ctm, fz_cookie *cookie)
1133
350
{
1134
350
  pdf_run_page_widgets(ctx, (pdf_page*)page, dev, ctm, cookie);
1135
350
}
1136
1137
static fz_transition * pdf_page_presentation_imp(fz_context *ctx, fz_page *page, fz_transition *transition, float *duration)
1138
0
{
1139
0
  return pdf_page_presentation(ctx, (pdf_page*)page, transition, duration);
1140
0
}
1141
1142
static fz_separations * pdf_page_separations_imp(fz_context *ctx, fz_page *page)
1143
0
{
1144
0
  return pdf_page_separations(ctx, (pdf_page*)page);
1145
0
}
1146
1147
static int pdf_page_uses_overprint_imp(fz_context *ctx, fz_page *page)
1148
0
{
1149
0
  return pdf_page_uses_overprint(ctx, (pdf_page*)page);
1150
0
}
1151
1152
static fz_link * pdf_create_link_imp(fz_context *ctx, fz_page *page, fz_rect bbox, const char *uri)
1153
0
{
1154
0
  return pdf_create_link(ctx, (pdf_page*)page, bbox, uri);
1155
0
}
1156
1157
static void pdf_delete_link_imp(fz_context *ctx, fz_page *page, fz_link *link)
1158
0
{
1159
0
  pdf_delete_link(ctx, (pdf_page*)page, link);
1160
0
}
1161
1162
static pdf_page *
1163
pdf_new_page(fz_context *ctx, pdf_document *doc)
1164
354
{
1165
354
  pdf_page *page = fz_new_derived_page(ctx, pdf_page, (fz_document*) doc);
1166
1167
354
  page->doc = doc; /* typecast alias for page->super.doc */
1168
1169
354
  page->super.drop_page = pdf_drop_page_imp;
1170
354
  page->super.load_links = pdf_load_links_imp;
1171
354
  page->super.bound_page = pdf_bound_page_imp;
1172
354
  page->super.run_page_contents = pdf_run_page_contents_imp;
1173
354
  page->super.run_page_annots = pdf_run_page_annots_imp;
1174
354
  page->super.run_page_widgets = pdf_run_page_widgets_imp;
1175
354
  page->super.page_presentation = pdf_page_presentation_imp;
1176
354
  page->super.separations = pdf_page_separations_imp;
1177
354
  page->super.overprint = pdf_page_uses_overprint_imp;
1178
354
  page->super.create_link = pdf_create_link_imp;
1179
354
  page->super.delete_link = pdf_delete_link_imp;
1180
1181
354
  page->obj = NULL;
1182
1183
354
  page->transparency = 0;
1184
354
  page->links = NULL;
1185
354
  page->annots = NULL;
1186
354
  page->annot_tailp = &page->annots;
1187
354
  page->widgets = NULL;
1188
354
  page->widget_tailp = &page->widgets;
1189
1190
354
  return page;
1191
354
}
1192
1193
static void
1194
pdf_load_default_colorspaces_imp(fz_context *ctx, fz_default_colorspaces *default_cs, pdf_obj *obj)
1195
829
{
1196
829
  pdf_obj *cs_obj;
1197
1198
  /* The spec says to ignore any colors we can't understand */
1199
1200
829
  cs_obj = pdf_dict_get(ctx, obj, PDF_NAME(DefaultGray));
1201
829
  if (cs_obj)
1202
0
  {
1203
0
    fz_try(ctx)
1204
0
    {
1205
0
      fz_colorspace *cs = pdf_load_colorspace(ctx, cs_obj);
1206
0
      fz_set_default_gray(ctx, default_cs, cs);
1207
0
      fz_drop_colorspace(ctx, cs);
1208
0
    }
1209
0
    fz_catch(ctx)
1210
0
    {
1211
0
      fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
1212
0
      fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
1213
0
      fz_report_error(ctx);
1214
0
    }
1215
0
  }
1216
1217
829
  cs_obj = pdf_dict_get(ctx, obj, PDF_NAME(DefaultRGB));
1218
829
  if (cs_obj)
1219
0
  {
1220
0
    fz_try(ctx)
1221
0
    {
1222
0
      fz_colorspace *cs = pdf_load_colorspace(ctx, cs_obj);
1223
0
      fz_set_default_rgb(ctx, default_cs, cs);
1224
0
      fz_drop_colorspace(ctx, cs);
1225
0
    }
1226
0
    fz_catch(ctx)
1227
0
    {
1228
0
      fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
1229
0
      fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
1230
0
      fz_report_error(ctx);
1231
0
    }
1232
0
  }
1233
1234
829
  cs_obj = pdf_dict_get(ctx, obj, PDF_NAME(DefaultCMYK));
1235
829
  if (cs_obj)
1236
0
  {
1237
0
    fz_try(ctx)
1238
0
    {
1239
0
      fz_colorspace *cs = pdf_load_colorspace(ctx, cs_obj);
1240
0
      fz_set_default_cmyk(ctx, default_cs, cs);
1241
0
      fz_drop_colorspace(ctx, cs);
1242
0
    }
1243
0
    fz_catch(ctx)
1244
0
    {
1245
0
      fz_rethrow_if(ctx, FZ_ERROR_TRYLATER);
1246
0
      fz_rethrow_if(ctx, FZ_ERROR_SYSTEM);
1247
0
      fz_report_error(ctx);
1248
0
    }
1249
0
  }
1250
829
}
1251
1252
fz_default_colorspaces *
1253
pdf_load_default_colorspaces(fz_context *ctx, pdf_document *doc, pdf_page *page)
1254
1.01k
{
1255
1.01k
  pdf_obj *res;
1256
1.01k
  pdf_obj *obj;
1257
1.01k
  fz_default_colorspaces *default_cs;
1258
1.01k
  fz_colorspace *oi;
1259
1260
1.01k
  default_cs = fz_new_default_colorspaces(ctx);
1261
1262
2.03k
  fz_try(ctx)
1263
2.03k
  {
1264
1.01k
    res = pdf_page_resources(ctx, page);
1265
1.01k
    obj = pdf_dict_get(ctx, res, PDF_NAME(ColorSpace));
1266
1.01k
    if (obj)
1267
475
      pdf_load_default_colorspaces_imp(ctx, default_cs, obj);
1268
1269
1.01k
    oi = pdf_document_output_intent(ctx, doc);
1270
1.01k
    if (oi)
1271
1
      fz_set_default_output_intent(ctx, default_cs, oi);
1272
1.01k
  }
1273
2.03k
  fz_catch(ctx)
1274
0
  {
1275
0
    if (fz_caught(ctx) != FZ_ERROR_TRYLATER)
1276
0
    {
1277
0
      fz_drop_default_colorspaces(ctx, default_cs);
1278
0
      fz_rethrow(ctx);
1279
0
    }
1280
0
    fz_ignore_error(ctx);
1281
0
    page->super.incomplete = 1;
1282
0
  }
1283
1284
1.01k
  return default_cs;
1285
1.01k
}
1286
1287
fz_default_colorspaces *
1288
pdf_update_default_colorspaces(fz_context *ctx, fz_default_colorspaces *old_cs, pdf_obj *res)
1289
5.80k
{
1290
5.80k
  pdf_obj *obj;
1291
5.80k
  fz_default_colorspaces *new_cs;
1292
1293
5.80k
  obj = pdf_dict_get(ctx, res, PDF_NAME(ColorSpace));
1294
5.80k
  if (!obj)
1295
5.45k
    return fz_keep_default_colorspaces(ctx, old_cs);
1296
1297
354
  new_cs = fz_clone_default_colorspaces(ctx, old_cs);
1298
708
  fz_try(ctx)
1299
708
    pdf_load_default_colorspaces_imp(ctx, new_cs, obj);
1300
708
  fz_catch(ctx)
1301
0
  {
1302
0
    fz_drop_default_colorspaces(ctx, new_cs);
1303
0
    fz_rethrow(ctx);
1304
0
  }
1305
1306
354
  return new_cs;
1307
354
}
1308
1309
void pdf_nuke_page(fz_context *ctx, pdf_page *page)
1310
0
{
1311
0
  pdf_nuke_links(ctx, page);
1312
0
  pdf_nuke_annots(ctx, page);
1313
0
  pdf_drop_obj(ctx, page->obj);
1314
0
  page->obj = NULL;
1315
0
  page->super.in_doc = 0;
1316
0
}
1317
1318
void pdf_sync_page(fz_context *ctx, pdf_page *page)
1319
0
{
1320
0
  pdf_sync_links(ctx, page);
1321
0
  pdf_sync_annots(ctx, page);
1322
0
}
1323
1324
void pdf_sync_open_pages(fz_context *ctx, pdf_document *doc)
1325
0
{
1326
0
  fz_page *page, *next;
1327
0
  pdf_page *ppage;
1328
0
  int number;
1329
1330
0
  for (page = doc->super.open; page != NULL; page = next)
1331
0
  {
1332
0
    next = page->next;
1333
0
    if (page->doc == NULL)
1334
0
      continue;
1335
0
    ppage = (pdf_page*)page;
1336
0
    number = pdf_lookup_page_number(ctx, doc, ppage->obj);
1337
0
    if (number < 0)
1338
0
    {
1339
0
      pdf_nuke_page(ctx, ppage);
1340
0
      if (next)
1341
0
        next->prev = page->prev;
1342
0
      if (page->prev)
1343
0
        *page->prev = page->next;
1344
0
    }
1345
0
    else
1346
0
    {
1347
0
      pdf_sync_page(ctx, ppage);
1348
0
      page->number = number;
1349
0
    }
1350
0
  }
1351
0
}
1352
1353
pdf_page *
1354
pdf_load_page(fz_context *ctx, pdf_document *doc, int number)
1355
0
{
1356
0
  return (pdf_page*)fz_load_page(ctx, (fz_document*)doc, number);
1357
0
}
1358
1359
int
1360
pdf_page_has_transparency(fz_context *ctx, pdf_page *page)
1361
0
{
1362
0
  return page->transparency;
1363
0
}
1364
1365
fz_page *
1366
pdf_load_page_imp(fz_context *ctx, fz_document *doc_, int chapter, int number)
1367
354
{
1368
354
  pdf_document *doc = (pdf_document*)doc_;
1369
354
  pdf_page *page;
1370
354
  pdf_annot *annot;
1371
354
  pdf_obj *pageobj, *obj;
1372
1373
354
  if (doc->is_fdf)
1374
0
    fz_throw(ctx, FZ_ERROR_FORMAT, "FDF documents have no pages");
1375
1376
354
  if (chapter != 0)
1377
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid chapter number: %d", chapter);
1378
1379
354
  if (number < 0 || number >= pdf_count_pages(ctx, doc))
1380
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "invalid page number: %d", number);
1381
1382
354
  if (doc->file_reading_linearly)
1383
0
  {
1384
0
    pageobj = pdf_progressive_advance(ctx, doc, number);
1385
0
    if (pageobj == NULL)
1386
0
      fz_throw(ctx, FZ_ERROR_TRYLATER, "page %d not available yet", number);
1387
0
  }
1388
354
  else
1389
354
    pageobj = pdf_lookup_page_obj(ctx, doc, number);
1390
1391
354
  page = pdf_new_page(ctx, doc);
1392
354
  page->obj = pdf_keep_obj(ctx, pageobj);
1393
1394
  /* Pre-load annotations and links */
1395
708
  fz_try(ctx)
1396
708
  {
1397
354
    obj = pdf_dict_get(ctx, pageobj, PDF_NAME(Annots));
1398
354
    if (obj)
1399
37
    {
1400
37
      fz_rect page_cropbox;
1401
37
      fz_matrix page_ctm;
1402
37
      pdf_page_transform(ctx, page, &page_cropbox, &page_ctm);
1403
37
      page->links = pdf_load_link_annots(ctx, doc, page, obj, number, page_ctm);
1404
37
      pdf_load_annots(ctx, page);
1405
37
    }
1406
354
  }
1407
708
  fz_catch(ctx)
1408
0
  {
1409
0
    if (fz_caught(ctx) != FZ_ERROR_TRYLATER)
1410
0
    {
1411
0
      fz_drop_page(ctx, &page->super);
1412
0
      fz_rethrow(ctx);
1413
0
    }
1414
0
    fz_ignore_error(ctx);
1415
0
    page->super.incomplete = 1;
1416
0
    fz_drop_link(ctx, page->links);
1417
0
    page->links = NULL;
1418
0
  }
1419
1420
  /* Scan for transparency and overprint */
1421
708
  fz_try(ctx)
1422
708
  {
1423
354
    pdf_obj *resources = pdf_page_resources(ctx, page);
1424
354
    if (pdf_name_eq(ctx, pdf_dict_getp(ctx, pageobj, "Group/S"), PDF_NAME(Transparency)))
1425
1
      page->transparency = 1;
1426
353
    else if (pdf_resources_use_blending(ctx, resources, NULL))
1427
75
      page->transparency = 1;
1428
354
    if (pdf_resources_use_overprint(ctx, resources, NULL))
1429
10
      page->overprint = 1;
1430
387
    for (annot = page->annots; annot && !page->transparency; annot = annot->next)
1431
33
    {
1432
66
      fz_try(ctx)
1433
66
      {
1434
33
        pdf_obj *ap;
1435
33
        pdf_obj *res;
1436
33
        pdf_annot_push_local_xref(ctx, annot);
1437
33
        ap = pdf_annot_ap(ctx, annot);
1438
33
        if (!ap)
1439
0
          break;
1440
33
        res = pdf_xobject_resources(ctx, ap);
1441
33
        if (pdf_resources_use_blending(ctx, res, NULL))
1442
20
          page->transparency = 1;
1443
33
        if (pdf_resources_use_overprint(ctx, pdf_xobject_resources(ctx, res), NULL))
1444
0
          page->overprint = 1;
1445
33
      }
1446
66
      fz_always(ctx)
1447
33
        pdf_annot_pop_local_xref(ctx, annot);
1448
33
      fz_catch(ctx)
1449
0
        fz_rethrow(ctx);
1450
33
    }
1451
931
    for (annot = page->widgets; annot && !page->transparency; annot = annot->next)
1452
577
    {
1453
1.15k
      fz_try(ctx)
1454
1.15k
      {
1455
577
        pdf_obj *ap;
1456
577
        pdf_obj *res;
1457
577
        pdf_annot_push_local_xref(ctx, annot);
1458
577
        ap = pdf_annot_ap(ctx, annot);
1459
577
        if (!ap)
1460
0
          break;
1461
577
        res = pdf_xobject_resources(ctx, ap);
1462
577
        if (pdf_resources_use_blending(ctx, res, NULL))
1463
0
          page->transparency = 1;
1464
577
        if (pdf_resources_use_overprint(ctx, pdf_xobject_resources(ctx, res), NULL))
1465
0
          page->overprint = 1;
1466
577
      }
1467
1.15k
      fz_always(ctx)
1468
577
        pdf_annot_pop_local_xref(ctx, annot);
1469
577
      fz_catch(ctx)
1470
0
        fz_rethrow(ctx);
1471
577
    }
1472
354
  }
1473
708
  fz_catch(ctx)
1474
0
  {
1475
0
    if (fz_caught(ctx) != FZ_ERROR_TRYLATER)
1476
0
    {
1477
0
      fz_drop_page(ctx, &page->super);
1478
0
      fz_rethrow(ctx);
1479
0
    }
1480
0
    fz_ignore_error(ctx);
1481
0
    page->super.incomplete = 1;
1482
0
  }
1483
1484
354
  return (fz_page*)page;
1485
354
}
1486
1487
void
1488
pdf_delete_page(fz_context *ctx, pdf_document *doc, int at)
1489
0
{
1490
0
  pdf_obj *parent, *kids;
1491
0
  int i;
1492
1493
0
  pdf_begin_operation(ctx, doc, "Delete page");
1494
0
  fz_try(ctx)
1495
0
  {
1496
0
    pdf_lookup_page_loc(ctx, doc, at, &parent, &i);
1497
0
    kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids));
1498
0
    pdf_array_delete(ctx, kids, i);
1499
1500
0
    while (parent)
1501
0
    {
1502
0
      int count = pdf_dict_get_int(ctx, parent, PDF_NAME(Count));
1503
0
      pdf_dict_put_int(ctx, parent, PDF_NAME(Count), count - 1);
1504
0
      parent = pdf_dict_get(ctx, parent, PDF_NAME(Parent));
1505
0
    }
1506
1507
    /* Adjust page labels */
1508
0
    pdf_adjust_page_labels(ctx, doc, at, -1);
1509
0
    pdf_end_operation(ctx, doc);
1510
0
  }
1511
0
  fz_catch(ctx)
1512
0
  {
1513
0
    pdf_abandon_operation(ctx, doc);
1514
0
    pdf_sync_open_pages(ctx, doc);
1515
0
    fz_rethrow(ctx);
1516
0
  }
1517
1518
0
  pdf_sync_open_pages(ctx, doc);
1519
0
}
1520
1521
void
1522
pdf_delete_page_range(fz_context *ctx, pdf_document *doc, int start, int end)
1523
0
{
1524
0
  int count = pdf_count_pages(ctx, doc);
1525
0
  if (end < 0)
1526
0
    end = count;
1527
0
  start = fz_clampi(start, 0, count);
1528
0
  end = fz_clampi(end, 0, count);
1529
0
  while (start < end)
1530
0
  {
1531
0
    pdf_delete_page(ctx, doc, start);
1532
0
    end--;
1533
0
  }
1534
0
}
1535
1536
pdf_obj *
1537
pdf_add_page(fz_context *ctx, pdf_document *doc, fz_rect mediabox, int rotate, pdf_obj *resources, fz_buffer *contents)
1538
0
{
1539
0
  pdf_obj *page_obj = NULL;
1540
0
  pdf_obj *page_ref = NULL;
1541
1542
0
  fz_var(page_obj);
1543
0
  fz_var(page_ref);
1544
1545
0
  pdf_begin_operation(ctx, doc, "Add page");
1546
1547
0
  fz_try(ctx)
1548
0
  {
1549
0
    page_obj = pdf_new_dict(ctx, doc, 5);
1550
1551
0
    pdf_dict_put(ctx, page_obj, PDF_NAME(Type), PDF_NAME(Page));
1552
0
    pdf_dict_put_rect(ctx, page_obj, PDF_NAME(MediaBox), mediabox);
1553
0
    pdf_dict_put_int(ctx, page_obj, PDF_NAME(Rotate), rotate);
1554
1555
0
    if (pdf_is_indirect(ctx, resources))
1556
0
      pdf_dict_put(ctx, page_obj, PDF_NAME(Resources), resources);
1557
0
    else if (pdf_is_dict(ctx, resources))
1558
0
      pdf_dict_put_drop(ctx, page_obj, PDF_NAME(Resources), pdf_add_object(ctx, doc, resources));
1559
0
    else
1560
0
      pdf_dict_put_dict(ctx, page_obj, PDF_NAME(Resources), 1);
1561
1562
0
    if (contents && contents->len > 0)
1563
0
      pdf_dict_put_drop(ctx, page_obj, PDF_NAME(Contents), pdf_add_stream(ctx, doc, contents, NULL, 0));
1564
0
    page_ref = pdf_add_object_drop(ctx, doc, page_obj);
1565
0
    pdf_end_operation(ctx, doc);
1566
0
  }
1567
0
  fz_catch(ctx)
1568
0
  {
1569
0
    pdf_drop_obj(ctx, page_obj);
1570
0
    pdf_abandon_operation(ctx, doc);
1571
0
    fz_rethrow(ctx);
1572
0
  }
1573
0
  return page_ref;
1574
0
}
1575
1576
void
1577
pdf_insert_page(fz_context *ctx, pdf_document *doc, int at, pdf_obj *page_ref)
1578
0
{
1579
0
  int count = pdf_count_pages(ctx, doc);
1580
0
  pdf_obj *parent, *kids;
1581
0
  int i;
1582
1583
0
  if (at < 0)
1584
0
    at = count;
1585
0
  if (at == INT_MAX)
1586
0
    at = count;
1587
0
  if (at > count)
1588
0
    fz_throw(ctx, FZ_ERROR_ARGUMENT, "cannot insert page beyond end of page tree");
1589
1590
0
  pdf_begin_operation(ctx, doc, "Insert page");
1591
1592
0
  fz_try(ctx)
1593
0
  {
1594
0
    if (count == 0)
1595
0
    {
1596
0
      pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root));
1597
0
      parent = pdf_dict_get(ctx, root, PDF_NAME(Pages));
1598
0
      if (!parent)
1599
0
        fz_throw(ctx, FZ_ERROR_FORMAT, "cannot find page tree");
1600
0
      kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids));
1601
0
      if (!kids)
1602
0
        fz_throw(ctx, FZ_ERROR_FORMAT, "malformed page tree");
1603
0
      pdf_array_insert(ctx, kids, page_ref, 0);
1604
0
    }
1605
0
    else if (at == count)
1606
0
    {
1607
      /* append after last page */
1608
0
      pdf_lookup_page_loc(ctx, doc, count - 1, &parent, &i);
1609
0
      kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids));
1610
0
      pdf_array_insert(ctx, kids, page_ref, i + 1);
1611
0
    }
1612
0
    else
1613
0
    {
1614
      /* insert before found page */
1615
0
      pdf_lookup_page_loc(ctx, doc, at, &parent, &i);
1616
0
      kids = pdf_dict_get(ctx, parent, PDF_NAME(Kids));
1617
0
      pdf_array_insert(ctx, kids, page_ref, i);
1618
0
    }
1619
1620
0
    pdf_dict_put(ctx, page_ref, PDF_NAME(Parent), parent);
1621
1622
    /* Adjust page counts */
1623
0
    while (parent)
1624
0
    {
1625
0
      count = pdf_dict_get_int(ctx, parent, PDF_NAME(Count));
1626
0
      pdf_dict_put_int(ctx, parent, PDF_NAME(Count), count + 1);
1627
0
      parent = pdf_dict_get(ctx, parent, PDF_NAME(Parent));
1628
0
    }
1629
1630
    /* Adjust page labels */
1631
0
    pdf_adjust_page_labels(ctx, doc, at, 1);
1632
0
    pdf_end_operation(ctx, doc);
1633
0
  }
1634
0
  fz_catch(ctx)
1635
0
  {
1636
0
    pdf_abandon_operation(ctx, doc);
1637
0
    pdf_sync_open_pages(ctx, doc);
1638
0
    fz_rethrow(ctx);
1639
0
  }
1640
0
  pdf_sync_open_pages(ctx, doc);
1641
0
}
1642
1643
/*
1644
 * Page Labels
1645
 */
1646
1647
struct page_label_range {
1648
  int offset;
1649
  pdf_obj *label;
1650
  int nums_ix;
1651
  pdf_obj *nums;
1652
};
1653
1654
static void
1655
pdf_lookup_page_label_imp(fz_context *ctx, pdf_obj *node, int index, struct page_label_range *range)
1656
0
{
1657
0
  pdf_obj *kids = pdf_dict_get(ctx, node, PDF_NAME(Kids));
1658
0
  pdf_obj *nums = pdf_dict_get(ctx, node, PDF_NAME(Nums));
1659
0
  int i;
1660
1661
0
  if (pdf_is_array(ctx, kids))
1662
0
  {
1663
0
    for (i = 0; i < pdf_array_len(ctx, kids); ++i)
1664
0
    {
1665
0
      pdf_obj *kid = pdf_array_get(ctx, kids, i);
1666
0
      pdf_lookup_page_label_imp(ctx, kid, index, range);
1667
0
    }
1668
0
  }
1669
1670
0
  if (pdf_is_array(ctx, nums))
1671
0
  {
1672
0
    for (i = 0; i < pdf_array_len(ctx, nums); i += 2)
1673
0
    {
1674
0
      int k = pdf_array_get_int(ctx, nums, i);
1675
0
      if (k <= index)
1676
0
      {
1677
0
        range->offset = k;
1678
0
        range->label = pdf_array_get(ctx, nums, i + 1);
1679
0
        range->nums_ix = i;
1680
0
        range->nums = nums;
1681
0
      }
1682
0
      else
1683
0
      {
1684
        /* stop looking if we've already passed the index */
1685
0
        return;
1686
0
      }
1687
0
    }
1688
0
  }
1689
0
}
1690
1691
static struct page_label_range
1692
pdf_lookup_page_label(fz_context *ctx, pdf_document *doc, int index)
1693
0
{
1694
0
  pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root));
1695
0
  pdf_obj *labels = pdf_dict_get(ctx, root, PDF_NAME(PageLabels));
1696
0
  pdf_obj *nums = pdf_dict_get(ctx, labels, PDF_NAME(Nums));
1697
0
  struct page_label_range range = { 0, NULL, 0, nums };
1698
0
  pdf_lookup_page_label_imp(ctx, labels, index, &range);
1699
0
  return range;
1700
0
}
1701
1702
static void
1703
pdf_flatten_page_label_tree_imp(fz_context *ctx, pdf_obj *node, pdf_obj *new_nums)
1704
0
{
1705
0
  pdf_obj *kids = pdf_dict_get(ctx, node, PDF_NAME(Kids));
1706
0
  pdf_obj *nums = pdf_dict_get(ctx, node, PDF_NAME(Nums));
1707
0
  int i;
1708
1709
0
  if (pdf_is_array(ctx, kids))
1710
0
  {
1711
0
    for (i = 0; i < pdf_array_len(ctx, kids); ++i)
1712
0
    {
1713
0
      pdf_obj *kid = pdf_array_get(ctx, kids, i);
1714
0
      pdf_flatten_page_label_tree_imp(ctx, kid, new_nums);
1715
0
    }
1716
0
  }
1717
1718
0
  if (pdf_is_array(ctx, nums))
1719
0
  {
1720
0
    for (i = 0; i < pdf_array_len(ctx, nums); i += 2)
1721
0
    {
1722
0
      pdf_array_push(ctx, new_nums, pdf_array_get(ctx, nums, i));
1723
0
      pdf_array_push(ctx, new_nums, pdf_array_get(ctx, nums, i + 1));
1724
0
    }
1725
0
  }
1726
0
}
1727
1728
static void
1729
pdf_flatten_page_label_tree(fz_context *ctx, pdf_document *doc)
1730
0
{
1731
0
  pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root));
1732
0
  pdf_obj *labels = pdf_dict_get(ctx, root, PDF_NAME(PageLabels));
1733
0
  pdf_obj *nums = pdf_dict_get(ctx, labels, PDF_NAME(Nums));
1734
1735
  // Already flat...
1736
0
  if (pdf_is_array(ctx, nums) && pdf_array_len(ctx, nums) >= 2)
1737
0
    return;
1738
1739
0
  nums = pdf_new_array(ctx, doc, 8);
1740
0
  fz_try(ctx)
1741
0
  {
1742
0
    if (!labels)
1743
0
      labels = pdf_dict_put_dict(ctx, root, PDF_NAME(PageLabels), 1);
1744
1745
0
    pdf_flatten_page_label_tree_imp(ctx, labels, nums);
1746
1747
0
    pdf_dict_del(ctx, labels, PDF_NAME(Kids));
1748
0
    pdf_dict_del(ctx, labels, PDF_NAME(Limits));
1749
0
    pdf_dict_put(ctx, labels, PDF_NAME(Nums), nums);
1750
1751
    /* No Page Label tree found - insert one with default values */
1752
0
    if (pdf_array_len(ctx, nums) == 0)
1753
0
    {
1754
0
      pdf_obj *obj;
1755
0
      pdf_array_push_int(ctx, nums, 0);
1756
0
      obj = pdf_array_push_dict(ctx, nums, 1);
1757
0
      pdf_dict_put(ctx, obj, PDF_NAME(S), PDF_NAME(D));
1758
0
    }
1759
0
  }
1760
0
  fz_always(ctx)
1761
0
    pdf_drop_obj(ctx, nums);
1762
0
  fz_catch(ctx)
1763
0
    fz_rethrow(ctx);
1764
0
}
1765
1766
static pdf_obj *
1767
pdf_create_page_label(fz_context *ctx, pdf_document *doc, pdf_page_label_style style, const char *prefix, int start)
1768
0
{
1769
0
  pdf_obj *obj = pdf_new_dict(ctx, doc, 3);
1770
0
  fz_try(ctx)
1771
0
  {
1772
0
    switch (style)
1773
0
    {
1774
0
    default:
1775
0
    case PDF_PAGE_LABEL_NONE:
1776
0
      break;
1777
0
    case PDF_PAGE_LABEL_DECIMAL:
1778
0
      pdf_dict_put(ctx, obj, PDF_NAME(S), PDF_NAME(D));
1779
0
      break;
1780
0
    case PDF_PAGE_LABEL_ROMAN_UC:
1781
0
      pdf_dict_put(ctx, obj, PDF_NAME(S), PDF_NAME(R));
1782
0
      break;
1783
0
    case PDF_PAGE_LABEL_ROMAN_LC:
1784
0
      pdf_dict_put(ctx, obj, PDF_NAME(S), PDF_NAME(r));
1785
0
      break;
1786
0
    case PDF_PAGE_LABEL_ALPHA_UC:
1787
0
      pdf_dict_put(ctx, obj, PDF_NAME(S), PDF_NAME(A));
1788
0
      break;
1789
0
    case PDF_PAGE_LABEL_ALPHA_LC:
1790
0
      pdf_dict_put(ctx, obj, PDF_NAME(S), PDF_NAME(a));
1791
0
      break;
1792
0
    }
1793
0
    if (prefix && strlen(prefix) > 0)
1794
0
      pdf_dict_put_text_string(ctx, obj, PDF_NAME(P), prefix);
1795
0
    if (start > 1)
1796
0
      pdf_dict_put_int(ctx, obj, PDF_NAME(St), start);
1797
0
  }
1798
0
  fz_catch(ctx)
1799
0
  {
1800
0
    pdf_drop_obj(ctx, obj);
1801
0
    fz_rethrow(ctx);
1802
0
  }
1803
0
  return obj;
1804
0
}
1805
1806
static void
1807
pdf_adjust_page_labels(fz_context *ctx, pdf_document *doc, int index, int adjust)
1808
0
{
1809
0
  pdf_obj *root = pdf_dict_get(ctx, pdf_trailer(ctx, doc), PDF_NAME(Root));
1810
0
  pdf_obj *labels = pdf_dict_get(ctx, root, PDF_NAME(PageLabels));
1811
1812
  // Skip the adjustment step if there are no page labels.
1813
  // Exception: If we would adjust the label for page 0, we must create one!
1814
  // Exception: If the document only has one page!
1815
0
  if (labels || (adjust > 0 && index == 0 && pdf_count_pages(ctx, doc) > 1))
1816
0
  {
1817
0
    struct page_label_range range;
1818
0
    int i;
1819
1820
    // Ensure we have a flat page label tree with at least one entry.
1821
0
    pdf_flatten_page_label_tree(ctx, doc);
1822
1823
    // Find page label affecting the page that triggered adjustment
1824
0
    range = pdf_lookup_page_label(ctx, doc, index);
1825
1826
    // Shift all page labels on and after the inserted index
1827
0
    if (adjust > 0)
1828
0
    {
1829
0
      if (range.offset == index)
1830
0
        i = range.nums_ix;
1831
0
      else
1832
0
        i = range.nums_ix + 2;
1833
0
    }
1834
1835
    // Shift all page labels after the removed index
1836
0
    else
1837
0
    {
1838
0
      i = range.nums_ix + 2;
1839
0
    }
1840
1841
1842
    // Increase/decrease the indices in the name tree
1843
0
    for (; i < pdf_array_len(ctx, range.nums); i += 2)
1844
0
      pdf_array_put_int(ctx, range.nums, i, pdf_array_get_int(ctx, range.nums, i) + adjust);
1845
1846
    // TODO: delete page labels that have no effect (zero range)
1847
1848
    // Make sure the number tree always has an entry for page 0
1849
0
    if (adjust > 0 && index == 0)
1850
0
    {
1851
0
      pdf_array_insert_drop(ctx, range.nums, pdf_new_int(ctx, index), 0);
1852
0
      pdf_array_insert_drop(ctx, range.nums, pdf_create_page_label(ctx, doc, PDF_PAGE_LABEL_DECIMAL, NULL, 1), 1);
1853
0
    }
1854
0
  }
1855
0
}
1856
1857
void
1858
pdf_set_page_labels(fz_context *ctx, pdf_document *doc,
1859
  int index,
1860
  pdf_page_label_style style, const char *prefix, int start)
1861
0
{
1862
0
  struct page_label_range range;
1863
1864
0
  pdf_begin_operation(ctx, doc, "Set page label");
1865
0
  fz_try(ctx)
1866
0
  {
1867
    // Ensure we have a flat page label tree with at least one entry.
1868
0
    pdf_flatten_page_label_tree(ctx, doc);
1869
1870
0
    range = pdf_lookup_page_label(ctx, doc, index);
1871
1872
0
    if (range.offset == index)
1873
0
    {
1874
      // Replace label
1875
0
      pdf_array_put_drop(ctx, range.nums,
1876
0
        range.nums_ix + 1,
1877
0
        pdf_create_page_label(ctx, doc, style, prefix, start));
1878
0
    }
1879
0
    else
1880
0
    {
1881
      // Insert new label
1882
0
      pdf_array_insert_drop(ctx, range.nums,
1883
0
        pdf_new_int(ctx, index),
1884
0
        range.nums_ix + 2);
1885
0
      pdf_array_insert_drop(ctx, range.nums,
1886
0
        pdf_create_page_label(ctx, doc, style, prefix, start),
1887
0
        range.nums_ix + 3);
1888
0
    }
1889
0
    pdf_end_operation(ctx, doc);
1890
0
  }
1891
0
  fz_catch(ctx)
1892
0
  {
1893
0
    pdf_abandon_operation(ctx, doc);
1894
0
    fz_rethrow(ctx);
1895
0
  }
1896
0
}
1897
1898
void
1899
pdf_delete_page_labels(fz_context *ctx, pdf_document *doc, int index)
1900
0
{
1901
0
  struct page_label_range range;
1902
1903
0
  if (index == 0)
1904
0
  {
1905
0
    pdf_set_page_labels(ctx, doc, 0, PDF_PAGE_LABEL_DECIMAL, NULL, 1);
1906
0
    return;
1907
0
  }
1908
1909
0
  pdf_begin_operation(ctx, doc, "Delete page label");
1910
0
  fz_try(ctx)
1911
0
  {
1912
    // Ensure we have a flat page label tree with at least one entry.
1913
0
    pdf_flatten_page_label_tree(ctx, doc);
1914
1915
0
    range = pdf_lookup_page_label(ctx, doc, index);
1916
1917
0
    if (range.offset == index)
1918
0
    {
1919
      // Delete label
1920
0
      pdf_array_delete(ctx, range.nums, range.nums_ix);
1921
0
      pdf_array_delete(ctx, range.nums, range.nums_ix);
1922
0
    }
1923
0
    pdf_end_operation(ctx, doc);
1924
0
  }
1925
0
  fz_catch(ctx)
1926
0
  {
1927
0
    pdf_abandon_operation(ctx, doc);
1928
0
    fz_rethrow(ctx);
1929
0
  }
1930
0
}
1931
1932
static const char *roman_uc[3][10] = {
1933
  { "", "I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX" },
1934
  { "", "X", "XX", "XXX", "XL", "L", "LX", "LXX", "LXXX", "XC" },
1935
  { "", "C", "CC", "CCC", "CD", "D", "DC", "DCC", "DCCC", "CM" },
1936
};
1937
1938
static const char *roman_lc[3][10] = {
1939
  { "", "i", "ii", "iii", "iv", "v", "vi", "vii", "viii", "ix" },
1940
  { "", "x", "xx", "xxx", "xl", "l", "lx", "lxx", "lxxx", "xc" },
1941
  { "", "c", "cc", "ccc", "cd", "d", "dc", "dcc", "dccc", "cm" },
1942
};
1943
1944
static void pdf_format_roman_page_label(char *buf, int size, int n, const char *sym[3][10], const char *sym_m)
1945
0
{
1946
0
  int I = n % 10;
1947
0
  int X = (n / 10) % 10;
1948
0
  int C = (n / 100) % 10;
1949
0
  int M = (n / 1000);
1950
1951
0
  fz_strlcpy(buf, "", size);
1952
0
  while (M--)
1953
0
    fz_strlcat(buf, sym_m, size);
1954
0
  fz_strlcat(buf, sym[2][C], size);
1955
0
  fz_strlcat(buf, sym[1][X], size);
1956
0
  fz_strlcat(buf, sym[0][I], size);
1957
0
}
1958
1959
static void pdf_format_alpha_page_label(char *buf, int size, int n, int alpha)
1960
0
{
1961
0
  int reps = (n - 1) / 26 + 1;
1962
0
  if (reps > size - 1)
1963
0
    reps = size - 1;
1964
0
  memset(buf, (n - 1) % 26 + alpha, reps);
1965
0
  buf[reps] = '\0';
1966
0
}
1967
1968
static void
1969
pdf_format_page_label(fz_context *ctx, int index, pdf_obj *dict, char *buf, size_t size)
1970
0
{
1971
0
  pdf_obj *style = pdf_dict_get(ctx, dict, PDF_NAME(S));
1972
0
  const char *prefix = pdf_dict_get_text_string(ctx, dict, PDF_NAME(P));
1973
0
  int start = pdf_dict_get_int(ctx, dict, PDF_NAME(St));
1974
0
  size_t n;
1975
1976
  // St must be >= 1; default is 1.
1977
0
  if (start < 1)
1978
0
    start = 1;
1979
1980
  // Add prefix (optional; may be empty)
1981
0
  fz_strlcpy(buf, prefix, size);
1982
0
  n = strlen(buf);
1983
0
  buf += n;
1984
0
  size -= n;
1985
1986
  // Append number using style (optional)
1987
0
  if (style == PDF_NAME(D))
1988
0
    fz_snprintf(buf, size, "%d", index + start);
1989
0
  else if (style == PDF_NAME(R))
1990
0
    pdf_format_roman_page_label(buf, (int)size, index + start, roman_uc, "M");
1991
0
  else if (style == PDF_NAME(r))
1992
0
    pdf_format_roman_page_label(buf, (int)size, index + start, roman_lc, "m");
1993
0
  else if (style == PDF_NAME(A))
1994
0
    pdf_format_alpha_page_label(buf, (int)size, index + start, 'A');
1995
0
  else if (style == PDF_NAME(a))
1996
0
    pdf_format_alpha_page_label(buf, (int)size, index + start, 'a');
1997
0
}
1998
1999
void
2000
pdf_page_label(fz_context *ctx, pdf_document *doc, int index, char *buf, size_t size)
2001
0
{
2002
0
  struct page_label_range range = pdf_lookup_page_label(ctx, doc, index);
2003
0
  if (range.label)
2004
0
    pdf_format_page_label(ctx, index - range.offset, range.label, buf, size);
2005
0
  else
2006
0
    fz_snprintf(buf, size, "%d", index + 1);
2007
0
}
2008
2009
void
2010
pdf_page_label_imp(fz_context *ctx, fz_document *doc, int chapter, int page, char *buf, size_t size)
2011
0
{
2012
0
  pdf_page_label(ctx, pdf_document_from_fz_document(ctx, doc), page, buf, size);
2013
0
}
2014
2015
pdf_page *
2016
pdf_keep_page(fz_context *ctx, pdf_page *page)
2017
0
{
2018
0
  return (pdf_page *) fz_keep_page(ctx, &page->super);
2019
0
}
2020
2021
void
2022
pdf_drop_page(fz_context *ctx, pdf_page *page)
2023
0
{
2024
0
  fz_drop_page(ctx, &page->super);
2025
0
}