Coverage Report

Created: 2026-11-12 07:07

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