Coverage Report

Created: 2026-07-24 07:44

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/pdf/pdf_doc.c
Line
Count
Source
1
/* Copyright (C) 2020-2026 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
/* Functions to deal with PDF structure, such as retrieving
17
 * the Info, Catalog, Root dictionaries, and finding resources
18
 * and page dictionaries.
19
 */
20
21
#include "ghostpdf.h"
22
#include "pdf_stack.h"
23
#include "pdf_deref.h"
24
#include "pdf_array.h"
25
#include "pdf_dict.h"
26
#include "pdf_loop_detect.h"
27
#include "pdf_misc.h"
28
#include "pdf_repair.h"
29
#include "pdf_doc.h"
30
#include "pdf_mark.h"
31
#include "pdf_colour.h"
32
#include "pdf_device.h"
33
34
int pdfi_read_Root(pdf_context *ctx)
35
35.6k
{
36
35.6k
    pdf_obj *o, *o1;
37
35.6k
    pdf_dict *d;
38
35.6k
    int code;
39
40
35.6k
    if (ctx->args.pdfdebug)
41
0
        outprintf(ctx->memory, "%% Reading Root dictionary\n");
42
43
    /* Unusual code. This is because if the entry in the trailer dictionary causes
44
     * us to repair the file, the Trailer dictionary in the context can be replaced.
45
     * This counts it down and frees it, potentially while pdfi_dict_get is still
46
     * using it! Rather than countup and down in the dict_get routine, which is
47
     * normally unnecessary, count it up and down round the access here.
48
     */
49
35.6k
    d = ctx->Trailer;
50
35.6k
    pdfi_countup(d);
51
35.6k
    code = pdfi_dict_get(ctx, d, "Root", &o1);
52
35.6k
    if (code < 0) {
53
949
        pdfi_countdown(d);
54
949
        return code;
55
949
    }
56
34.6k
    pdfi_countdown(d);
57
58
34.6k
    if (pdfi_type_of(o1) == PDF_INDIRECT) {
59
0
        code = pdfi_dereference(ctx, ((pdf_indirect_ref *)o1)->ref_object_num,  ((pdf_indirect_ref *)o1)->ref_generation_num, &o);
60
0
        pdfi_countdown(o1);
61
0
        if (code < 0)
62
0
            return code;
63
64
0
        if (pdfi_type_of(o) != PDF_DICT) {
65
0
            pdfi_countdown(o);
66
0
            return_error(gs_error_typecheck);
67
0
        }
68
69
0
        code = pdfi_dict_put(ctx, ctx->Trailer, "Root", o);
70
0
        if (code < 0) {
71
0
            pdfi_countdown(o);
72
0
            return code;
73
0
        }
74
0
        o1 = o;
75
34.6k
    } else {
76
34.6k
        if (pdfi_type_of(o1) != PDF_DICT) {
77
200
            pdfi_countdown(o1);
78
200
            if (ctx->Root == NULL)
79
67
                return_error(gs_error_typecheck);
80
133
            return 0;
81
200
        }
82
34.6k
    }
83
84
34.4k
    code = pdfi_dict_get_type(ctx, (pdf_dict *)o1, "Type", PDF_NAME, &o);
85
34.4k
    if (code < 0) {
86
243
        bool known = false;
87
88
243
        if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_syntaxerror), NULL, E_PDF_MISSINGTYPE, "pdfi_read_Root", NULL)) < 0) {
89
0
            pdfi_countdown(o1);
90
0
            return code;
91
0
        }
92
93
        /* Missing the *required* /Type key! See if it has /Pages at least, if it does carry on */
94
243
        code = pdfi_dict_known(ctx, (pdf_dict *)o1, "Pages", &known);
95
243
        if (code < 0 || known == false) {
96
77
            pdfi_countdown(o1);
97
77
            return code;
98
77
        }
99
243
    }
100
34.2k
    else {
101
34.2k
        if (pdfi_name_strcmp((pdf_name *)o, "Catalog") != 0){
102
589
            pdf_obj *pages = NULL;
103
104
            /* Bug #706038 has a Root dictionary with /Type /Calalog which (of course) Acrobat
105
             * happily opens :-( So if we find a /Type and it's not /Catalog, try and see if
106
             * we have a /Pages key (the only other required entry). If we do assume that the
107
             * Type is wrong and use this dictionary, otherwise fall back to seeing if we
108
             * have a repaired Root. See above for handling a missing /Type.....
109
             */
110
589
            code = pdfi_dict_get_type(ctx, (pdf_dict *)o1, "Pages", PDF_DICT, &pages);
111
589
            if (code < 0) {
112
306
                pdfi_countdown(o);
113
306
                pdfi_countdown(o1);
114
                /* If we repaired the file, we may already have spotted a potential Root dictionary
115
                 * so if the one we found here isn't valid, try the one we found when scanning
116
                 */
117
306
                if (ctx->Root == NULL) {
118
67
                    pdfi_set_error(ctx, 0, NULL, E_PDF_NO_ROOT, "pdfi_read_Root", NULL);
119
67
                    return_error(gs_error_syntaxerror);
120
67
                }
121
239
                return 0;
122
306
            }
123
283
            pdfi_countdown(pages);
124
283
            if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_typecheck), NULL, E_PDF_BAD_ROOT_TYPE, "pdfi_read_Root", NULL)) < 0) {
125
0
                pdfi_countdown(o);
126
0
                pdfi_countdown(o1);
127
0
                return code;
128
0
            }
129
283
        }
130
33.9k
        pdfi_countdown(o);
131
33.9k
    }
132
133
34.1k
    if (ctx->args.pdfdebug)
134
0
        outprintf(ctx->memory, "\n");
135
    /* We don't pdfi_countdown(o1) now, because we've transferred our
136
     * reference to the pointer in the pdf_context structure.
137
     */
138
34.1k
    pdfi_countdown(ctx->Root); /* If file was repaired it might be set already */
139
34.1k
    ctx->Root = (pdf_dict *)o1;
140
34.1k
    return 0;
141
34.4k
}
142
143
static int Info_check_dict(pdf_context *ctx, pdf_dict *d);
144
145
static int Info_check_array(pdf_context *ctx, pdf_array *a)
146
3.88k
{
147
3.88k
    int code = 0, i = 0;
148
3.88k
    pdf_obj *array_obj = NULL;
149
150
3.88k
    code = pdfi_loop_detector_mark(ctx);
151
3.88k
    if (code < 0)
152
0
        return code;
153
154
35.2k
    for (i = 0;i < pdfi_array_size(a); i++) {
155
31.8k
        code = pdfi_array_fetch_recursing(ctx, a, i, &array_obj, true, true);
156
31.8k
        if (code < 0)
157
281
            goto error;
158
159
31.6k
        switch(pdfi_type_of(array_obj)) {
160
905
            case PDF_DICT:
161
905
                if (array_obj->object_num != 0) {
162
875
                    code = pdfi_loop_detector_add_object(ctx, array_obj->object_num);
163
875
                    if (code < 0)
164
0
                        goto error;
165
875
                }
166
905
                code = Info_check_dict(ctx, (pdf_dict *)array_obj);
167
905
                if (code < 0) {
168
215
                    if (array_obj->object_num != 0) {
169
215
                        pdf_indirect_ref *i_o;
170
171
215
                        code = pdfi_object_alloc(ctx, PDF_INDIRECT, 0, (pdf_obj **)&i_o);
172
215
                        if (code < 0)
173
0
                            goto error;
174
215
                        i_o->ref_generation_num = array_obj->generation_num;
175
215
                        i_o->ref_object_num = array_obj->object_num;
176
215
                        i_o->indirect_num = array_obj->object_num;
177
215
                        i_o->indirect_gen = array_obj->generation_num;
178
215
                        code = pdfi_array_put(ctx, a, i, (pdf_obj *)i_o);
179
215
                        if (code < 0)
180
0
                            return code;
181
215
                    }
182
215
                    goto error;
183
215
                }
184
690
                break;
185
690
            case PDF_ARRAY:
186
237
                if (array_obj->object_num != 0) {
187
46
                    code = pdfi_loop_detector_add_object(ctx, array_obj->object_num);
188
46
                    if (code < 0)
189
0
                        goto error;
190
46
                }
191
237
                code = Info_check_array(ctx, (pdf_array *)array_obj);
192
237
                if (code < 0) {
193
14
                    if (array_obj->object_num != 0) {
194
14
                        pdf_indirect_ref *i_o;
195
196
14
                        code = pdfi_object_alloc(ctx, PDF_INDIRECT, 0, (pdf_obj **)&i_o);
197
14
                        if (code < 0)
198
0
                            goto error;
199
14
                        i_o->ref_generation_num = array_obj->generation_num;
200
14
                        i_o->ref_object_num = array_obj->object_num;
201
14
                        i_o->indirect_num = array_obj->object_num;
202
14
                        i_o->indirect_gen = array_obj->generation_num;
203
14
                        code = pdfi_array_put(ctx, a, i, (pdf_obj *)i_o);
204
14
                        if (code < 0)
205
0
                            return code;
206
14
                    }
207
14
                    goto error;
208
14
                }
209
223
                break;
210
30.4k
            default:
211
30.4k
                break;
212
31.6k
        }
213
214
31.3k
        pdfi_countdown(array_obj);
215
31.3k
        array_obj = NULL;
216
31.3k
    }
217
3.88k
error:
218
3.88k
    pdfi_countdown(array_obj);
219
3.88k
    pdfi_loop_detector_cleartomark(ctx);
220
3.88k
    return code;
221
3.88k
}
222
223
static int Info_check_dict(pdf_context *ctx, pdf_dict *d)
224
3.23k
{
225
3.23k
    int code = 0;
226
3.23k
    uint64_t index = 0;
227
3.23k
    pdf_name *Key = NULL;
228
3.23k
    pdf_obj *Value = NULL;
229
230
3.23k
    code = pdfi_loop_detector_mark(ctx);
231
3.23k
    if (code < 0)
232
0
        return code;
233
234
3.23k
    code = pdfi_dict_first(ctx, d, (pdf_obj **)&Key, &Value, &index);
235
3.23k
    if (code < 0) {
236
347
        if (code == gs_error_undefined)
237
272
            code = 0;
238
347
        goto error;
239
347
    }
240
241
11.6k
    while (code >= 0) {
242
11.4k
        switch(pdfi_type_of(Value)) {
243
1.65k
            case PDF_DICT:
244
1.65k
                if (Value->object_num != 0) {
245
796
                    code = pdfi_loop_detector_add_object(ctx, Value->object_num);
246
796
                    if (code < 0)
247
0
                        goto error;
248
796
                }
249
1.65k
                code = Info_check_dict(ctx, (pdf_dict *)Value);
250
1.65k
                if (code < 0)
251
342
                    goto error;
252
1.31k
                break;
253
2.27k
            case PDF_ARRAY:
254
2.27k
                if (Value->object_num != 0) {
255
117
                    code = pdfi_loop_detector_add_object(ctx, Value->object_num);
256
117
                    if (code < 0)
257
0
                        goto error;
258
117
                }
259
2.27k
                code = Info_check_array(ctx, (pdf_array *)Value);
260
2.27k
                if (code < 0)
261
236
                    goto error;
262
2.03k
                break;
263
7.55k
            default:
264
7.55k
                break;
265
11.4k
        }
266
10.9k
        pdfi_countdown(Key);
267
10.9k
        Key = NULL;
268
10.9k
        pdfi_countdown(Value);
269
10.9k
        Value = NULL;
270
271
10.9k
        code = pdfi_dict_next(ctx, d, (pdf_obj **)&Key, &Value, &index);
272
10.9k
        if (code == gs_error_undefined) {
273
2.10k
            code = 0;
274
2.10k
            break;
275
2.10k
        }
276
10.9k
    }
277
3.23k
error:
278
3.23k
    pdfi_countdown(Key);
279
3.23k
    pdfi_countdown(Value);
280
3.23k
    pdfi_loop_detector_cleartomark(ctx);
281
3.23k
    return code;
282
2.89k
}
283
284
static int pdfi_sanitize_Info_references(pdf_context *ctx, pdf_dict *Info)
285
20.8k
{
286
20.8k
    int code = 0;
287
20.8k
    uint64_t index = 0;
288
20.8k
    pdf_name *Key = NULL;
289
20.8k
    pdf_obj *Value = NULL;
290
291
21.2k
restart_scan:
292
21.2k
    code = pdfi_loop_detector_mark(ctx);
293
21.2k
    if (code < 0)
294
0
        return code;
295
296
21.2k
    code = pdfi_dict_first(ctx, Info, (pdf_obj **)&Key, &Value, &index);
297
21.2k
    if (code == gs_error_undefined) {
298
475
        code = 0;
299
475
        goto error;
300
475
    }
301
302
101k
    while (code >= 0) {
303
101k
        switch(pdfi_type_of(Value)) {
304
673
            case PDF_DICT:
305
673
                code = Info_check_dict(ctx, (pdf_dict *)Value);
306
673
                break;
307
1.37k
            case PDF_ARRAY:
308
1.37k
                code = Info_check_array(ctx, (pdf_array *)Value);
309
1.37k
                break;
310
99.3k
            default:
311
99.3k
                code = 0;
312
99.3k
                break;
313
101k
        }
314
101k
        pdfi_countdown(Value);
315
101k
        Value = NULL;
316
101k
        if (code < 0) {
317
331
            code = pdfi_dict_delete_pair(ctx, Info, Key);
318
331
            if (code < 0)
319
0
                goto error;
320
331
            pdfi_countdown(Key);
321
331
            Key = NULL;
322
323
331
            pdfi_loop_detector_cleartomark(ctx);
324
331
            goto restart_scan;
325
331
        }
326
101k
        pdfi_countdown(Key);
327
101k
        Key = NULL;
328
329
101k
        pdfi_loop_detector_cleartomark(ctx);
330
101k
        code = pdfi_loop_detector_mark(ctx);
331
101k
        if (code < 0) {
332
0
            pdfi_countdown(Key);
333
0
            pdfi_countdown(Value);
334
0
            return code;
335
0
        }
336
337
101k
        code = pdfi_dict_next(ctx, Info, (pdf_obj **)&Key, &Value, &index);
338
101k
        if (code == gs_error_undefined) {
339
20.3k
            code = 0;
340
20.3k
            break;
341
20.3k
        }
342
101k
    }
343
20.8k
error:
344
20.8k
    pdfi_countdown(Key);
345
20.8k
    pdfi_countdown(Value);
346
20.8k
    pdfi_loop_detector_cleartomark(ctx);
347
20.8k
    return code;
348
20.7k
}
349
350
int pdfi_read_Info(pdf_context *ctx)
351
34.5k
{
352
34.5k
    pdf_dict *Info;
353
34.5k
    int code;
354
34.5k
    pdf_dict *d;
355
356
34.5k
    if (ctx->args.pdfdebug)
357
0
        outprintf(ctx->memory, "%% Reading Info dictionary\n");
358
359
    /* See comment in pdfi_read_Root() for details */
360
34.5k
    d = ctx->Trailer;
361
34.5k
    pdfi_countup(d);
362
34.5k
    code = pdfi_dict_get_type(ctx, ctx->Trailer, "Info", PDF_DICT, (pdf_obj **)&Info);
363
34.5k
    pdfi_countdown(d);
364
34.5k
    if (code < 0)
365
13.6k
        return code;
366
367
20.8k
    if (ctx->args.pdfdebug)
368
0
        outprintf(ctx->memory, "\n");
369
370
20.8k
    code = pdfi_loop_detector_mark(ctx);
371
20.8k
    if (code < 0)
372
0
        goto error;
373
20.8k
    if (Info->object_num != 0) {
374
20.8k
        code = pdfi_loop_detector_add_object(ctx, Info->object_num);
375
20.8k
        if (code < 0)
376
0
            goto error1;
377
20.8k
    } else {
378
0
        if ((code = pdfi_set_warning_stop(ctx, 0, NULL, W_PDF_INFO_NOT_INDIRECT, "pdfi_read_Info", "")) < 0)
379
0
            goto error1;
380
0
    }
381
382
    /* sanitize Info for circular references */
383
20.8k
    code = pdfi_sanitize_Info_references(ctx, Info);
384
20.8k
    if (code < 0)
385
66
        goto error1;
386
387
20.8k
    (void)pdfi_loop_detector_cleartomark(ctx);
388
389
20.8k
    pdfi_pdfmark_write_docinfo(ctx, Info);
390
391
    /* We don't pdfi_countdown(Info) now, because we've transferred our
392
     * reference to the pointer in the pdf_context structure.
393
     */
394
20.8k
    ctx->Info = Info;
395
20.8k
    return 0;
396
397
66
error1:
398
66
    pdfi_loop_detector_cleartomark(ctx);
399
66
error:
400
66
    pdfi_countdown(Info);
401
66
    return code;
402
66
}
403
404
int pdfi_read_Pages(pdf_context *ctx)
405
75.5k
{
406
75.5k
    pdf_obj *o, *o1;
407
75.5k
    pdf_array *a = NULL;
408
75.5k
    int code, pagecount = 0;
409
75.5k
    double d;
410
411
75.5k
    if (ctx->args.pdfdebug)
412
0
        outprintf(ctx->memory, "%% Reading Pages dictionary\n");
413
414
75.5k
    code = pdfi_dict_get(ctx, ctx->Root, "Pages", &o1);
415
75.5k
    if (code < 0)
416
2.46k
        return code;
417
418
73.1k
    if (pdfi_type_of(o1) == PDF_INDIRECT) {
419
0
        code = pdfi_dereference(ctx, ((pdf_indirect_ref *)o1)->ref_object_num,  ((pdf_indirect_ref *)o1)->ref_generation_num, &o);
420
0
        pdfi_countdown(o1);
421
0
        if (code < 0)
422
0
            return code;
423
424
0
        if (pdfi_type_of(o) != PDF_DICT) {
425
0
            pdfi_countdown(o);
426
0
            if (pdfi_type_of(o) == PDF_INDIRECT)
427
0
                pdfi_set_error(ctx, 0, NULL, E_PDF_BADPAGEDICT, "pdfi_read_Pages", (char *)"*** Error: Something is wrong with the Pages dictionary.  Giving up.");
428
0
            else
429
0
                pdfi_set_error(ctx, 0, NULL, E_PDF_BADPAGEDICT, "pdfi_read_Pages", (char *)"*** Error: Something is wrong with the Pages dictionary.  Giving up.\n           Double indirect reference.  Loop in Pages tree?");
430
0
            return_error(gs_error_typecheck);
431
0
        }
432
433
0
        code = pdfi_dict_put(ctx, ctx->Root, "Pages", o);
434
0
        if (code < 0) {
435
0
            pdfi_countdown(o);
436
0
            return code;
437
0
        }
438
0
        o1 = o;
439
73.1k
    } else {
440
73.1k
        if (pdfi_type_of(o1) != PDF_DICT) {
441
135
            pdfi_countdown(o1);
442
135
            return_error(gs_error_typecheck);
443
135
        }
444
73.1k
    }
445
446
72.9k
    if (ctx->args.pdfdebug)
447
0
        outprintf(ctx->memory, "\n");
448
449
    /* Acrobat allows the Pages Count to be a floating point number (!) */
450
    /* sample w_a.PDF from Bug688419 (not on the cluster, maybe it should be?) has no /Count entry because
451
     * The Root dictionary Pages key points directly to a single dictionary of type /Page. This is plainly
452
     * illegal but Acrobat can deal with it. We do so by ignoring the error her, and adding logic in
453
     * pdfi_get_page_dict() which notes that ctx->PagesTree is NULL and tries to get the single Page
454
     * dictionary from the Root instead of using the PagesTree.
455
     */
456
72.9k
    code = pdfi_dict_get_number(ctx, (pdf_dict *)o1, "Count", &d);
457
72.9k
    if (code < 0) {
458
709
        if (code == gs_error_undefined) {
459
679
            pdf_name *n = NULL;
460
            /* It may be that the Root dictionary Pages entry points directly to a sinlge Page dictionary
461
             * See if the dictionary has a Type of /Page, if so don't throw an error and the pdf_page.c
462
             * logic in pdfi_get_page_dict() logic will take care of it.
463
             */
464
679
            code = pdfi_dict_get_type(ctx, (pdf_dict *)o1, "Type", PDF_NAME, (pdf_obj **)&n);
465
679
            if (code == 0) {
466
626
                if(pdfi_name_is(n, "Page")) {
467
509
                    ctx->num_pages = 1;
468
509
                    code = 0;
469
509
                }
470
117
                else
471
117
                    code = gs_error_undefined;
472
626
                pdfi_countdown(n);
473
626
            }
474
679
        }
475
709
        pdfi_countdown(o1);
476
709
        return code;
477
709
    }
478
479
72.2k
    if (floor(d) != d) {
480
0
        pdfi_countdown(o1);
481
0
        return_error(gs_error_rangecheck);
482
72.2k
    } else {
483
72.2k
        ctx->num_pages = (int)floor(d);
484
72.2k
    }
485
486
    /* A simple confidence check in the value of Count. We only do this because
487
     * the OSS-fuzz tool keeps on coming up with files that time out because the
488
     * initial Count is insanely huge, and we spend much time trying to find
489
     * millions of pages which don't exist.
490
     */
491
72.2k
    code = pdfi_dict_knownget_type(ctx, (pdf_dict *)o1, "Kids", PDF_ARRAY, (pdf_obj **)&a);
492
72.2k
    if (code == 0)
493
28
        code = gs_note_error(gs_error_undefined);
494
72.2k
    if (code < 0) {
495
28
        pdfi_countdown(o1);
496
28
        return code;
497
28
    }
498
499
    /* Firstly check if the Kids array has enough nodes, in which case it's
500
     * probably flat (the common case)
501
     */
502
72.2k
    if (a->size != ctx->num_pages) {
503
1.20k
        int i = 0;
504
1.20k
        pdf_obj *p = NULL, *p1 = NULL;
505
1.20k
        pdf_num *c = NULL;
506
507
        /* Either its not a flat tree, or the top node /Count is incorrect.
508
         * Get each entry in the Kids array in turn and total the /Count of
509
         * each node and add any leaf nodes.
510
         */
511
8.73k
        for (i=0;i < a->size; i++) {
512
            /* We must not allow the entry in the array to be replaced, in case it's a circular reference */
513
7.53k
            code = pdfi_array_get_no_store_R(ctx, a, i, &p);
514
7.53k
            if (code < 0)
515
3.13k
                continue;
516
4.40k
            if (pdfi_type_of(p) != PDF_DICT) {
517
1.14k
                pdfi_countdown(p);
518
1.14k
                p = NULL;
519
1.14k
                continue;
520
1.14k
            }
521
            /* Explicit check that the root node Kids array entry is not a self-reference
522
             * back to the root node. We only check one level of the Kids array. so we don't
523
             * need a full loop detection setup here.
524
             */
525
3.26k
            if (p->object_num != 0 && p->object_num == o1->object_num) {
526
11
                pdfi_countdown(p);
527
11
                p = NULL;
528
11
                pdfi_countdown(a);
529
11
                pdfi_countdown(o1);
530
11
                ctx->num_pages = 0;
531
11
                return_error(gs_error_circular_reference);
532
11
            }
533
            /* Now we've checked for circular reference we can replace the entry in the
534
             * array, and add the object to the cache.
535
             * These are optimisations, so we don't especially care whether they succeed
536
             */
537
3.25k
            (void)pdfi_array_put(ctx, a, i, p);
538
3.25k
            (void)replace_cache_entry(ctx, p);
539
3.25k
            code = pdfi_dict_knownget_type(ctx, (pdf_dict *)p, "Type", PDF_NAME, (pdf_obj **)&p1);
540
3.25k
            if (code <= 0) {
541
40
                pdfi_countdown(p);
542
40
                p = NULL;
543
40
                continue;
544
40
            }
545
3.21k
            if (pdfi_name_is((pdf_name *)p1, "Page")) {
546
1.83k
                pagecount++;
547
1.83k
            } else {
548
1.38k
                if (pdfi_name_is((pdf_name *)p1, "Pages")) {
549
1.30k
                    code = pdfi_dict_knownget(ctx, (pdf_dict *)p, "Count", (pdf_obj **)&c);
550
1.30k
                    if (code >= 0) {
551
1.30k
                        if (pdfi_type_of(c) == PDF_INT)
552
1.29k
                            pagecount += c->value.i;
553
1.30k
                        if (pdfi_type_of(c) == PDF_REAL)
554
0
                            pagecount += (int)c->value.d;
555
1.30k
                        pdfi_countdown(c);
556
1.30k
                        c = NULL;
557
1.30k
                    }
558
1.30k
                }
559
1.38k
            }
560
3.21k
            pdfi_countdown(p1);
561
3.21k
            p1 = NULL;
562
3.21k
            pdfi_countdown(p);
563
3.21k
            p = NULL;
564
3.21k
        }
565
1.20k
    } else
566
71.0k
        pagecount = a->size;
567
568
72.2k
    pdfi_countdown(a);
569
570
    /* If the count of the top level of the tree doesn't match the /Count
571
     * of the root node then something is wrong. We could abort right now
572
     * and will if this continues to be a problem, but initially let's assume
573
     * the count of the top level is correct and the root node /Count is wrong.
574
     * This will allow us to recover if only the root /Count gets corrupted.
575
     * In future we could also try validating the entire tree at this point,
576
     * though I suspect that's pointless; if the tree is corrupted we aren't
577
     * likely to get much that's usable from it.
578
     */
579
72.2k
    if (pagecount != ctx->num_pages) {
580
715
        ctx->num_pages = pagecount;
581
715
        if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_syntaxerror), NULL, E_PDF_BADPAGECOUNT, "pdfi_read_Pages", NULL)) < 0)
582
0
            return code;
583
715
        code = pdfi_dict_put_int(ctx, (pdf_dict *)o1, "Count", ctx->num_pages);
584
715
        if (code < 0) {
585
0
            pdfi_countdown(o1);
586
0
            return code;
587
0
        }
588
715
    }
589
590
    /* We don't pdfi_countdown(o1) now, because we've transferred our
591
     * reference to the pointer in the pdf_context structure.
592
     */
593
72.2k
    ctx->PagesTree = (pdf_dict *)o1;
594
72.2k
    return 0;
595
72.2k
}
596
597
/* Read optional things in from Root */
598
void pdfi_read_OptionalRoot(pdf_context *ctx)
599
72.7k
{
600
72.7k
    pdf_obj *obj = NULL;
601
72.7k
    int code;
602
72.7k
    bool known;
603
604
72.7k
    if (ctx->args.pdfdebug)
605
0
        outprintf(ctx->memory, "%% Reading other Root contents\n");
606
607
72.7k
    if (ctx->args.pdfdebug)
608
0
        outprintf(ctx->memory, "%% OCProperties\n");
609
72.7k
    code = pdfi_dict_get_type(ctx, ctx->Root, "OCProperties", PDF_DICT, &obj);
610
72.7k
    if (code == 0) {
611
3.11k
        ctx->OCProperties = (pdf_dict *)obj;
612
69.6k
    } else {
613
69.6k
        ctx->OCProperties = NULL;
614
69.6k
        if (ctx->args.pdfdebug)
615
0
            outprintf(ctx->memory, "%% (None)\n");
616
69.6k
    }
617
618
72.7k
    (void)pdfi_dict_known(ctx, ctx->Root, "Collection", &known);
619
620
72.7k
    if (known) {
621
0
        if (ctx->args.pdfdebug)
622
0
            outprintf(ctx->memory, "%% Collection\n");
623
0
        code = pdfi_dict_get(ctx, ctx->Root, "Collection", (pdf_obj **)&ctx->Collection);
624
0
        if (code < 0) {
625
0
            (void)pdfi_set_warning_stop(ctx, 0, NULL, W_PDF_BAD_COLLECTION, "pdfi_read_OptionalRoot", "");
626
0
        }
627
0
    }
628
72.7k
}
629
630
void pdfi_free_OptionalRoot(pdf_context *ctx)
631
108k
{
632
108k
    if (ctx->OCProperties) {
633
3.11k
        pdfi_countdown(ctx->OCProperties);
634
3.11k
        ctx->OCProperties = NULL;
635
3.11k
    }
636
108k
    if (ctx->Collection) {
637
0
        pdfi_countdown(ctx->Collection);
638
0
        ctx->Collection = NULL;
639
0
    }
640
108k
}
641
642
/* Handle child node processing for page_dict */
643
static int pdfi_get_child(pdf_context *ctx, pdf_array *Kids, int i, pdf_dict **pchild)
644
688k
{
645
688k
    pdf_indirect_ref *node = NULL;
646
688k
    pdf_dict *child = NULL;
647
688k
    pdf_name *Type = NULL;
648
688k
    pdf_dict *leaf_dict = NULL;
649
688k
    pdf_name *Key = NULL;
650
688k
    int code = 0;
651
652
688k
    code = pdfi_array_get_no_deref(ctx, Kids, i, (pdf_obj **)&node);
653
688k
    if (code < 0)
654
0
        goto errorExit;
655
656
688k
    if (pdfi_type_of(node) != PDF_INDIRECT && pdfi_type_of(node) != PDF_DICT) {
657
46
        code = gs_note_error(gs_error_typecheck);
658
46
        goto errorExit;
659
46
    }
660
661
688k
    if (pdfi_type_of(node) == PDF_INDIRECT) {
662
136k
        code = pdfi_deref_loop_detect(ctx, node->ref_object_num, node->ref_generation_num,
663
136k
                                (pdf_obj **)&child);
664
136k
        if (code < 0) {
665
33.9k
            int code1 = pdfi_repair_file(ctx);
666
33.9k
            if (code1 < 0)
667
33.9k
                goto errorExit;
668
30
            code = pdfi_deref_loop_detect(ctx, node->ref_object_num,
669
30
                                    node->ref_generation_num, (pdf_obj **)&child);
670
30
            if (code < 0)
671
17
                goto errorExit;
672
30
        }
673
        /* It makes no sense for the Page dictionary to be a stream, but safedocs/DialectDictIsStream.pdf
674
         * has this (stream length is 0, not that it matters) and Acrobat happily opens it....
675
         */
676
102k
        if (pdfi_type_of(child) != PDF_DICT) {
677
920
            pdf_dict *d1 = NULL;
678
679
920
            if (pdfi_type_of(child) != PDF_STREAM) {
680
695
                code = gs_note_error(gs_error_typecheck);
681
695
                goto errorExit;
682
695
            }
683
225
            if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_typecheck), NULL, E_PDF_DICT_IS_STREAM, "pdfi_get_child", NULL)) < 0)
684
0
                goto errorExit;
685
686
225
            code = pdfi_get_stream_dict(ctx, (pdf_stream *)child, &d1);
687
225
            if (code < 0)
688
0
                goto errorExit;
689
225
            pdfi_countdown(child);
690
225
            child = d1;
691
225
            code = replace_cache_entry(ctx, (pdf_obj *)d1);
692
225
            if (code < 0)
693
0
                goto errorExit;
694
225
        }
695
        /* If its an intermediate node, store it in the page_table, if its a leaf node
696
         * then don't store it. Instead we create a special dictionary of our own which
697
         * has a /Type of /PageRef and a /PageRef key which is the indirect reference
698
         * to the page. However in this case we pass on the actual page dictionary to
699
         * the Kids processing below. If we didn't then we'd fall foul of the loop
700
         * detection by dereferencing the same object twice.
701
         * This is tedious, but it means we don't store all the page dictionaries in
702
         * the Pages tree, because page dictionaries can be large and we generally
703
         * only use them once. If processed in order we only dereference each page
704
         * dictionary once, any other order will dereference each page twice. (or more
705
         * if we render the same page multiple times).
706
         */
707
101k
        code = pdfi_dict_get_type(ctx, child, "Type", PDF_NAME, (pdf_obj **)&Type);
708
101k
        if (code < 0)
709
1.14k
            goto errorExit;
710
100k
        if (pdfi_name_is(Type, "Pages")) {
711
862
            code = pdfi_array_put(ctx, Kids, i, (pdf_obj *)child);
712
862
            if (code < 0)
713
0
                goto errorExit;
714
99.8k
        } else {
715
            /* Bizarrely, one of the QL FTS files (FTS_07_0704.pdf) has a page diciotnary with a /Type of /Template */
716
99.8k
            if (!pdfi_name_is(Type, "Page"))
717
770
                if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_typecheck), NULL, E_PDF_BADPAGETYPE, "pdfi_get_child", NULL)) < 0)
718
0
                    goto errorExit;
719
            /* Make a 'PageRef' entry (just stores an indirect reference to the actual page)
720
             * and store that in the Kids array for future reference. But pass on the
721
             * dereferenced Page dictionary, in case this is the target page.
722
             */
723
724
99.8k
            code = pdfi_dict_alloc(ctx, 0, &leaf_dict);
725
99.8k
            if (code < 0)
726
0
                goto errorExit;
727
99.8k
            code = pdfi_name_alloc(ctx, (byte *)"PageRef", 7, (pdf_obj **)&Key);
728
99.8k
            if (code < 0)
729
0
                goto errorExit;
730
99.8k
            pdfi_countup(Key);
731
732
99.8k
            code = pdfi_dict_put_obj(ctx, leaf_dict, (pdf_obj *)Key, (pdf_obj *)node, true);
733
99.8k
            if (code < 0)
734
0
                goto errorExit;
735
99.8k
            code = pdfi_dict_put(ctx, leaf_dict, "Type", (pdf_obj *)Key);
736
99.8k
            if (code < 0)
737
0
                goto errorExit;
738
99.8k
            code = pdfi_array_put(ctx, Kids, i, (pdf_obj *)leaf_dict);
739
99.8k
            if (code < 0)
740
0
                goto errorExit;
741
99.8k
            leaf_dict = NULL;
742
99.8k
        }
743
552k
    } else {
744
552k
        if (ctx->loop_detection != NULL) {
745
552k
            if (node->object_num != 0 && pdfi_loop_detector_check_object(ctx, node->object_num)) {
746
0
                code = gs_note_error(gs_error_circular_reference);
747
0
                goto errorExit;
748
0
            }
749
552k
        }
750
552k
        child = (pdf_dict *)node;
751
552k
        pdfi_countup(child);
752
552k
    }
753
754
653k
    *pchild = child;
755
653k
    child = NULL;
756
757
688k
 errorExit:
758
688k
    pdfi_free_object((pdf_obj *)leaf_dict);
759
688k
    pdfi_countdown(child);
760
688k
    pdfi_countdown(node);
761
688k
    pdfi_countdown(Type);
762
688k
    pdfi_countdown(Key);
763
688k
    return code;
764
653k
}
765
766
/* Check if key is in the dictionary, and if so, copy it into the inheritable dict.
767
 */
768
static int pdfi_check_inherited_key(pdf_context *ctx, pdf_dict *d, const char *keyname, pdf_dict *inheritable)
769
992k
{
770
992k
    int code = 0;
771
992k
    pdf_obj *object = NULL;
772
992k
    bool known;
773
774
    /* Check for inheritable keys, if we find any copy them to the 'inheritable' dictionary at this level */
775
992k
    code = pdfi_dict_known(ctx, d, keyname, &known);
776
992k
    if (code < 0)
777
0
        goto exit;
778
992k
    if (known) {
779
60.9k
        code = pdfi_loop_detector_mark(ctx);
780
60.9k
        if (code < 0){
781
0
            goto exit;
782
0
        }
783
60.9k
        code = pdfi_dict_get(ctx, d, keyname, &object);
784
60.9k
        if (code < 0) {
785
116
            (void)pdfi_loop_detector_cleartomark(ctx);
786
116
            goto exit;
787
116
        }
788
60.8k
        code = pdfi_loop_detector_cleartomark(ctx);
789
60.8k
        if (code < 0) {
790
0
            goto exit;
791
0
        }
792
60.8k
        code = pdfi_dict_put(ctx, inheritable, keyname, object);
793
60.8k
    }
794
795
992k
 exit:
796
992k
    pdfi_countdown(object);
797
992k
    return code;
798
992k
}
799
800
int pdfi_get_page_dict(pdf_context *ctx, pdf_dict *d, uint64_t page_num, uint64_t *page_offset,
801
                   pdf_dict **target, pdf_dict *inherited)
802
248k
{
803
248k
    int i, code = 0;
804
248k
    pdf_array *Kids = NULL;
805
248k
    pdf_dict *child = NULL;
806
248k
    pdf_name *Type = NULL;
807
248k
    pdf_dict *inheritable = NULL;
808
248k
    int64_t num;
809
248k
    double dbl;
810
811
248k
    if (ctx->args.pdfdebug)
812
0
        outprintf(ctx->memory, "%% Finding page dictionary for page %"PRIi64"\n", page_num + 1);
813
814
    /* Allocated inheritable dict (it might stay empty) */
815
248k
    code = pdfi_dict_alloc(ctx, 0, &inheritable);
816
248k
    if (code < 0)
817
0
        return code;
818
248k
    pdfi_countup(inheritable);
819
820
248k
    code = pdfi_loop_detector_mark(ctx);
821
248k
    if (code < 0)
822
0
        return code;
823
824
    /* if we are being passed any inherited values from our parent, copy them now */
825
248k
    if (inherited != NULL) {
826
9.63k
        code = pdfi_dict_copy(ctx, inheritable, inherited);
827
9.63k
        if (code < 0)
828
0
            goto exit;
829
9.63k
    }
830
831
248k
    code = pdfi_dict_get_number(ctx, d, "Count", &dbl);
832
248k
    if (code < 0)
833
0
        goto exit;
834
248k
    if (dbl != floor(dbl)) {
835
0
        code = gs_note_error(gs_error_rangecheck);
836
0
        goto exit;
837
0
    }
838
248k
    num = (int)dbl;
839
840
248k
    if (num < 0 || (num + *page_offset) > ctx->num_pages) {
841
0
        code = gs_note_error(gs_error_rangecheck);
842
0
        goto exit;
843
0
    }
844
248k
    if (num + *page_offset < page_num) {
845
0
        *page_offset += num;
846
0
        code = 1;
847
0
        goto exit;
848
0
    }
849
    /* The requested page is a descendant of this node */
850
851
    /* Check for inheritable keys, if we find any copy them to the 'inheritable' dictionary at this level */
852
248k
    code = pdfi_check_inherited_key(ctx, d, "Resources", inheritable);
853
248k
    if (code < 0)
854
116
        goto exit;
855
248k
    code = pdfi_check_inherited_key(ctx, d, "MediaBox", inheritable);
856
248k
    if (code < 0)
857
0
        goto exit;
858
248k
    code = pdfi_check_inherited_key(ctx, d, "CropBox", inheritable);
859
248k
    if (code < 0)
860
0
        goto exit;
861
248k
    code = pdfi_check_inherited_key(ctx, d, "Rotate", inheritable);
862
248k
    if (code < 0) {
863
0
        goto exit;
864
0
    }
865
866
    /* Get the Kids array */
867
248k
    code = pdfi_dict_get_type(ctx, d, "Kids", PDF_ARRAY, (pdf_obj **)&Kids);
868
248k
    if (code < 0) {
869
20
        goto exit;
870
20
    }
871
872
    /* Check each entry in the Kids array */
873
688k
    for (i = 0;i < pdfi_array_size(Kids);i++) {
874
688k
        pdfi_countdown(child);
875
688k
        child = NULL;
876
688k
        pdfi_countdown(Type);
877
688k
        Type = NULL;
878
879
688k
        code = pdfi_get_child(ctx, Kids, i, &child);
880
688k
        if (code < 0) {
881
35.8k
            goto exit;
882
35.8k
        }
883
884
        /* Check the type, if its a Pages entry, then recurse. If its a Page entry, is it the one we want */
885
653k
        code = pdfi_dict_get_type(ctx, child, "Type", PDF_NAME, (pdf_obj **)&Type);
886
653k
        if (code == 0) {
887
652k
            if (pdfi_name_is(Type, "Pages")) {
888
15.3k
                code = pdfi_dict_get_number(ctx, child, "Count", &dbl);
889
15.3k
                if (code == 0) {
890
15.2k
                    if (dbl != floor(dbl)) {
891
0
                        code = gs_note_error(gs_error_rangecheck);
892
0
                        goto exit;
893
0
                    }
894
15.2k
                    num = (int)dbl;
895
15.2k
                    if (num < 0 || (num + *page_offset) > ctx->num_pages) {
896
17
                        code = gs_note_error(gs_error_rangecheck);
897
17
                        goto exit;
898
15.2k
                    } else {
899
15.2k
                        if (num + *page_offset <= page_num) {
900
5.64k
                            *page_offset += num;
901
9.63k
                        } else {
902
9.63k
                            if (child->object_num > 0) {
903
9.63k
                                code = pdfi_loop_detector_mark(ctx);
904
9.63k
                                if (code < 0)
905
9.63k
                                    goto exit;;
906
907
9.63k
                                code = pdfi_loop_detector_add_object(ctx, child->object_num);
908
9.63k
                                if (code < 0)
909
0
                                    goto exit;
910
9.63k
                            }
911
9.63k
                            code = pdfi_get_page_dict(ctx, child, page_num, page_offset, target, inheritable);
912
9.63k
                            if (child->object_num > 0)
913
9.63k
                                pdfi_loop_detector_cleartomark(ctx);
914
9.63k
                            goto exit;
915
9.63k
                        }
916
15.2k
                    }
917
15.2k
                }
918
637k
            } else {
919
637k
                if (pdfi_name_is(Type, "PageRef")) {
920
518k
                    if ((*page_offset) == page_num) {
921
100k
                        pdf_dict *page_dict = NULL;
922
923
100k
                        code = pdfi_dict_get_no_store_R(ctx, child, "PageRef", (pdf_obj **)&page_dict);
924
100k
                        if (code < 0)
925
3
                            goto exit;
926
100k
                        if (pdfi_type_of(page_dict) != PDF_DICT) {
927
0
                            code = gs_note_error(gs_error_typecheck);
928
0
                            goto exit;
929
0
                        }
930
100k
                        code = pdfi_merge_dicts(ctx, page_dict, inheritable);
931
100k
                        *target = page_dict;
932
100k
                        pdfi_countup(*target);
933
100k
                        pdfi_countdown(page_dict);
934
100k
                        goto exit;
935
417k
                    } else {
936
417k
                        *page_offset += 1;
937
417k
                    }
938
518k
                } else {
939
119k
                    if (!pdfi_name_is(Type, "Page")) {
940
974
                        if ((code = pdfi_set_error_stop(ctx, gs_note_error(gs_error_typecheck), NULL, E_PDF_BADPAGETYPE, "pdfi_get_page_dict", NULL)) < 0)
941
0
                            goto exit;
942
974
                    }
943
119k
                    if ((*page_offset) == page_num) {
944
102k
                        code = pdfi_merge_dicts(ctx, child, inheritable);
945
102k
                        *target = child;
946
102k
                        pdfi_countup(*target);
947
102k
                        goto exit;
948
102k
                    } else {
949
17.2k
                        *page_offset += 1;
950
17.2k
                    }
951
119k
                }
952
637k
            }
953
652k
        }
954
441k
        if (code < 0)
955
154
            goto exit;
956
441k
    }
957
    /* Positive return value indicates we did not find the target below this node, try the next one */
958
34
    code = 1;
959
960
248k
 exit:
961
248k
    pdfi_loop_detector_cleartomark(ctx);
962
248k
    pdfi_countdown(inheritable);
963
248k
    pdfi_countdown(Kids);
964
248k
    pdfi_countdown(child);
965
248k
    pdfi_countdown(Type);
966
248k
    return code;
967
34
}
968
969
int pdfi_doc_page_array_init(pdf_context *ctx)
970
72.7k
{
971
72.7k
    size_t size = ctx->num_pages*sizeof(uint32_t);
972
973
72.7k
    ctx->page_array = (uint32_t *)gs_alloc_bytes(ctx->memory, size,
974
72.7k
                                                 "pdfi_doc_page_array_init(page_array)");
975
72.7k
    if (ctx->page_array == NULL)
976
1
        return_error(gs_error_VMerror);
977
978
72.7k
    memset(ctx->page_array, 0, size);
979
72.7k
    return 0;
980
72.7k
}
981
982
void pdfi_doc_page_array_free(pdf_context *ctx)
983
108k
{
984
108k
    if (!ctx->page_array)
985
36.1k
        return;
986
72.7k
    gs_free_object(ctx->memory, ctx->page_array, "pdfi_doc_page_array_free(page_array)");
987
72.7k
    ctx->page_array = NULL;
988
72.7k
}
989
990
/*
991
 * Checks for both "Resource" and "RD" in the specified dict.
992
 * And then gets the typedict of Type (e.g. Font or XObject).
993
 * Returns 0 if undefined, >0 if found, <0 if error
994
 */
995
static int pdfi_resource_knownget_typedict(pdf_context *ctx, unsigned char *Type,
996
                                           pdf_dict *dict, pdf_dict **typedict)
997
5.13M
{
998
5.13M
    int code;
999
5.13M
    pdf_dict *Resources = NULL;
1000
1001
5.13M
    code = pdfi_dict_knownget_type(ctx, dict, "Resources", PDF_DICT, (pdf_obj **)&Resources);
1002
5.13M
    if (code == 0)
1003
2.11M
        code = pdfi_dict_knownget_type(ctx, dict, "DR", PDF_DICT, (pdf_obj **)&Resources);
1004
5.13M
    if (code < 0)
1005
179k
        goto exit;
1006
4.95M
    if (code > 0)
1007
2.83M
        code = pdfi_dict_knownget_type(ctx, Resources, (const char *)Type, PDF_DICT, (pdf_obj **)typedict);
1008
5.13M
 exit:
1009
5.13M
    pdfi_countdown(Resources);
1010
5.13M
    return code;
1011
4.95M
}
1012
1013
int pdfi_find_resource(pdf_context *ctx, unsigned char *Type, pdf_name *name,
1014
                       pdf_dict *dict, pdf_dict *page_dict, pdf_obj **o)
1015
2.28M
{
1016
2.28M
    pdf_dict *typedict = NULL;
1017
2.28M
    pdf_dict *Parent = NULL;
1018
2.28M
    pdf_name *n = NULL;
1019
2.28M
    int code;
1020
2.28M
    bool known = false;
1021
1022
2.28M
    *o = NULL;
1023
1024
    /* Check the provided dict, stream_dict can be NULL if we are trying to find a Default* ColorSpace */
1025
2.28M
    if (dict != NULL) {
1026
2.28M
        bool deref_parent = true, dict_is_XObject = false;
1027
1028
2.28M
        code = pdfi_resource_knownget_typedict(ctx, Type, dict, &typedict);
1029
2.28M
        if (code < 0)
1030
3.74k
            goto exit;
1031
2.28M
        if (code > 0) {
1032
440k
            code = pdfi_dict_get_no_store_R_key(ctx, typedict, name, o);
1033
440k
            if (code != gs_error_undefined)
1034
351k
                goto exit;
1035
440k
        }
1036
1037
        /* Check the Parents, if any */
1038
        /* If the current dictionary is a Page dictionary, do NOT dereference it's Parent, as that
1039
         * will be the Pages tree, and we will end up with circular references, causing a memory leak.
1040
         */
1041
1.93M
        if (pdfi_dict_knownget_type(ctx, dict, "Type", PDF_NAME, (pdf_obj **)&n) > 0) {
1042
95.5k
            if (pdfi_name_is(n, "Page"))
1043
7
                deref_parent = false;
1044
95.5k
            if (pdfi_name_is(n, "XObject"))
1045
81.1k
                dict_is_XObject = true;
1046
95.5k
            pdfi_countdown(n);
1047
95.5k
        }
1048
1049
1.93M
        if (deref_parent) {
1050
1.93M
            code = pdfi_dict_known(ctx, dict, "Parent", &known);
1051
1.93M
            if (code >= 0 && known == true) {
1052
85.9k
                code = pdfi_dict_get_no_store_R(ctx, dict, "Parent", (pdf_obj **)&Parent);
1053
1054
85.9k
                if (code >= 0) {
1055
80.9k
                    if (pdfi_type_of(Parent) != PDF_DICT) {
1056
95
                        if (pdfi_type_of(Parent) == PDF_INDIRECT) {
1057
0
                            pdf_indirect_ref *o = (pdf_indirect_ref *)Parent;
1058
1059
0
                            Parent = NULL;
1060
0
                            code = pdfi_dereference(ctx, o->ref_object_num, o->ref_generation_num, (pdf_obj **)&Parent);
1061
0
                            pdfi_countdown(o);
1062
0
                            if (code >= 0 && pdfi_type_of(Parent) != PDF_DICT) {
1063
0
                                pdfi_countdown(Parent);
1064
0
                                Parent = NULL;
1065
0
                            }
1066
95
                        } else {
1067
95
                            pdfi_countdown(Parent);
1068
95
                            Parent = NULL;
1069
95
                        }
1070
95
                    }
1071
80.9k
                } else
1072
5.02k
                    Parent = NULL;
1073
85.9k
            }
1074
1075
1.93M
            if (Parent != NULL) {
1076
80.8k
                if (ctx->page.CurrentPageDict != NULL && Parent->object_num != ctx->page.CurrentPageDict->object_num) {
1077
80.8k
                    if (pdfi_loop_detector_check_object(ctx, Parent->object_num) == true) {
1078
7.89k
                        code = gs_note_error(gs_error_circular_reference);
1079
7.89k
                        goto exit;
1080
7.89k
                    }
1081
1082
72.9k
                    code = pdfi_loop_detector_mark(ctx);
1083
72.9k
                    if (code < 0)
1084
0
                        goto exit;
1085
1086
72.9k
                    code = pdfi_loop_detector_add_object(ctx, dict->object_num);
1087
72.9k
                    if (code < 0) {
1088
0
                        (void)pdfi_loop_detector_cleartomark(ctx);
1089
0
                        goto exit;
1090
0
                    }
1091
72.9k
                    code = pdfi_find_resource(ctx, Type, name, Parent, page_dict, o);
1092
72.9k
                    (void)pdfi_loop_detector_cleartomark(ctx);
1093
72.9k
                    if (code != gs_error_undefined) {
1094
425
                        if (dict_is_XObject)
1095
374
                            pdfi_set_warning(ctx, 0, NULL, W_PDF_INHERITED_STREAM_RESOURCE, "pdfi_find_resource", (char *)"Couldn't find named resource in supplied dictionary, matching name located in Page Resource");
1096
425
                        goto exit;
1097
425
                    }
1098
72.9k
                }
1099
80.8k
            }
1100
1.92M
            code = 0;
1101
1.92M
        }
1102
1.92M
        pdfi_countdown(typedict);
1103
1.92M
        typedict = NULL;
1104
1.92M
    }
1105
1106
    /* Normally page_dict can't be (or shouldn't be) NULL. However, if we are processing
1107
     * a TYpe 3 font, then the 'page dict' is the Resources dictionary of that font. If
1108
     * the font inherits Resources from its page (which it should not) then its possible
1109
     * that the 'page dict' could be NULL here. We need to guard against that. Its possible
1110
     * there may be other, similar, cases (eg Patterns within Patterns). In addition we
1111
     * do need to be able to check the real page dictionary for inhereited resources, and
1112
     * in the case of a type 3 font BuildChar at least there is no easy way to do that.
1113
     * So we'll store the page dictionary for the current page in the context as a
1114
     * last-ditch resource to check.
1115
     */
1116
1.92M
    if (page_dict != NULL) {
1117
1.92M
        code = pdfi_resource_knownget_typedict(ctx, Type, page_dict, &typedict);
1118
1.92M
        if (code < 0)
1119
231k
            goto exit;
1120
1121
1.69M
        if (code > 0) {
1122
1.55M
            code = pdfi_dict_get_no_store_R_key(ctx, typedict, name, o);
1123
1.55M
            if (code != gs_error_undefined)
1124
1.18M
                goto exit;
1125
1.55M
        }
1126
1.69M
    }
1127
1128
511k
    pdfi_countdown(typedict);
1129
511k
    typedict = NULL;
1130
1131
511k
    if (ctx->page.CurrentPageDict != NULL) {
1132
511k
        code = pdfi_resource_knownget_typedict(ctx, Type, ctx->page.CurrentPageDict, &typedict);
1133
511k
        if (code < 0)
1134
135
            goto exit;
1135
1136
510k
        if (code > 0) {
1137
383k
            code = pdfi_dict_get_no_store_R_key(ctx, typedict, name, o);
1138
383k
            if (code != gs_error_undefined)
1139
3.67k
                goto exit;
1140
383k
        }
1141
510k
    }
1142
1143
507k
    pdfi_countdown(typedict);
1144
507k
    typedict = NULL;
1145
1146
507k
    if (ctx->current_stream != NULL) {
1147
256k
        pdf_dict *stream_dict = NULL;
1148
256k
        pdf_stream *stream = ctx->current_stream;
1149
1150
404k
        do {
1151
404k
            code = pdfi_dict_from_obj(ctx, (pdf_obj *)stream, &stream_dict);
1152
404k
            if (code < 0)
1153
0
                goto exit;
1154
404k
            code = pdfi_resource_knownget_typedict(ctx, Type, stream_dict, &typedict);
1155
404k
            if (code < 0)
1156
0
                goto exit;
1157
404k
            if (code > 0) {
1158
152k
                code = pdfi_dict_get_no_store_R_key(ctx, typedict, name, o);
1159
152k
                if (code == 0) {
1160
8
                    pdfi_set_error(ctx, 0, NULL, E_PDF_INHERITED_STREAM_RESOURCE, "pdfi_find_resource", (char *)"Couldn't find named resource in supplied dictionary, or Parents, or Pages, matching name located in earlier stream Resource");
1161
8
                    goto exit;
1162
8
                }
1163
152k
            }
1164
404k
            pdfi_countdown(typedict);
1165
404k
            typedict = NULL;
1166
404k
            stream = pdfi_stream_parent(ctx, stream);
1167
404k
        }while(stream != NULL);
1168
256k
    }
1169
1170
    /* If we got all the way down there, we didn't find it */
1171
507k
    pdfi_set_warning(ctx, 0, NULL, W_PDF_MISSING_NAMED_RESOURCE, "pdfi_find_resource", NULL);
1172
507k
    code = gs_error_undefined;
1173
1174
2.28M
exit:
1175
2.28M
    pdfi_countdown(typedict);
1176
2.28M
    pdfi_countdown(Parent);
1177
2.28M
    return code;
1178
507k
}
1179
1180
/* Mark the actual outline */
1181
static int pdfi_doc_mark_the_outline(pdf_context *ctx, pdf_dict *outline)
1182
727
{
1183
727
    int code = 0;
1184
727
    pdf_dict *tempdict = NULL;
1185
727
    uint64_t dictsize;
1186
727
    uint64_t index;
1187
727
    pdf_name *Key = NULL;
1188
727
    double num = 0;
1189
1190
    /* Basically we only do /Count, /Title, /A, /C, /F
1191
     * The /First, /Last, /Next, /Parent get written magically by pdfwrite
1192
     */
1193
1194
    /* Make a temporary copy of the outline dict */
1195
727
    dictsize = pdfi_dict_entries(outline);
1196
727
    code = pdfi_dict_alloc(ctx, dictsize, &tempdict);
1197
727
    if (code < 0) goto exit;
1198
727
    pdfi_countup(tempdict);
1199
727
    code = pdfi_dict_copy(ctx, tempdict, outline);
1200
727
    if (code < 0) goto exit;
1201
1202
    /* Due to some craziness on the part of Adobe, the /Count in an Outline entry
1203
     * in a PDF file, and the /Count value in an /OUT pdfmark mean different things(!)
1204
     * In a PDF file it is the number of outline entries beneath the current entry
1205
     * (all child descsndants) whereas in a pdfmark it is the number of entries
1206
     * in just the next level. So we cannot use the /Count from the PDF file, we
1207
     * need to go to the /First entry of the next level, and then count all
1208
     * the entries at that level by following each /Next.
1209
     */
1210
727
    code = pdfi_dict_knownget_number(ctx, outline, "Count", &num);
1211
727
    if (code < 0)
1212
0
        goto exit;
1213
1214
    /* We can't rely on Count being present to indicate that the Outline has descendants
1215
     * see bug #707228. Instead, look for the presence of a /First key. If there is no count
1216
     * key, or it is 0, then assume the entry is closed.
1217
     */
1218
727
    {
1219
727
        pdf_dict *current = NULL, *next = NULL;
1220
727
        int count = 0, code1;
1221
1222
727
        code1 = pdfi_dict_knownget_type(ctx, outline, "First", PDF_DICT, (pdf_obj **)&current);
1223
727
        if (code1 > 0) {
1224
58
            if (code <= 0) {
1225
0
                if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_OUTLINECHILD_NO_COUNT, "pdfi_doc_mark_the_outline", NULL)) < 0)
1226
0
                    goto exit;
1227
0
            }
1228
58
            count++;
1229
116
            do {
1230
116
                code1 = pdfi_dict_knownget_type(ctx, current, "Next", PDF_DICT, (pdf_obj **)&next);
1231
116
                if (code1 > 0) {
1232
58
                    pdfi_countdown(current);
1233
58
                    current = next;
1234
58
                    next = NULL;
1235
58
                    count++;
1236
58
                } else
1237
58
                    break;
1238
116
            } while (1);
1239
58
            pdfi_countdown(current);
1240
58
        }
1241
727
        if (num <= 0)
1242
671
            count *= -1;
1243
727
        pdfi_dict_put_int(ctx, tempdict, "Count", count);
1244
727
    }
1245
1246
    /* Go through the dict, removing some keys and doing special handling for others.
1247
     */
1248
0
    code = pdfi_dict_key_first(ctx, outline, (pdf_obj **)&Key, &index);
1249
2.89k
    while (code >= 0) {
1250
2.89k
        if (pdfi_name_is(Key, "Last") || pdfi_name_is(Key, "Next") || pdfi_name_is(Key, "First") ||
1251
2.54k
            pdfi_name_is(Key, "Prev") || pdfi_name_is(Key, "Parent")) {
1252
            /* Delete some keys
1253
             * These are handled in pdfwrite and can lead to circular refs
1254
             */
1255
1.25k
            code = pdfi_dict_delete_pair(ctx, tempdict, Key);
1256
1.63k
        } else if (pdfi_name_is(Key, "SE")) {
1257
            /* TODO: Not sure what to do with SE, delete for now */
1258
            /* Seems to be okay to just delete it, since there should also be a /Dest
1259
             * See sample fts_29_2903.pdf
1260
             * Currently we are same as gs
1261
             */
1262
2
            code = pdfi_dict_delete_pair(ctx, tempdict, Key);
1263
1.63k
        } else if (pdfi_name_is(Key, "A")) {
1264
437
            code = pdfi_pdfmark_modA(ctx, tempdict);
1265
1.20k
        } else if (pdfi_name_is(Key, "Dest")) {
1266
286
            code = pdfi_pdfmark_modDest(ctx, tempdict);
1267
286
        }
1268
2.89k
        if (code < 0)
1269
76
            goto exit;
1270
1271
2.81k
        pdfi_countdown(Key);
1272
2.81k
        Key = NULL;
1273
1274
2.81k
        code = pdfi_dict_key_next(ctx, outline, (pdf_obj **)&Key, &index);
1275
2.81k
        if (code == gs_error_undefined) {
1276
651
            code = 0;
1277
651
            break;
1278
651
        }
1279
2.81k
    }
1280
651
    if (code < 0) goto exit;
1281
1282
    /* Write the pdfmark */
1283
651
    code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "OUT");
1284
651
    if (code < 0)
1285
2
        goto exit;
1286
1287
727
 exit:
1288
727
    pdfi_countdown(tempdict);
1289
727
    pdfi_countdown(Key);
1290
727
    return code;
1291
651
}
1292
1293
/* Do pdfmark on an outline entry (recursive)
1294
 * Note: the logic here is wonky.  It is relying on the behavior of the pdfwrite driver.
1295
 * See pdf_main.ps/writeoutline()
1296
 */
1297
static int pdfi_doc_mark_outline(pdf_context *ctx, pdf_dict *outline)
1298
727
{
1299
727
    int code = 0;
1300
727
    pdf_dict *child = NULL;
1301
727
    pdf_dict *Next = NULL;
1302
1303
727
    if (outline == (pdf_dict *)PDF_NULL_OBJ)
1304
0
        return 0;
1305
1306
    /* Mark the outline */
1307
    /* NOTE: I think the pdfmark for this needs to be written before the children
1308
     * because I think pdfwrite relies on the order of things.
1309
     */
1310
727
    code = pdfi_doc_mark_the_outline(ctx, outline);
1311
727
    if (code < 0)
1312
78
        goto exit1;
1313
1314
    /* Handle the children */
1315
649
    code = pdfi_loop_detector_mark(ctx);
1316
649
    if (code < 0)
1317
0
        goto exit1;
1318
1319
    /* Handle any children (don't deref them, we don't want to leave them hanging around) */
1320
649
    code = pdfi_dict_get_no_store_R(ctx, outline, "First", (pdf_obj **)&child);
1321
649
    if (code < 0 || pdfi_type_of(child) != PDF_DICT) {
1322
        /* TODO: flag a warning? */
1323
597
        code = 0;
1324
597
        goto exit;
1325
597
    }
1326
1327
52
    if (child->object_num != 0) {
1328
52
        code = pdfi_loop_detector_add_object(ctx, child->object_num);
1329
52
        if (code < 0)
1330
0
            goto exit;
1331
52
    }
1332
1333
99
    do {
1334
99
        code = pdfi_doc_mark_outline(ctx, child);
1335
99
        if (code < 0) goto exit;
1336
1337
1338
91
        code = pdfi_dict_get_no_store_R(ctx, child, "Next", (pdf_obj **)&Next);
1339
91
        if (code == gs_error_undefined) {
1340
43
            code = 0;
1341
43
            break;
1342
43
        }
1343
48
        if (code == gs_error_circular_reference) {
1344
0
            code = 0;
1345
0
            goto exit;
1346
0
        }
1347
48
        if (code < 0 || pdfi_type_of(Next) != PDF_DICT)
1348
1
            goto exit;
1349
1350
47
        pdfi_countdown(child);
1351
47
        child = Next;
1352
47
        Next = NULL;
1353
47
    } while (true);
1354
1355
649
 exit:
1356
649
    (void)pdfi_loop_detector_cleartomark(ctx);
1357
727
 exit1:
1358
727
    pdfi_countdown(child);
1359
727
    pdfi_countdown(Next);
1360
727
    return code;
1361
649
}
1362
1363
/* Do pdfmark for Outlines */
1364
static int pdfi_doc_Outlines(pdf_context *ctx)
1365
8.87k
{
1366
8.87k
    int code = 0;
1367
8.87k
    pdf_dict *Outlines = NULL;
1368
8.87k
    pdf_dict *outline = NULL;
1369
8.87k
    pdf_dict *Next = NULL;
1370
1371
8.87k
    if (ctx->args.no_pdfmark_outlines)
1372
0
        goto exit1;
1373
1374
8.87k
    code = pdfi_dict_knownget_type(ctx, ctx->Root, "Outlines", PDF_DICT, (pdf_obj **)&Outlines);
1375
8.87k
    if (code <= 0) {
1376
        /* TODO: flag a warning */
1377
8.26k
        code = 0;
1378
8.26k
        goto exit1;
1379
8.26k
    }
1380
1381
606
    code = pdfi_loop_detector_mark(ctx);
1382
606
    if (code < 0)
1383
0
        goto exit1;
1384
1385
    /* Handle any children (don't deref them, we don't want to leave them hanging around) */
1386
606
    code = pdfi_dict_get_no_store_R(ctx, Outlines, "First", (pdf_obj **)&outline);
1387
606
    if (code < 0 || pdfi_type_of(outline) != PDF_DICT) {
1388
        /* TODO: flag a warning? */
1389
147
        code = 0;
1390
147
        goto exit;
1391
147
    }
1392
1393
459
    if (pdfi_type_of(outline) != PDF_DICT)
1394
0
        goto exit; /* Exit with no error? */
1395
1396
459
    if (outline->object_num != 0) {
1397
459
        code = pdfi_loop_detector_add_object(ctx, outline->object_num);
1398
459
        if (code < 0)
1399
0
            goto exit;
1400
459
    }
1401
1402
    /* Loop through all the top-level outline entries
1403
     * First one is in Outlines, and if there are more, they are the Next of the
1404
     * current outline item.  (see spec)
1405
     * (basically we are walking a linked list)
1406
     */
1407
628
    do {
1408
628
        code = pdfi_doc_mark_outline(ctx, outline);
1409
628
        if (code < 0) goto exit;
1410
1411
1412
549
        code = pdfi_dict_get_no_store_R(ctx, outline, "Next", (pdf_obj **)&Next);
1413
549
        if (code == gs_error_undefined) {
1414
378
            code = 0;
1415
378
            break;
1416
378
        }
1417
171
        if (code == gs_error_circular_reference) {
1418
0
            code = 0;
1419
0
            goto exit;
1420
0
        }
1421
171
        if (code < 0 || pdfi_type_of(Next) != PDF_DICT)
1422
2
            goto exit;
1423
1424
169
        pdfi_countdown(outline);
1425
169
        outline = Next;
1426
169
        Next = NULL;
1427
169
    } while (true);
1428
1429
606
 exit:
1430
606
    (void)pdfi_loop_detector_cleartomark(ctx);
1431
8.87k
 exit1:
1432
8.87k
    pdfi_countdown(Outlines);
1433
8.87k
    pdfi_countdown(outline);
1434
8.87k
    pdfi_countdown(Next);
1435
8.87k
    return code;
1436
606
}
1437
1438
/* Do pdfmark for Info */
1439
static int pdfi_doc_Info(pdf_context *ctx)
1440
8.87k
{
1441
8.87k
    int code = 0;
1442
8.87k
    pdf_dict *Info = NULL, *d = NULL;
1443
8.87k
    pdf_dict *tempdict = NULL;
1444
8.87k
    uint64_t dictsize;
1445
8.87k
    uint64_t index;
1446
8.87k
    pdf_name *Key = NULL;
1447
8.87k
    pdf_obj *Value = NULL;
1448
1449
    /* See comment in pdfi_read_Root() for details */
1450
8.87k
    d = ctx->Trailer;
1451
8.87k
    pdfi_countup(d);
1452
8.87k
    code = pdfi_dict_knownget_type(ctx, d, "Info", PDF_DICT, (pdf_obj **)&Info);
1453
8.87k
    pdfi_countdown(d);
1454
8.87k
    if (code <= 0) {
1455
        /* TODO: flag a warning */
1456
6.32k
        goto exit;
1457
6.32k
    }
1458
1459
    /* Make a temporary copy of the Info dict */
1460
2.54k
    dictsize = pdfi_dict_entries(Info);
1461
2.54k
    code = pdfi_dict_alloc(ctx, dictsize, &tempdict);
1462
2.54k
    if (code < 0) goto exit;
1463
2.54k
    pdfi_countup(tempdict);
1464
1465
    /* Copy only certain keys from Info to tempdict
1466
     * NOTE: pdfwrite will set /Producer, /CreationDate and /ModDate
1467
     */
1468
2.54k
    code = pdfi_dict_first(ctx, Info, (pdf_obj **)&Key, &Value, &index);
1469
12.4k
    while (code >= 0) {
1470
12.4k
        if (pdfi_name_is(Key, "Author") || pdfi_name_is(Key, "Creator") ||
1471
9.37k
            pdfi_name_is(Key, "Title") || pdfi_name_is(Key, "Subject") ||
1472
8.05k
            pdfi_name_is(Key, "Keywords")) {
1473
1474
4.74k
            code = pdfi_dict_put_obj(ctx, tempdict, (pdf_obj *)Key, Value, true);
1475
4.74k
            if (code < 0)
1476
0
                goto exit;
1477
4.74k
        }
1478
12.4k
        pdfi_countdown(Key);
1479
12.4k
        Key = NULL;
1480
12.4k
        pdfi_countdown(Value);
1481
12.4k
        Value = NULL;
1482
1483
12.4k
        code = pdfi_dict_next(ctx, Info, (pdf_obj **)&Key, &Value, &index);
1484
12.4k
        if (code == gs_error_undefined) {
1485
2.49k
            code = 0;
1486
2.49k
            break;
1487
2.49k
        }
1488
12.4k
    }
1489
2.54k
    if (code < 0) goto exit;
1490
1491
    /* Write the pdfmark */
1492
2.49k
    code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "DOCINFO");
1493
1494
8.87k
 exit:
1495
8.87k
    pdfi_countdown(Key);
1496
8.87k
    pdfi_countdown(Value);
1497
8.87k
    pdfi_countdown(Info);
1498
8.87k
    pdfi_countdown(tempdict);
1499
8.87k
    return code;
1500
2.49k
}
1501
1502
/* Handle PageLabels for pdfwrite device */
1503
static int pdfi_doc_PageLabels(pdf_context *ctx)
1504
72.7k
{
1505
72.7k
    int code;
1506
72.7k
    pdf_dict *PageLabels = NULL;
1507
1508
72.7k
    if (ctx->loop_detection) {
1509
0
        code = pdfi_loop_detector_mark(ctx);
1510
0
        if (code < 0)
1511
0
            return code;
1512
0
    }
1513
1514
72.7k
    code = pdfi_dict_knownget_type(ctx, ctx->Root, "PageLabels", PDF_DICT, (pdf_obj **)&PageLabels);
1515
72.7k
    if (code <= 0) {
1516
69.9k
        if (ctx->loop_detection)
1517
0
            (void)pdfi_loop_detector_cleartomark(ctx);
1518
        /* TODO: flag a warning */
1519
69.9k
        goto exit;
1520
69.9k
    }
1521
1522
2.76k
    if (ctx->loop_detection) {
1523
0
        code = pdfi_loop_detector_cleartomark(ctx);
1524
0
        if (code < 0)
1525
0
            goto exit;
1526
0
    }
1527
1528
2.76k
    if (ctx->device_state.WantsPageLabels) {
1529
        /* This will send the PageLabels object as a 'pdfpagelabels' setdeviceparams */
1530
493
        code = pdfi_pdfmark_object(ctx, (pdf_obj *)PageLabels, "pdfpagelabels");
1531
493
        if (code < 0)
1532
32
            goto exit;
1533
493
    }
1534
1535
72.7k
 exit:
1536
72.7k
    pdfi_countdown(PageLabels);
1537
72.7k
    return code;
1538
2.76k
}
1539
1540
/* Handle OutputIntents stuff
1541
 * (bottom of pdf_main.ps/process_trailer_attrs)
1542
 */
1543
static int pdfi_doc_OutputIntents(pdf_context *ctx)
1544
72.7k
{
1545
72.7k
    int code;
1546
72.7k
    pdf_array *OutputIntents = NULL;
1547
72.7k
    pdf_dict *intent = NULL;
1548
72.7k
    pdf_string *name = NULL;
1549
72.7k
    pdf_stream *DestOutputProfile = NULL;
1550
72.7k
    uint64_t index;
1551
1552
    /* NOTE: subtle difference in error handling -- we are checking for OutputIntents first,
1553
     * so this will just ignore UsePDFX3Profile or UseOutputIntent params without warning,
1554
     * if OutputIntents doesn't exist.  Seems fine to me.
1555
     */
1556
72.7k
    code = pdfi_dict_knownget_type(ctx, ctx->Root, "OutputIntents", PDF_ARRAY,
1557
72.7k
                                   (pdf_obj **)&OutputIntents);
1558
72.7k
    if (code <= 0) {
1559
72.3k
        goto exit;
1560
72.3k
    }
1561
1562
    /* TODO: Implement writeoutputintents  if somebody ever complains...
1563
     * See pdf_main.ps/writeoutputintents
1564
     * I am not aware of a device that supports "/OutputIntent" so
1565
     * couldn't figure out what to do for this.
1566
     */
1567
1568
    /* Handle UsePDFX3Profile and UseOutputIntent command line options */
1569
455
    if (ctx->args.UsePDFX3Profile) {
1570
        /* This is an index into the array */
1571
0
        code = pdfi_array_get_type(ctx, OutputIntents, ctx->args.PDFX3Profile_num,
1572
0
                                   PDF_DICT, (pdf_obj **)&intent);
1573
0
        if (code < 0) {
1574
0
            code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_OUTPUTINTENT_INDEX, "pdfi_doc_OutputIntents", "");
1575
0
            goto exit;
1576
0
        }
1577
455
    } else if (ctx->args.UseOutputIntent != NULL) {
1578
        /* This is a name to look up in the array */
1579
0
        for (index=0; index<pdfi_array_size(OutputIntents); index ++) {
1580
0
            code = pdfi_array_get_type(ctx, OutputIntents, index, PDF_DICT, (pdf_obj **)&intent);
1581
0
            if (code < 0) goto exit;
1582
1583
0
            code = pdfi_dict_knownget_type(ctx, intent, "OutputConditionIdentifier", PDF_STRING,
1584
0
                                           (pdf_obj **)&name);
1585
0
            if (code < 0) goto exit;
1586
0
            if (code == 0)
1587
0
                continue;
1588
1589
            /* If the ID is "Custom" then check "Info" instead */
1590
0
            if (pdfi_string_is(name, "Custom")) {
1591
0
                pdfi_countdown(name);
1592
0
                name = NULL;
1593
0
                code = pdfi_dict_knownget_type(ctx, intent, "Info", PDF_STRING, (pdf_obj **)&name);
1594
0
                if (code < 0) goto exit;
1595
0
                if (code == 0)
1596
0
                    continue;
1597
0
            }
1598
1599
            /* Check for a match */
1600
0
            if (pdfi_string_is(name, ctx->args.UseOutputIntent))
1601
0
                break;
1602
1603
0
            pdfi_countdown(intent);
1604
0
            intent = NULL;
1605
0
            pdfi_countdown(name);
1606
0
            name = NULL;
1607
0
        }
1608
0
        code = 0;
1609
455
    } else {
1610
        /* No command line arg was specified, so nothing to do */
1611
455
        code = 0;
1612
455
        goto exit;
1613
455
    }
1614
1615
    /* Now if intent is non-null, we found the selected intent dictionary */
1616
0
    if (intent == NULL)
1617
0
        goto exit;
1618
1619
    /* Load the profile, if it exists */
1620
0
    code = pdfi_dict_knownget_type(ctx, intent, "DestOutputProfile", PDF_STREAM, (pdf_obj **)&DestOutputProfile);
1621
    /* TODO: Flag an error if it doesn't exist?  Only required in some cases */
1622
0
    if (code <= 0) goto exit;
1623
1624
    /* Set the intent to the profile */
1625
0
    code = pdfi_color_setoutputintent(ctx, intent, DestOutputProfile);
1626
1627
72.7k
 exit:
1628
72.7k
    pdfi_countdown(OutputIntents);
1629
72.7k
    pdfi_countdown(intent);
1630
72.7k
    pdfi_countdown(name);
1631
72.7k
    pdfi_countdown(DestOutputProfile);
1632
72.7k
    return code;
1633
0
}
1634
1635
/* Handled an embedded files Names array for pdfwrite device */
1636
static int pdfi_doc_EmbeddedFiles_Names(pdf_context *ctx, pdf_array *names)
1637
30
{
1638
30
    int code;
1639
30
    uint64_t arraysize;
1640
30
    uint64_t index;
1641
30
    pdf_string *name = NULL;
1642
30
    pdf_dict *filespec = NULL;
1643
1644
30
    arraysize = pdfi_array_size(names);
1645
30
    if ((arraysize % 2) != 0) {
1646
1
        code = gs_note_error(gs_error_syntaxerror);
1647
1
        goto exit;
1648
1
    }
1649
1650
    /* This is supposed to be an array of
1651
     * [ (filename1) (filespec1) (filename2) (filespec2) ... ]
1652
     */
1653
64
    for (index = 0; index < arraysize; index += 2) {
1654
39
        code = pdfi_array_get_type(ctx, names, index, PDF_STRING, (pdf_obj **)&name);
1655
39
        if (code < 0) goto exit;
1656
1657
39
        code = pdfi_array_get_type(ctx, names, index+1, PDF_DICT, (pdf_obj **)&filespec);
1658
39
        if (code < 0) goto exit;
1659
1660
38
        code = pdfi_pdfmark_embed_filespec(ctx, name, filespec);
1661
38
        if (code < 0) goto exit;
1662
1663
35
        pdfi_countdown(name);
1664
35
        name = NULL;
1665
35
        pdfi_countdown(filespec);
1666
35
        filespec = NULL;
1667
35
    }
1668
1669
1670
30
 exit:
1671
30
    pdfi_countdown(name);
1672
30
    pdfi_countdown(filespec);
1673
30
    return code;
1674
29
}
1675
1676
/* Handle PageLabels for pdfwrite device */
1677
static int pdfi_doc_EmbeddedFiles(pdf_context *ctx)
1678
8.87k
{
1679
8.87k
    int code;
1680
8.87k
    pdf_dict *Names = NULL;
1681
8.87k
    pdf_dict *EmbeddedFiles = NULL;
1682
8.87k
    pdf_array *Names_array = NULL;
1683
8.87k
    pdf_array *Kids = NULL;
1684
1685
8.87k
    code = pdfi_dict_knownget_type(ctx, ctx->Root, "Collection", PDF_DICT, (pdf_obj **)&Names);
1686
8.87k
    if (code < 0) goto exit;
1687
8.87k
    if (code > 0) {
1688
0
        code = 0;
1689
0
        goto exit;
1690
0
    }
1691
1692
8.87k
    code = pdfi_dict_knownget_type(ctx, ctx->Root, "Names", PDF_DICT, (pdf_obj **)&Names);
1693
8.87k
    if (code <= 0) goto exit;
1694
1695
800
    code = pdfi_dict_knownget_type(ctx, Names, "EmbeddedFiles", PDF_DICT, (pdf_obj **)&EmbeddedFiles);
1696
800
    if (code <= 0) goto exit;
1697
1698
30
    code = pdfi_dict_knownget_type(ctx, Names, "Kids", PDF_ARRAY, (pdf_obj **)&Kids);
1699
30
    if (code < 0) goto exit;
1700
30
    if (code > 0) {
1701
        /* TODO: Need to implement */
1702
0
        errprintf(ctx->memory, "*** WARNING Kids array in EmbeddedFiles not implemented\n");
1703
0
    }
1704
1705
    /* TODO: This is a name tree.
1706
     * Can contain a Names array, or some complicated Kids.
1707
     * Just handling Names array for now
1708
     */
1709
30
    code = pdfi_dict_knownget_type(ctx, EmbeddedFiles, "Names", PDF_ARRAY, (pdf_obj **)&Names_array);
1710
30
    if (code <= 0) goto exit;
1711
1712
30
    code = pdfi_doc_EmbeddedFiles_Names(ctx, Names_array);
1713
30
    if (code <= 0) goto exit;
1714
1715
8.87k
 exit:
1716
8.87k
    pdfi_countdown(Kids);
1717
8.87k
    pdfi_countdown(Names);
1718
8.87k
    pdfi_countdown(EmbeddedFiles);
1719
8.87k
    pdfi_countdown(Names_array);
1720
8.87k
    return code;
1721
30
}
1722
1723
/* Handle some bookkeeping related to AcroForm (and annotations)
1724
 * See pdf_main.ps/process_trailer_attrs/AcroForm
1725
 *
1726
 * Mainly we preload AcroForm and NeedAppearances in the context
1727
 *
1728
 * TODO: gs code also seems to do something to link up parents in fields/annotations (ParentField)
1729
 * We are going to avoid doing that for now.
1730
 */
1731
static int pdfi_doc_AcroForm(pdf_context *ctx)
1732
72.7k
{
1733
72.7k
    int code = 0;
1734
72.7k
    pdf_dict *AcroForm = NULL;
1735
72.7k
    bool boolval = false;
1736
1737
72.7k
    code = pdfi_dict_knownget_type(ctx, ctx->Root, "AcroForm", PDF_DICT, (pdf_obj **)&AcroForm);
1738
72.7k
    if (code <= 0) goto exit;
1739
1740
6.23k
    code = pdfi_dict_get_bool(ctx, AcroForm, "NeedAppearances", &boolval);
1741
6.23k
    if (code < 0) {
1742
5.72k
        if (code == gs_error_undefined) {
1743
5.71k
            boolval = true;
1744
5.71k
            code = 0;
1745
5.71k
        }
1746
1
        else
1747
1
            goto exit;
1748
5.72k
    }
1749
6.23k
    ctx->NeedAppearances = boolval;
1750
1751
    /* Save this for efficiency later */
1752
6.23k
    ctx->AcroForm = AcroForm;
1753
6.23k
    pdfi_countup(AcroForm);
1754
1755
    /* TODO: Link up ParentField (but hopefully we can avoid doing this hacky mess).
1756
     * Also: Something to do with Bug692447.pdf?
1757
     */
1758
1759
1760
72.7k
 exit:
1761
72.7k
    pdfi_countdown(AcroForm);
1762
72.7k
    return code;
1763
6.23k
}
1764
1765
1766
static int pdfi_doc_view(pdf_context *ctx)
1767
8.87k
{
1768
8.87k
    int code = 0;
1769
8.87k
    pdf_dict *tempdict = NULL;
1770
8.87k
    pdf_name *Mode = NULL, *Layout = NULL;
1771
8.87k
    pdf_obj *ActionDest = NULL;
1772
1773
8.87k
    code = pdfi_dict_knownget_type(ctx, ctx->Root, "PageMode", PDF_NAME, (pdf_obj **)&Mode);
1774
8.87k
    if (code < 0)
1775
0
        return code;
1776
1777
8.87k
    if (code != 0) {
1778
620
        code = pdfi_dict_alloc(ctx, 1, &tempdict);
1779
620
        if (code < 0)
1780
0
            goto exit;
1781
1782
620
        pdfi_countup(tempdict);
1783
1784
620
        code = pdfi_dict_put(ctx, tempdict, "PageMode", (pdf_obj *)Mode);
1785
620
        if (code < 0)
1786
0
            goto exit;
1787
1788
620
        code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "DOCVIEW");
1789
620
        if (code < 0)
1790
0
            goto exit;
1791
620
        pdfi_countdown(tempdict);
1792
620
        tempdict = NULL;
1793
620
    }
1794
1795
8.87k
    code = pdfi_dict_knownget_type(ctx, ctx->Root, "PageLayout", PDF_NAME, (pdf_obj **)&Layout);
1796
8.87k
    if (code < 0)
1797
0
        goto exit;
1798
1799
8.87k
    if (code != 0) {
1800
577
        code = pdfi_dict_alloc(ctx, 1, &tempdict);
1801
577
        if (code < 0)
1802
0
            goto exit;
1803
1804
577
        pdfi_countup(tempdict);
1805
1806
577
        code = pdfi_dict_put(ctx, tempdict, "PageLayout", (pdf_obj *)Layout);
1807
577
        if (code < 0)
1808
0
            goto exit;
1809
1810
577
        code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "DOCVIEW");
1811
577
        if (code < 0)
1812
0
            goto exit;
1813
577
        pdfi_countdown(tempdict);
1814
577
        tempdict = NULL;
1815
577
    }
1816
1817
8.87k
    code = pdfi_dict_knownget(ctx, ctx->Root, "OpenAction", &ActionDest);
1818
8.87k
    if (code < 0)
1819
29
        goto exit;
1820
1821
8.84k
    if (code != 0) {
1822
528
        if (pdfi_type_of(ActionDest) == PDF_DICT) {
1823
            /* Dictionary means this is an action */
1824
22
            code = pdfi_dict_alloc(ctx, 1, &tempdict);
1825
22
            if (code < 0)
1826
0
                goto exit;
1827
1828
22
            pdfi_countup(tempdict);
1829
1830
22
            code = pdfi_dict_put(ctx, tempdict, "A", (pdf_obj *)ActionDest);
1831
22
            if (code < 0)
1832
0
                goto exit;
1833
1834
22
            code = pdfi_pdfmark_modA(ctx, tempdict);
1835
22
            if (code < 0) goto exit;
1836
1837
21
            code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "DOCVIEW");
1838
506
        } else {
1839
506
            if (pdfi_type_of(ActionDest) == PDF_ARRAY) {
1840
                /* Array means this is a destination */
1841
505
                code = pdfi_dict_alloc(ctx, 1, &tempdict);
1842
505
                if (code < 0)
1843
0
                    goto exit;
1844
1845
505
                pdfi_countup(tempdict);
1846
1847
505
                code = pdfi_dict_put(ctx, tempdict, "Dest", (pdf_obj *)ActionDest);
1848
505
                if (code < 0)
1849
0
                    goto exit;
1850
505
                code = pdfi_pdfmark_modDest(ctx, tempdict);
1851
505
                if (code < 0)
1852
55
                    goto exit;
1853
450
                code = pdfi_pdfmark_from_dict(ctx, tempdict, NULL, "DOCVIEW");
1854
450
                if (code < 0)
1855
0
                    goto exit;
1856
450
            } else {
1857
1
                code = gs_note_error(gs_error_typecheck);
1858
1
                goto exit;
1859
1
            }
1860
506
        }
1861
528
    }
1862
1863
8.87k
exit:
1864
8.87k
    pdfi_countdown(ActionDest);
1865
8.87k
    pdfi_countdown(Layout);
1866
8.87k
    pdfi_countdown(Mode);
1867
8.87k
    pdfi_countdown(tempdict);
1868
8.87k
    return code;
1869
8.84k
}
1870
1871
1872
/* See pdf_main.ps/process_trailer_attrs()
1873
 * Some of this stuff is about pdfmarks, and some of it is just handling
1874
 * random things in the trailer.
1875
 */
1876
int pdfi_doc_trailer(pdf_context *ctx)
1877
72.7k
{
1878
72.7k
    int code = 0;
1879
1880
    /* Can't do this stuff with no Trailer */
1881
72.7k
    if (!ctx->Trailer) {
1882
38.9k
        if ((code = pdfi_set_warning_stop(ctx, gs_note_error(gs_error_undefined), NULL, W_PDF_BAD_TRAILER, "pdfi_doc_trailer", NULL)) < 0)
1883
0
            goto exit;
1884
38.9k
    }
1885
1886
72.7k
    if (ctx->device_state.writepdfmarks) {
1887
        /* Handle Outlines */
1888
8.87k
        code = pdfi_doc_Outlines(ctx);
1889
8.87k
        if (code < 0) {
1890
79
            if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_OUTLINES, "pdfi_doc_trailer", NULL)) < 0)
1891
0
                goto exit;
1892
79
        }
1893
1894
        /* Handle Docview pdfmark stuff */
1895
8.87k
        if (ctx->args.preservedocview) {
1896
8.87k
            code = pdfi_doc_view(ctx);
1897
8.87k
            if (code < 0) {
1898
86
                if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_VIEW, "pdfi_doc_view", NULL)) < 0)
1899
0
                    goto exit;
1900
86
            }
1901
8.87k
        }
1902
1903
        /* Handle Info */
1904
8.87k
        code = pdfi_doc_Info(ctx);
1905
8.87k
        if (code < 0) {
1906
            /* pdfwrite will set a Fatal error when processing the DOCINFO if it has been
1907
             * told to create a PDF/A. the PDFA compatibility is 2, and the file info
1908
             * cannot be handled. In that case, abort immediately.
1909
             */
1910
5.06k
            if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_INFO, "pdfi_doc_trailer", NULL)) < 0)
1911
0
                goto exit;
1912
5.06k
        }
1913
1914
        /* Handle EmbeddedFiles */
1915
        /* TODO: add a configuration option to embed or omit */
1916
8.87k
        if (ctx->args.preserveembeddedfiles) {
1917
8.87k
            code = pdfi_doc_EmbeddedFiles(ctx);
1918
8.87k
            if (code < 0) {
1919
81
                if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_EMBEDDEDFILES, "pdfi_doc_trailer", NULL)) < 0)
1920
0
                    goto exit;
1921
81
            }
1922
8.87k
        }
1923
8.87k
    }
1924
1925
    /* Handle OCProperties */
1926
    /* NOTE: Apparently already handled by pdfi_read_OptionalRoot() */
1927
1928
    /* Handle AcroForm -- this is some bookkeeping once per doc, not rendering them yet */
1929
72.7k
    code = pdfi_doc_AcroForm(ctx);
1930
72.7k
    if (code < 0) {
1931
849
        if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_ACROFORM, "pdfi_doc_trailer", NULL)) < 0)
1932
0
            goto exit;
1933
849
    }
1934
1935
    /* Handle OutputIntent ICC Profile */
1936
72.7k
    code = pdfi_doc_OutputIntents(ctx);
1937
72.7k
    if (code < 0) {
1938
0
        if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_OUTPUTINTENTS, "pdfi_doc_trailer", NULL)) < 0)
1939
0
            goto exit;
1940
0
    }
1941
1942
    /* Handle PageLabels */
1943
72.7k
    code = pdfi_doc_PageLabels(ctx);
1944
72.7k
    if (code < 0) {
1945
120
        if ((code = pdfi_set_warning_stop(ctx, code, NULL, W_PDF_BAD_PAGELABELS, "pdfi_doc_trailer", NULL)) < 0)
1946
0
            goto exit;
1947
120
    }
1948
1949
72.7k
 exit:
1950
72.7k
    return code;
1951
72.7k
}