Coverage Report

Created: 2024-05-20 06:23

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