Coverage Report

Created: 2022-10-31 07:00

/src/ghostpdl/pdf/pdf_check.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2019-2022 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.,  1305 Grant Avenue - Suite 200, Novato,
13
   CA 94945, U.S.A., +1(415)492-9861, for further information.
14
*/
15
16
/* Checks for transparency and spots for the PDF interpreter */
17
18
#include "pdf_int.h"
19
#include "pdf_stack.h"
20
#include "pdf_page.h"
21
#include "pdf_file.h"
22
#include "pdf_dict.h"
23
#include "pdf_array.h"
24
#include "pdf_loop_detect.h"
25
#include "pdf_colour.h"
26
#include "pdf_trans.h"
27
#include "pdf_font_types.h"
28
#include "pdf_gstate.h"
29
#include "pdf_misc.h"
30
#include "pdf_check.h"
31
#include "pdf_device.h"
32
#include "gsdevice.h"       /* For gs_setdevice_no_erase */
33
#include "gspaint.h"        /* For gs_erasepage() */
34
35
/* For performance and resource reasons we do not want to install the transparency blending
36
 * compositor unless we need it. Similarly, if a device handles spot colours it can minimise
37
 * memory usage if it knows ahead of time how many spot colours there will be.
38
 *
39
 * The PDF interpreter written in PostScript performed these as two separate tasks, on opening
40
 * a PDF file it would count spot colour usage and then for each page it would chek if the page
41
 * used any transparency. The code below is used to check for both transparency and spot colours.
42
 * If the int pointer to num_spots is NULL then we aren't interested in spot colours (not supported
43
 * by the device), if the int pointer to transparent is NULL then we aren't interested in transparency
44
 * (-dNOTRANSPARENCY is set).
45
 *
46
 * Currently the code is run when we open the PDF file, the existence of transparency on any given
47
 * page is recorded in a bit array for later use. If it turns out that checking every page when we
48
 * open the file is a performance burden then we could do it on a per-page basis instead. NB if
49
 * the device supports spot colours then we do need to scan the file before starting any page.
50
 *
51
 * The technique is fairly straight-forward, we start with each page, and open its Resources
52
 * dictionary, we then check by type each possible resource. Some resources (eg Pattern, XObject)
53
 * can themselves contain Resources, in which case we recursively check that dictionary. Note; not
54
 * all Resource types need to be checked for both transparency and spot colours, some types can
55
 * only contain one or the other.
56
 *
57
 * Routines with the name pdfi_check_xxx_dict are intended to check a Resource dictionary entry, this
58
 * will be a dictioanry of names and values, where the values are objects of the given Resource type.
59
 *
60
 */
61
62
/* Structure for keeping track of resources as they are being checked */
63
/* CheckedResources is a bit of a hack, for the file Bug697655.pdf. That file has many (unused)
64
 * XObjects which use Resources and the Resources use other XObjects and so on nested
65
 * very deeply. This takes *hours* to check. Ghostscript gets around this because it
66
 * stores all the objects (which we don't want to do because its wasteful) and checking
67
 * to see if its already tested a given resource for spots/transparency.
68
 * This is a temporary allocation, big enough to hold all the objects in the file (1 per bit)
69
 * each time we have fully checked a resource we add it here, when checking a resource we
70
 * first check this list to see if its already been checked, in which case we can skip
71
 * it. When done we release the memory.
72
 */
73
typedef struct {
74
    bool transparent;
75
    bool has_overprint; /* Does it have OP or op in an ExtGState? */
76
    pdf_dict *spot_dict;
77
    pdf_array *font_array;
78
    uint32_t size;
79
    byte *CheckedResources;
80
} pdfi_check_tracker_t;
81
82
static int pdfi_check_Resources(pdf_context *ctx, pdf_dict *Resources_dict, pdf_dict *page_dict, pdfi_check_tracker_t *tracker);
83
84
static inline bool resource_is_checked(pdfi_check_tracker_t *tracker, pdf_obj *o)
85
2.35M
{
86
2.35M
    uint32_t byte_offset;
87
2.35M
    byte bit_offset;
88
2.35M
    int object_num;
89
90
2.35M
    if(tracker->CheckedResources == NULL)
91
28.0k
        return 0;
92
93
    /* objects with object number 0 are directly defined, we can't
94
     * store those so just return immediately
95
     */
96
2.32M
    object_num = pdf_object_num(o);
97
2.32M
    if (object_num > 0 && (object_num >> 3) < tracker->size) {
98
        /* CheckedResources is a byte array, each byte represents
99
         * 8 objects. So the object number / 8 is the byte offset
100
         * into the array, and then object number % 8 is the bit
101
         * within that byte that we want.
102
         */
103
1.65M
        bit_offset = 0x01 << (object_num % 8);
104
1.65M
        byte_offset = object_num >> 3;
105
106
        /* If its already set, then return that. */
107
1.65M
        if (tracker->CheckedResources[byte_offset] & bit_offset)
108
124k
            return true;
109
1.53M
        else
110
            /* Otherwise set it for futre reference */
111
1.53M
            tracker->CheckedResources[byte_offset] |= bit_offset;
112
1.65M
    }
113
2.20M
    return false;
114
2.32M
}
115
116
117
static int
118
pdfi_check_free_tracker(pdf_context *ctx, pdfi_check_tracker_t *tracker)
119
86.9k
{
120
86.9k
    gs_free_object(ctx->memory, tracker->CheckedResources, "pdfi_check_free_tracker(flags)");
121
86.9k
    pdfi_countdown(tracker->spot_dict);
122
86.9k
    pdfi_countdown(tracker->font_array);
123
86.9k
    memset(tracker, 0, sizeof(*tracker));
124
86.9k
    return 0;
125
86.9k
}
126
127
static int
128
pdfi_check_init_tracker(pdf_context *ctx, pdfi_check_tracker_t *tracker, pdf_array **fonts_array, pdf_array **spot_array)
129
86.9k
{
130
86.9k
    int code = 0;
131
132
86.9k
    memset(tracker, 0, sizeof(*tracker));
133
134
86.9k
    tracker->size = (ctx->xref_table->xref_size + 7) / 8;
135
86.9k
    tracker->CheckedResources = gs_alloc_bytes(ctx->memory, tracker->size,
136
86.9k
                                            "pdfi_check_init_tracker(flags)");
137
86.9k
    if (tracker->CheckedResources == NULL)
138
0
        return_error(gs_error_VMerror);
139
140
86.9k
    memset(tracker->CheckedResources, 0x00, tracker->size);
141
142
86.9k
    if (ctx->device_state.spot_capable ||
143
86.9k
        (ctx->pgs->device->icc_struct->overprint_control) == gs_overprint_control_simulate ||
144
86.9k
        spot_array != NULL)
145
9.19k
    {
146
9.19k
        code = pdfi_dict_alloc(ctx, 32, &tracker->spot_dict);
147
9.19k
        if (code < 0)
148
0
            goto cleanup;
149
9.19k
        pdfi_countup(tracker->spot_dict);
150
9.19k
    }
151
152
86.9k
    if (fonts_array != NULL) {
153
0
        code = pdfi_array_alloc(ctx, 0, &tracker->font_array);
154
0
        if (code < 0)
155
0
            goto cleanup;
156
0
        pdfi_countup(tracker->font_array);
157
0
    }
158
159
86.9k
    return 0;
160
161
0
cleanup:
162
0
    pdfi_check_free_tracker(ctx, tracker);
163
0
    return code;
164
86.9k
}
165
166
/*
167
 * Check the Resources dictionary ColorSpace entry. pdfi_check_ColorSpace_for_spots is defined
168
 * in pdf_colour.c
169
 */
170
static int pdfi_check_ColorSpace_dict(pdf_context *ctx, pdf_dict *cspace_dict,
171
                                      pdf_dict *page_dict, pdfi_check_tracker_t *tracker)
172
3.32k
{
173
3.32k
    int code;
174
3.32k
    uint64_t i, index;
175
3.32k
    pdf_obj *Key = NULL, *Value = NULL;
176
177
3.32k
    if (resource_is_checked(tracker, (pdf_obj *)cspace_dict))
178
4
        return 0;
179
180
3.32k
    if (pdfi_type_of(cspace_dict) != PDF_DICT)
181
0
        return_error(gs_error_typecheck);
182
183
3.32k
    if (pdfi_dict_entries(cspace_dict) > 0) {
184
3.31k
        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the ColorSpace dictionary loop */
185
3.31k
        if (code < 0)
186
0
            return code;
187
188
3.31k
        code = pdfi_dict_first(ctx, cspace_dict, &Key, &Value, &index);
189
3.31k
        if (code < 0)
190
267
            goto error1;
191
192
3.04k
        i = 1;
193
4.78k
        do {
194
4.78k
            code = pdfi_check_ColorSpace_for_spots(ctx, Value, cspace_dict, page_dict, tracker->spot_dict);
195
4.78k
            if (code < 0)
196
34
                goto error2;
197
198
4.74k
            pdfi_countdown(Key);
199
4.74k
            Key = NULL;
200
4.74k
            pdfi_countdown(Value);
201
4.74k
            Value = NULL;
202
203
4.74k
            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */
204
205
4.74k
            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Shading dictionary loop */
206
4.74k
            if (code < 0)
207
0
                goto error1;
208
209
4.77k
            do {
210
4.77k
                if (i++ >= pdfi_dict_entries(cspace_dict)) {
211
3.01k
                    code = 0;
212
3.01k
                    goto transparency_exit;
213
3.01k
                }
214
215
1.75k
                code = pdfi_dict_next(ctx, cspace_dict, &Key, &Value, &index);
216
1.75k
                if (code == 0 && pdfi_type_of(Value) == PDF_ARRAY)
217
1.73k
                    break;
218
26
                pdfi_countdown(Key);
219
26
                Key = NULL;
220
26
                pdfi_countdown(Value);
221
26
                Value = NULL;
222
26
            } while(1);
223
4.74k
        }while (1);
224
3.04k
    }
225
6
    return 0;
226
227
3.01k
transparency_exit:
228
3.04k
error2:
229
3.04k
    pdfi_countdown(Key);
230
3.04k
    pdfi_countdown(Value);
231
232
3.31k
error1:
233
3.31k
    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
234
3.31k
    return code;
235
3.04k
}
236
237
/*
238
 * Process an individual Shading dictionary to see if it contains a ColorSpace with a spot colour
239
 */
240
static int pdfi_check_Shading(pdf_context *ctx, pdf_obj *shading,
241
                              pdf_dict *page_dict, pdfi_check_tracker_t *tracker)
242
6.48k
{
243
6.48k
    int code;
244
6.48k
    pdf_obj *o = NULL;
245
6.48k
    pdf_dict *shading_dict = NULL;
246
247
6.48k
    if (resource_is_checked(tracker, shading))
248
180
        return 0;
249
250
6.30k
    code = pdfi_dict_from_obj(ctx, shading, &shading_dict);
251
6.30k
    if (code < 0)
252
10
        return code;
253
254
6.29k
    if (pdfi_type_of(shading_dict) != PDF_DICT)
255
0
        return_error(gs_error_typecheck);
256
257
6.29k
    code = pdfi_dict_knownget(ctx, shading_dict, "ColorSpace", (pdf_obj **)&o);
258
6.29k
    if (code > 0) {
259
5.82k
        code = pdfi_check_ColorSpace_for_spots(ctx, o, shading_dict, page_dict, tracker->spot_dict);
260
5.82k
        pdfi_countdown(o);
261
5.82k
        return code;
262
5.82k
    }
263
478
    return 0;
264
6.29k
}
265
266
/*
267
 * Check the Resources dictionary Shading entry.
268
 */
269
static int pdfi_check_Shading_dict(pdf_context *ctx, pdf_dict *shading_dict,
270
                                   pdf_dict *page_dict, pdfi_check_tracker_t *tracker)
271
482
{
272
482
    int code;
273
482
    uint64_t i, index;
274
482
    pdf_obj *Key = NULL, *Value = NULL;
275
276
482
    if (resource_is_checked(tracker, (pdf_obj *)shading_dict))
277
0
        return 0;
278
279
482
    if (pdfi_type_of(shading_dict) != PDF_DICT)
280
0
        return_error(gs_error_typecheck);
281
282
482
    if (pdfi_dict_entries(shading_dict) > 0) {
283
482
        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the Shading dictionary loop */
284
482
        if (code < 0)
285
0
            return code;
286
287
482
        code = pdfi_dict_first(ctx, shading_dict, &Key, &Value, &index);
288
482
        if (code < 0 || !(pdfi_type_of(Value) == PDF_DICT || pdfi_type_of(Value) == PDF_STREAM))
289
114
            goto error1;
290
291
368
        i = 1;
292
6.25k
        do {
293
6.25k
            code = pdfi_check_Shading(ctx, Value, page_dict, tracker);
294
6.25k
            if (code < 0)
295
4
                goto error2;
296
297
6.25k
            pdfi_countdown(Key);
298
6.25k
            Key = NULL;
299
6.25k
            pdfi_countdown(Value);
300
6.25k
            Value = NULL;
301
302
6.25k
            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */
303
304
6.25k
            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Shading dictionary loop */
305
6.25k
            if (code < 0)
306
0
                goto error1;
307
308
12.0k
            do {
309
12.0k
                if (i++ >= pdfi_dict_entries(shading_dict)) {
310
364
                    code = 0;
311
364
                    goto transparency_exit;
312
364
                }
313
314
11.6k
                code = pdfi_dict_next(ctx, shading_dict, &Key, &Value, &index);
315
11.6k
                if (code == 0 && pdfi_type_of(Value) == PDF_DICT)
316
5.89k
                    break;
317
5.78k
                pdfi_countdown(Key);
318
5.78k
                Key = NULL;
319
5.78k
                pdfi_countdown(Value);
320
5.78k
                Value = NULL;
321
5.78k
            } while(1);
322
6.25k
        }while (1);
323
368
    }
324
0
    return 0;
325
326
364
transparency_exit:
327
368
error2:
328
368
    pdfi_countdown(Key);
329
368
    pdfi_countdown(Value);
330
331
482
error1:
332
482
    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
333
482
    return code;
334
368
}
335
336
/*
337
 * This routine checks an XObject to see if it contains any spot
338
 * colour definitions, or transparency usage.
339
 */
340
static int pdfi_check_XObject(pdf_context *ctx, pdf_dict *xobject, pdf_dict *page_dict,
341
                              pdfi_check_tracker_t *tracker)
342
185k
{
343
185k
    int code = 0;
344
185k
    pdf_name *n = NULL;
345
185k
    bool known = false;
346
185k
    double f;
347
348
185k
    if (resource_is_checked(tracker, (pdf_obj *)xobject))
349
37.9k
        return 0;
350
351
147k
    if (pdfi_type_of(xobject) != PDF_DICT)
352
217
        return_error(gs_error_typecheck);
353
354
146k
    code = pdfi_dict_get_type(ctx, xobject, "Subtype", PDF_NAME, (pdf_obj **)&n);
355
146k
    if (code >= 0) {
356
146k
        if (pdfi_name_is((const pdf_name *)n, "Image")) {
357
46.5k
            pdf_obj *CS = NULL;
358
359
46.5k
            pdfi_countdown(n);
360
46.5k
            n = NULL;
361
46.5k
            code = pdfi_dict_known(ctx, xobject, "SMask", &known);
362
46.5k
            if (code >= 0) {
363
46.5k
                if (known == true) {
364
16.4k
                    tracker->transparent = true;
365
16.4k
                    if (tracker->spot_dict == NULL)
366
13.4k
                        goto transparency_exit;
367
16.4k
                }
368
33.0k
                code = pdfi_dict_knownget_number(ctx, xobject, "SMaskInData", &f);
369
33.0k
                if (code > 0) {
370
0
                    code = 0;
371
0
                    if (f != 0.0)
372
0
                        tracker->transparent = true;
373
0
                    if (tracker->spot_dict == NULL)
374
0
                        goto transparency_exit;
375
0
                }
376
                /* Check the image dictionary for a ColorSpace entry, if we are checking spot names */
377
33.0k
                if (tracker->spot_dict) {
378
7.56k
                    code = pdfi_dict_knownget(ctx, xobject, "ColorSpace", (pdf_obj **)&CS);
379
7.56k
                    if (code > 0) {
380
                        /* We don't care if there's an error here, it'll be picked up if we use the ColorSpace later */
381
6.84k
                        (void)pdfi_check_ColorSpace_for_spots(ctx, CS, xobject, page_dict, tracker->spot_dict);
382
6.84k
                        pdfi_countdown(CS);
383
6.84k
                    }
384
7.56k
                }
385
33.0k
            }
386
100k
        } else {
387
100k
            if (pdfi_name_is((const pdf_name *)n, "Form")) {
388
99.9k
                pdf_dict *group_dict = NULL, *resource_dict = NULL;
389
99.9k
                pdf_obj *CS = NULL;
390
391
99.9k
                pdfi_countdown(n);
392
99.9k
                code = pdfi_dict_knownget_type(ctx, xobject, "Group", PDF_DICT, (pdf_obj **)&group_dict);
393
99.9k
                if (code > 0) {
394
3.73k
                    tracker->transparent = true;
395
3.73k
                    if (tracker->spot_dict == NULL) {
396
2.91k
                        pdfi_countdown(group_dict);
397
2.91k
                        goto transparency_exit;
398
2.91k
                    }
399
400
                    /* Start a new loop detector group to avoid this being detected in the Resources check below */
401
816
                    code = pdfi_loop_detector_mark(ctx); /* Mark the start of the XObject dictionary loop */
402
816
                    if (code == 0) {
403
816
                        code = pdfi_dict_knownget(ctx, group_dict, "CS", &CS);
404
816
                        if (code > 0)
405
                            /* We don't care if there's an error here, it'll be picked up if we use the ColorSpace later */
406
656
                            (void)pdfi_check_ColorSpace_for_spots(ctx, CS, group_dict, page_dict, tracker->spot_dict);
407
816
                        (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the XObject dictionary loop */
408
816
                    }
409
816
                    pdfi_countdown(group_dict);
410
816
                    pdfi_countdown(CS);
411
816
                }
412
413
97.0k
                code = pdfi_dict_knownget_type(ctx, xobject, "Resources", PDF_DICT, (pdf_obj **)&resource_dict);
414
97.0k
                if (code > 0) {
415
96.6k
                    code = pdfi_check_Resources(ctx, resource_dict, page_dict, tracker);
416
96.6k
                    pdfi_countdown(resource_dict);
417
96.6k
                    if (code < 0)
418
0
                        goto transparency_exit;
419
96.6k
                }
420
97.0k
            } else
421
145
                pdfi_countdown(n);
422
100k
        }
423
146k
    }
424
425
130k
    return 0;
426
427
16.3k
transparency_exit:
428
16.3k
    return code;
429
146k
}
430
431
/*
432
 * Check the Resources dictionary XObject entry.
433
 */
434
static int pdfi_check_XObject_dict(pdf_context *ctx, pdf_dict *xobject_dict, pdf_dict *page_dict,
435
                                   pdfi_check_tracker_t *tracker)
436
150k
{
437
150k
    int code;
438
150k
    uint64_t i, index;
439
150k
    pdf_obj *Key = NULL, *Value = NULL;
440
150k
    pdf_dict *Value_dict = NULL;
441
442
150k
    if (resource_is_checked(tracker, (pdf_obj *)xobject_dict))
443
0
        return 0;
444
445
150k
    if (pdfi_type_of(xobject_dict) != PDF_DICT)
446
0
        return_error(gs_error_typecheck);
447
448
150k
    if (pdfi_dict_entries(xobject_dict) > 0) {
449
149k
        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the XObject dictionary loop */
450
149k
        if (code < 0)
451
0
            return code;
452
453
149k
        code = pdfi_dict_first(ctx, xobject_dict, &Key, &Value, &index);
454
149k
        if (code < 0)
455
11.6k
            goto error_exit;
456
138k
        if (pdfi_type_of(Value) != PDF_STREAM)
457
1.56k
            goto error_exit;
458
459
136k
        i = 1;
460
184k
        do {
461
184k
            code = pdfi_dict_from_obj(ctx, Value, &Value_dict);
462
184k
            if (code < 0)
463
0
                goto error_exit;
464
465
184k
            code = pdfi_check_XObject(ctx, Value_dict, page_dict, tracker);
466
184k
            if (code < 0)
467
0
                goto error_exit;
468
469
184k
            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the XObject dictionary loop */
470
471
184k
            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the XObject dictionary loop */
472
184k
            if (code < 0)
473
0
                goto error_exit;
474
475
184k
            pdfi_countdown(Key);
476
184k
            Key = NULL;
477
184k
            pdfi_countdown(Value);
478
184k
            Value = NULL;
479
184k
            Value_dict = NULL;
480
481
189k
            do {
482
189k
                if (i++ >= pdfi_dict_entries(xobject_dict)) {
483
136k
                    code = 0;
484
136k
                    goto transparency_exit;
485
136k
                }
486
487
53.0k
                code = pdfi_dict_next(ctx, xobject_dict, &Key, &Value, &index);
488
53.0k
                if (code == 0 && pdfi_type_of(Value) == PDF_STREAM)
489
48.4k
                    break;
490
4.64k
                pdfi_countdown(Key);
491
4.64k
                Key = NULL;
492
4.64k
                pdfi_countdown(Value);
493
4.64k
                Value = NULL;
494
4.64k
            } while(1);
495
184k
        }while(1);
496
136k
    }
497
318
    return 0;
498
499
136k
transparency_exit:
500
149k
error_exit:
501
149k
    pdfi_countdown(Key);
502
149k
    pdfi_countdown(Value);
503
149k
    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
504
149k
    return code;
505
136k
}
506
507
/*
508
 * This routine checks an ExtGState dictionary to see if it contains transparency or OP
509
 */
510
static int pdfi_check_ExtGState(pdf_context *ctx, pdf_dict *extgstate_dict, pdf_dict *page_dict,
511
                                pdfi_check_tracker_t *tracker)
512
168k
{
513
168k
    int code;
514
168k
    pdf_obj *o = NULL;
515
168k
    double f;
516
168k
    bool overprint;
517
518
168k
    if (resource_is_checked(tracker, (pdf_obj *)extgstate_dict))
519
76.1k
        return 0;
520
521
92.5k
    if (pdfi_type_of(extgstate_dict) != PDF_DICT)
522
1.07k
        return_error(gs_error_typecheck);
523
524
91.4k
    if (pdfi_dict_entries(extgstate_dict) > 0) {
525
        /* See if /OP or /op is true */
526
91.4k
        code = pdfi_dict_get_bool(ctx, extgstate_dict, "OP", &overprint);
527
91.4k
        if (code == 0 && overprint)
528
4.95k
            tracker->has_overprint = true;
529
91.4k
        code = pdfi_dict_get_bool(ctx, extgstate_dict, "op", &overprint);
530
91.4k
        if (code == 0 && overprint)
531
5.12k
            tracker->has_overprint = true;
532
533
        /* Check SMask */
534
91.4k
        code = pdfi_dict_knownget(ctx, extgstate_dict, "SMask", &o);
535
91.4k
        if (code > 0) {
536
12.1k
            switch (pdfi_type_of(o)) {
537
11.4k
                case PDF_NAME:
538
11.4k
                    if (!pdfi_name_is((pdf_name *)o, "None")) {
539
6
                        pdfi_countdown(o);
540
6
                        tracker->transparent = true;
541
6
                        return 0;
542
6
                    }
543
11.4k
                    break;
544
11.4k
                case PDF_DICT:
545
712
                {
546
712
                    pdf_obj *G = NULL;
547
548
712
                    tracker->transparent = true;
549
550
712
                    if (tracker->spot_dict != NULL) {
551
                        /* Check if the SMask has a /G (Group) */
552
263
                        code = pdfi_dict_knownget(ctx, (pdf_dict *)o, "G", &G);
553
263
                        if (code > 0) {
554
217
                            code = pdfi_check_XObject(ctx, (pdf_dict *)G, page_dict,
555
217
                                                      tracker);
556
217
                            pdfi_countdown(G);
557
217
                        }
558
263
                    }
559
712
                    pdfi_countdown(o);
560
712
                    return code;
561
11.4k
                }
562
0
                default:
563
0
                    break;
564
12.1k
            }
565
12.1k
        }
566
90.7k
        pdfi_countdown(o);
567
90.7k
        o = NULL;
568
569
90.7k
        code = pdfi_dict_knownget_type(ctx, extgstate_dict, "BM", PDF_NAME, &o);
570
90.7k
        if (code > 0) {
571
33.2k
            if (!pdfi_name_is((pdf_name *)o, "Normal")) {
572
1.38k
                if (!pdfi_name_is((pdf_name *)o, "Compatible")) {
573
1.37k
                    pdfi_countdown(o);
574
1.37k
                    tracker->transparent = true;
575
1.37k
                    return 0;
576
1.37k
                }
577
1.38k
            }
578
33.2k
        }
579
89.3k
        pdfi_countdown(o);
580
89.3k
        o = NULL;
581
582
89.3k
        code = pdfi_dict_knownget_number(ctx, extgstate_dict, "CA", &f);
583
89.3k
        if (code > 0) {
584
31.4k
            if (f != 1.0) {
585
6.57k
                tracker->transparent = true;
586
6.57k
                return 0;
587
6.57k
            }
588
31.4k
        }
589
590
82.7k
        code = pdfi_dict_knownget_number(ctx, extgstate_dict, "ca", &f);
591
82.7k
        if (code > 0) {
592
25.4k
            if (f != 1.0) {
593
164
                tracker->transparent = true;
594
164
                return 0;
595
164
            }
596
25.4k
        }
597
598
82.7k
    }
599
82.6k
    return 0;
600
91.4k
}
601
602
/*
603
 * Check the Resources dictionary ExtGState entry.
604
 */
605
static int pdfi_check_ExtGState_dict(pdf_context *ctx, pdf_dict *extgstate_dict, pdf_dict *page_dict,
606
                                     pdfi_check_tracker_t *tracker)
607
135k
{
608
135k
    int code;
609
135k
    uint64_t i, index;
610
135k
    pdf_obj *Key = NULL, *Value = NULL;
611
612
135k
    if (resource_is_checked(tracker, (pdf_obj *)extgstate_dict))
613
4
        return 0;
614
615
135k
    if (pdfi_type_of(extgstate_dict) != PDF_DICT)
616
0
        return_error(gs_error_typecheck);
617
618
135k
    if (pdfi_dict_entries(extgstate_dict) > 0) {
619
135k
        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the ColorSpace dictionary loop */
620
135k
        if (code < 0)
621
0
            return code;
622
623
135k
        code = pdfi_dict_first(ctx, extgstate_dict, &Key, &Value, &index);
624
135k
        if (code < 0)
625
4.81k
            goto error1;
626
627
130k
        i = 1;
628
168k
        do {
629
630
168k
            (void)pdfi_check_ExtGState(ctx, (pdf_dict *)Value, page_dict, tracker);
631
168k
            if (tracker->transparent == true && tracker->spot_dict == NULL)
632
2.91k
                goto transparency_exit;
633
634
165k
            pdfi_countdown(Key);
635
165k
            Key = NULL;
636
165k
            pdfi_countdown(Value);
637
165k
            Value = NULL;
638
639
165k
            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the ExtGState dictionary loop */
640
641
165k
            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the ExtGState dictionary loop */
642
165k
            if (code < 0)
643
0
                goto error1;
644
645
173k
            do {
646
173k
                if (i++ >= pdfi_dict_entries(extgstate_dict)) {
647
127k
                    code = 0;
648
127k
                    goto transparency_exit;
649
127k
                }
650
651
45.8k
                code = pdfi_dict_next(ctx, extgstate_dict, &Key, &Value, &index);
652
45.8k
                if (code == 0 && pdfi_type_of(Value) == PDF_DICT)
653
37.9k
                    break;
654
7.84k
                pdfi_countdown(Key);
655
7.84k
                Key = NULL;
656
7.84k
                pdfi_countdown(Value);
657
7.84k
                Value = NULL;
658
7.84k
            } while(1);
659
165k
        }while (1);
660
130k
    }
661
254
    return 0;
662
663
130k
transparency_exit:
664
130k
    pdfi_countdown(Key);
665
130k
    pdfi_countdown(Value);
666
667
135k
error1:
668
135k
    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
669
135k
    return code;
670
130k
}
671
672
/*
673
 * This routine checks a Pattern dictionary to see if it contains any spot
674
 * colour definitions, or transparency usage.
675
 */
676
static int pdfi_check_Pattern(pdf_context *ctx, pdf_dict *pattern, pdf_dict *page_dict,
677
                              pdfi_check_tracker_t *tracker)
678
23.4k
{
679
23.4k
    int code = 0;
680
23.4k
    pdf_obj *o = NULL;
681
682
23.4k
    if (resource_is_checked(tracker, (pdf_obj *)pattern))
683
230
        return 0;
684
685
23.2k
    if (pdfi_type_of(pattern) != PDF_DICT)
686
0
        return_error(gs_error_typecheck);
687
688
23.2k
    if (tracker->spot_dict != NULL) {
689
4.71k
        code = pdfi_dict_knownget(ctx, pattern, "Shading", &o);
690
4.71k
        if (code > 0)
691
231
            (void)pdfi_check_Shading(ctx, o, page_dict, tracker);
692
4.71k
        pdfi_countdown(o);
693
4.71k
        o = NULL;
694
4.71k
    }
695
696
23.2k
    code = pdfi_dict_knownget_type(ctx, pattern, "Resources", PDF_DICT, &o);
697
23.2k
    if (code > 0)
698
21.6k
        (void)pdfi_check_Resources(ctx, (pdf_dict *)o, page_dict, tracker);
699
23.2k
    pdfi_countdown(o);
700
23.2k
    o = NULL;
701
23.2k
    if (tracker->transparent == true && tracker->spot_dict == NULL)
702
15.6k
        goto transparency_exit;
703
704
7.55k
    code = pdfi_dict_knownget_type(ctx, pattern, "ExtGState", PDF_DICT, &o);
705
7.55k
    if (code > 0)
706
0
        (void)pdfi_check_ExtGState(ctx, (pdf_dict *)o, page_dict, tracker);
707
7.55k
    pdfi_countdown(o);
708
7.55k
    o = NULL;
709
710
23.2k
transparency_exit:
711
23.2k
    return 0;
712
7.55k
}
713
714
/*
715
 * This routine checks a Pattern dictionary for transparency.
716
 */
717
int pdfi_check_Pattern_transparency(pdf_context *ctx, pdf_dict *pattern, pdf_dict *page_dict,
718
                                    bool *transparent)
719
7.20k
{
720
7.20k
    int code;
721
7.20k
    pdfi_check_tracker_t tracker = {0, 0, NULL, NULL, 0, NULL};
722
723
    /* NOTE: We use a "null" tracker that won't do any optimization to prevent
724
     * checking the same resource twice.
725
     * This means we don't get the optimization, but we also don't have the overhead
726
     * of setting it up.
727
     * If we do want the optimization, call pdfi_check_init_tracker() and
728
     * pdfi_check_free_tracker() as in pdfi_check_page(), below.
729
     */
730
7.20k
    code = pdfi_check_Pattern(ctx, pattern, page_dict, &tracker);
731
7.20k
    if (code == 0)
732
7.20k
        *transparent = tracker.transparent;
733
0
    else
734
0
        *transparent = false;
735
7.20k
    return code;
736
7.20k
}
737
738
/*
739
 * Check the Resources dictionary Pattern entry.
740
 */
741
static int pdfi_check_Pattern_dict(pdf_context *ctx, pdf_dict *pattern_dict, pdf_dict *page_dict,
742
                                   pdfi_check_tracker_t *tracker)
743
2.42k
{
744
2.42k
    int code;
745
2.42k
    uint64_t i, index;
746
2.42k
    pdf_obj *Key = NULL, *Value = NULL;
747
2.42k
    pdf_dict *instance_dict = NULL;
748
749
2.42k
    if (resource_is_checked(tracker, (pdf_obj *)pattern_dict))
750
0
        return 0;
751
752
2.42k
    if (pdfi_type_of(pattern_dict) != PDF_DICT)
753
0
        return_error(gs_error_typecheck);
754
755
2.42k
    if (pdfi_dict_entries(pattern_dict) > 0) {
756
2.18k
        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the Pattern dictionary loop */
757
2.18k
        if (code < 0)
758
0
            return code;
759
760
2.18k
        code = pdfi_dict_first(ctx, pattern_dict, &Key, &Value, &index);
761
2.18k
        if (code < 0)
762
155
            goto error1;
763
764
2.02k
        if (pdfi_type_of(Value) != PDF_DICT && pdfi_type_of(Value) != PDF_STREAM)
765
112
            goto transparency_exit;
766
767
1.91k
        i = 1;
768
16.2k
        do {
769
16.2k
            code = pdfi_dict_from_obj(ctx, Value, &instance_dict);
770
16.2k
            if (code < 0)
771
0
                goto transparency_exit;
772
773
16.2k
            code = pdfi_check_Pattern(ctx, instance_dict, page_dict, tracker);
774
16.2k
            if (code < 0)
775
0
                goto transparency_exit;
776
777
16.2k
            pdfi_countdown(Key);
778
16.2k
            Key = NULL;
779
16.2k
            pdfi_countdown(Value);
780
16.2k
            instance_dict = NULL;
781
16.2k
            Value = NULL;
782
16.2k
            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Shading dictionary loop */
783
784
16.2k
            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Shading dictionary loop */
785
16.2k
            if (code < 0)
786
0
                goto error1;
787
788
25.2k
            do {
789
25.2k
                if (i++ >= pdfi_dict_entries(pattern_dict)) {
790
1.91k
                    code = 0;
791
1.91k
                    goto transparency_exit;
792
1.91k
                }
793
794
23.3k
                code = pdfi_dict_next(ctx, pattern_dict, &Key, &Value, &index);
795
23.3k
                if (code == 0 && (pdfi_type_of(Value) == PDF_DICT || pdfi_type_of(Value) == PDF_STREAM))
796
14.3k
                    break;
797
9.00k
                pdfi_countdown(Key);
798
9.00k
                Key = NULL;
799
9.00k
                pdfi_countdown(Value);
800
9.00k
                Value = NULL;
801
9.00k
            } while(1);
802
16.2k
        }while (1);
803
1.91k
    }
804
246
    return 0;
805
806
2.02k
transparency_exit:
807
2.02k
    pdfi_countdown(Key);
808
2.02k
    pdfi_countdown(Value);
809
2.02k
    pdfi_countdown(instance_dict);
810
811
2.18k
error1:
812
2.18k
    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
813
2.18k
    return code;
814
2.02k
}
815
816
/*
817
 * This routine checks a Font dictionary to see if it contains any spot
818
 * colour definitions, or transparency usage. While we are here, if the tracker's font_array
819
 * is not NULL, pick up the font information and store it in the array.
820
 */
821
static int pdfi_check_Font(pdf_context *ctx, pdf_dict *font, pdf_dict *page_dict,
822
                           pdfi_check_tracker_t *tracker)
823
121k
{
824
121k
    int code = 0;
825
121k
    pdf_obj *o = NULL;
826
827
121k
    if (resource_is_checked(tracker, (pdf_obj *)font))
828
9.48k
        return 0;
829
830
111k
    if (pdfi_type_of(font) != PDF_DICT)
831
12.8k
        return_error(gs_error_typecheck);
832
833
99.0k
    if (tracker->font_array != NULL) {
834
        /* If we get to here this is a font we have not seen before. We need
835
         * to make a new font array big enough to hold the existing entries +1
836
         * copy the existing entries to the new array and free the old array.
837
         * Finally create a dictionary with all the font information we want
838
         * and add it to the array.
839
         */
840
0
        pdf_array *new_fonts = NULL;
841
0
        int index = 0;
842
0
        pdf_obj *array_obj = NULL;
843
0
        pdf_dict *font_info_dict = NULL;
844
845
        /* Let's start by gathering the information we need and storing it in a dictionary */
846
0
        code = pdfi_dict_alloc(ctx, 4, &font_info_dict);
847
0
        if (code < 0)
848
0
            return code;
849
0
        pdfi_countup(font_info_dict);
850
851
0
        if (font->object_num != 0) {
852
0
            pdf_num *int_obj = NULL;
853
854
0
            code = pdfi_object_alloc(ctx, PDF_INT, 0, (pdf_obj **)&int_obj);
855
0
            if (code >= 0) {
856
0
                pdfi_countup(int_obj);
857
0
                int_obj->value.i = font->object_num;
858
0
                code = pdfi_dict_put(ctx, font_info_dict, "ObjectNum", (pdf_obj *)int_obj);
859
0
                pdfi_countdown(int_obj);
860
0
            }
861
0
            if (code < 0) {
862
0
                pdfi_countdown(font_info_dict);
863
0
                return code;
864
0
            }
865
0
        }
866
867
0
        code = pdfi_dict_get(ctx, font, "BaseFont", &array_obj);
868
0
        if (code >= 0) {
869
0
            code = pdfi_dict_put(ctx, font_info_dict, "BaseFont", array_obj);
870
0
            if (code < 0) {
871
0
                pdfi_countdown(array_obj);
872
0
                pdfi_countdown(font_info_dict);
873
0
                return code;
874
0
            }
875
0
        }
876
0
        pdfi_countdown(array_obj);
877
0
        array_obj = NULL;
878
879
0
        code = pdfi_dict_get(ctx, font, "ToUnicode", &array_obj);
880
0
        if (code >= 0)
881
0
            code = pdfi_dict_put(ctx, font_info_dict, "ToUnicode", PDF_TRUE_OBJ);
882
0
        else
883
0
            code = pdfi_dict_put(ctx, font_info_dict, "ToUnicode", PDF_FALSE_OBJ);
884
0
        pdfi_countdown(array_obj);
885
0
        array_obj = NULL;
886
0
        if (code < 0)
887
0
            return code;
888
889
0
        code = pdfi_dict_get(ctx, font, "FontDescriptor", &array_obj);
890
0
        if (code >= 0) {
891
0
            bool known = false;
892
893
0
            (void)pdfi_dict_known(ctx, (pdf_dict *)array_obj, "FontFile", &known);
894
0
            if (!known) {
895
0
                (void)pdfi_dict_known(ctx, (pdf_dict *)array_obj, "FontFile2", &known);
896
0
                if (!known) {
897
0
                    (void)pdfi_dict_known(ctx, (pdf_dict *)array_obj, "FontFile3", &known);
898
0
                }
899
0
            }
900
901
0
            if (known >= 0)
902
0
                code = pdfi_dict_put(ctx, font_info_dict, "Embedded", PDF_TRUE_OBJ);
903
0
            else
904
0
                code = pdfi_dict_put(ctx, font_info_dict, "Embedded", PDF_FALSE_OBJ);
905
0
        } else
906
0
            code = pdfi_dict_put(ctx, font_info_dict, "Embedded", PDF_FALSE_OBJ);
907
908
0
        pdfi_countdown(array_obj);
909
0
        array_obj = NULL;
910
911
0
        if (code < 0)
912
0
            return code;
913
914
915
0
        code = pdfi_dict_knownget_type(ctx, font, "Subtype", PDF_NAME, &array_obj);
916
0
        if (code > 0) {
917
0
            code = pdfi_dict_put(ctx, font_info_dict, "Subtype", array_obj);
918
0
            if (code < 0) {
919
0
                pdfi_countdown(array_obj);
920
0
                pdfi_countdown(font_info_dict);
921
0
                return code;
922
0
            }
923
924
0
            if (pdfi_name_is((pdf_name *)array_obj, "Type3")) {
925
0
                pdfi_countdown(o);
926
0
                o = NULL;
927
928
0
                code = pdfi_dict_knownget_type(ctx, font, "Resources", PDF_DICT, &o);
929
0
                if (code > 0)
930
0
                    (void)pdfi_check_Resources(ctx, (pdf_dict *)o, page_dict, tracker);
931
0
            }
932
933
0
            if (pdfi_name_is((const pdf_name *)array_obj, "Type0")){
934
0
                pdf_array *descendants = NULL;
935
0
                pdf_dict *desc_font = NULL;
936
937
0
                code = pdfi_dict_get(ctx, font, "DescendantFonts", (pdf_obj **)&descendants);
938
0
                if (code >= 0) {
939
0
                    code = pdfi_array_get(ctx, descendants, 0, (pdf_obj **)&desc_font);
940
0
                    if (code >= 0){
941
0
                        pdf_array *desc_array = NULL;
942
943
0
                        code = pdfi_array_alloc(ctx, 0, &desc_array);
944
0
                        pdfi_countup(desc_array);
945
0
                        if (code >= 0) {
946
0
                            pdf_array *saved = tracker->font_array;
947
948
0
                            tracker->font_array = desc_array;
949
0
                            (void)pdfi_check_Font(ctx, desc_font, page_dict, tracker);
950
0
                            (void)pdfi_dict_put(ctx, font_info_dict, "Descendants", (pdf_obj *)tracker->font_array);
951
0
                            pdfi_countdown((pdf_obj *)tracker->font_array);
952
0
                            tracker->font_array = saved;
953
0
                        }
954
0
                        pdfi_countdown(descendants);
955
0
                        pdfi_countdown(desc_font);
956
0
                    }
957
0
                }
958
0
            }
959
0
        }
960
0
        pdfi_countdown(array_obj);
961
0
        array_obj = NULL;
962
963
0
        code = pdfi_array_alloc(ctx, pdfi_array_size(tracker->font_array) + 1, &new_fonts);
964
0
        if (code < 0) {
965
0
            pdfi_countdown(font_info_dict);
966
0
            return code;
967
0
        }
968
0
        pdfi_countup(new_fonts);
969
970
0
        for (index = 0; index < pdfi_array_size(tracker->font_array); index++) {
971
0
            code = pdfi_array_get(ctx, tracker->font_array, index, &array_obj);
972
0
            if (code < 0) {
973
0
                pdfi_countdown(font_info_dict);
974
0
                pdfi_countdown(new_fonts);
975
0
                return code;
976
0
            }
977
0
            code = pdfi_array_put(ctx, new_fonts, index, array_obj);
978
0
            pdfi_countdown(array_obj);
979
0
            if (code < 0) {
980
0
                pdfi_countdown(font_info_dict);
981
0
                pdfi_countdown(new_fonts);
982
0
                return code;
983
0
            }
984
0
        }
985
0
        code = pdfi_array_put(ctx, new_fonts, index, (pdf_obj *)font_info_dict);
986
0
        if (code < 0) {
987
0
            pdfi_countdown(font_info_dict);
988
0
            pdfi_countdown(new_fonts);
989
0
            return code;
990
0
        }
991
0
        pdfi_countdown(font_info_dict);
992
0
        pdfi_countdown(tracker->font_array);
993
0
        tracker->font_array = new_fonts;
994
99.0k
    } else {
995
99.0k
        code = pdfi_dict_knownget_type(ctx, font, "Subtype", PDF_NAME, &o);
996
99.0k
        if (code > 0) {
997
98.6k
            if (pdfi_name_is((pdf_name *)o, "Type3")) {
998
3.02k
                pdfi_countdown(o);
999
3.02k
                o = NULL;
1000
1001
3.02k
                code = pdfi_dict_knownget_type(ctx, font, "Resources", PDF_DICT, &o);
1002
3.02k
                if (code > 0)
1003
756
                    (void)pdfi_check_Resources(ctx, (pdf_dict *)o, page_dict, tracker);
1004
3.02k
            }
1005
98.6k
        }
1006
1007
99.0k
        pdfi_countdown(o);
1008
99.0k
        o = NULL;
1009
99.0k
    }
1010
1011
99.0k
    return 0;
1012
99.0k
}
1013
1014
/*
1015
 * Check the Resources dictionary Font entry.
1016
 */
1017
static int pdfi_check_Font_dict(pdf_context *ctx, pdf_dict *font_dict, pdf_dict *page_dict,
1018
                                pdfi_check_tracker_t *tracker)
1019
89.0k
{
1020
89.0k
    int code;
1021
89.0k
    uint64_t i, index;
1022
89.0k
    pdf_obj *Key = NULL, *Value = NULL;
1023
1024
89.0k
    if (resource_is_checked(tracker, (pdf_obj *)font_dict))
1025
2
        return 0;
1026
1027
89.0k
    if (pdfi_type_of(font_dict) != PDF_DICT)
1028
0
        return_error(gs_error_typecheck);
1029
1030
89.0k
    if (pdfi_dict_entries(font_dict) > 0) {
1031
88.9k
        code = pdfi_loop_detector_mark(ctx); /* Mark the start of the Font dictionary loop */
1032
88.9k
        if (code < 0)
1033
0
            return code;
1034
1035
88.9k
        code = pdfi_dict_first(ctx, font_dict, &Key, &Value, &index);
1036
88.9k
        if (code < 0)
1037
12.6k
            goto error1;
1038
1039
76.2k
        i = 1;
1040
121k
        do {
1041
121k
            code = pdfi_check_Font(ctx, (pdf_dict *)Value, page_dict, tracker);
1042
1043
121k
            pdfi_countdown(Key);
1044
121k
            Key = NULL;
1045
121k
            pdfi_countdown(Value);
1046
121k
            Value = NULL;
1047
1048
121k
            (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the Font dictionary loop */
1049
1050
121k
            code = pdfi_loop_detector_mark(ctx); /* Mark the new start of the Font dictionary loop */
1051
121k
            if (code < 0)
1052
0
                goto error1;
1053
1054
162k
            do {
1055
162k
                if (i++ >= pdfi_dict_entries(font_dict)) {
1056
76.2k
                    code = 0;
1057
76.2k
                    goto transparency_exit;
1058
76.2k
                }
1059
1060
86.3k
                code = pdfi_dict_next(ctx, font_dict, &Key, &Value, &index);
1061
86.3k
                if (code == 0 && pdfi_type_of(Value) == PDF_DICT)
1062
45.2k
                    break;
1063
41.0k
                pdfi_countdown(Key);
1064
41.0k
                Key = NULL;
1065
41.0k
                pdfi_countdown(Value);
1066
41.0k
                Value = NULL;
1067
41.0k
            } while(1);
1068
121k
        }while (1);
1069
76.2k
    }
1070
94
    return 0;
1071
1072
76.2k
transparency_exit:
1073
76.2k
    pdfi_countdown(Key);
1074
76.2k
    pdfi_countdown(Value);
1075
1076
88.9k
error1:
1077
88.9k
    (void)pdfi_loop_detector_cleartomark(ctx); /* Clear to the mark for the current resource loop */
1078
88.9k
    return code;
1079
76.2k
}
1080
1081
static int pdfi_check_Resources(pdf_context *ctx, pdf_dict *Resources_dict,
1082
                                pdf_dict *page_dict, pdfi_check_tracker_t *tracker)
1083
452k
{
1084
452k
    int code;
1085
452k
    pdf_obj *d = NULL;
1086
1087
452k
    if (resource_is_checked(tracker, (pdf_obj *)Resources_dict))
1088
20
        return 0;
1089
1090
452k
    if (pdfi_type_of(Resources_dict) != PDF_DICT)
1091
0
        return_error(gs_error_typecheck);
1092
1093
    /* First up, check any colour spaces, for new spot colours.
1094
     * We only do this if asked because its expensive. spot_dict being NULL
1095
     * means we aren't interested in spot colours (not a DeviceN or Separation device)
1096
     */
1097
452k
    if (tracker->spot_dict != NULL) {
1098
19.7k
        code = pdfi_dict_knownget_type(ctx, Resources_dict, "ColorSpace", PDF_DICT, &d);
1099
19.7k
        if (code > 0)
1100
3.32k
            (void)pdfi_check_ColorSpace_dict(ctx, (pdf_dict *)d, page_dict, tracker);
1101
1102
19.7k
        pdfi_countdown(d);
1103
19.7k
        d = NULL;
1104
1105
19.7k
        code = pdfi_dict_knownget_type(ctx, Resources_dict, "Shading", PDF_DICT, &d);
1106
19.7k
        if (code > 0)
1107
482
            (void)pdfi_check_Shading_dict(ctx, (pdf_dict *)d, page_dict, tracker);
1108
19.7k
        pdfi_countdown(d);
1109
19.7k
        d = NULL;
1110
19.7k
    }
1111
1112
452k
    code = pdfi_dict_knownget_type(ctx, Resources_dict, "XObject", PDF_DICT, &d);
1113
452k
    if (code > 0)
1114
150k
        (void)pdfi_check_XObject_dict(ctx, (pdf_dict *)d, page_dict, tracker);
1115
452k
    pdfi_countdown(d);
1116
452k
    d = NULL;
1117
1118
452k
    code = pdfi_dict_knownget_type(ctx, Resources_dict, "Pattern", PDF_DICT, &d);
1119
452k
    if (code > 0)
1120
2.42k
        (void)pdfi_check_Pattern_dict(ctx, (pdf_dict *)d, page_dict, tracker);
1121
452k
    pdfi_countdown(d);
1122
452k
    d = NULL;
1123
1124
452k
    code = pdfi_dict_knownget_type(ctx, Resources_dict, "Font", PDF_DICT, &d);
1125
452k
    if (code > 0)
1126
89.0k
        (void)pdfi_check_Font_dict(ctx, (pdf_dict *)d, page_dict, tracker);
1127
    /* From this point onwards, if we detect transparency (or have already detected it) we
1128
     * can exit, we have already counted up any spot colours.
1129
     */
1130
452k
    pdfi_countdown(d);
1131
452k
    d = NULL;
1132
1133
452k
    code = pdfi_dict_knownget_type(ctx, Resources_dict, "ExtGState", PDF_DICT, &d);
1134
452k
    if (code > 0)
1135
135k
        (void)pdfi_check_ExtGState_dict(ctx, (pdf_dict *)d, page_dict, tracker);
1136
452k
    pdfi_countdown(d);
1137
452k
    d = NULL;
1138
1139
452k
    return 0;
1140
452k
}
1141
1142
static int pdfi_check_annot_for_transparency(pdf_context *ctx, pdf_dict *annot, pdf_dict *page_dict,
1143
                                             pdfi_check_tracker_t *tracker)
1144
979k
{
1145
979k
    int code;
1146
979k
    pdf_name *n;
1147
979k
    pdf_obj *N = NULL;
1148
979k
    pdf_dict *ap = NULL;
1149
979k
    pdf_dict *Resources = NULL;
1150
979k
    double f;
1151
1152
979k
    if (resource_is_checked(tracker, (pdf_obj *)annot))
1153
18
        return 0;
1154
1155
979k
    if (pdfi_type_of(annot) != PDF_DICT)
1156
0
        return_error(gs_error_typecheck);
1157
1158
    /* Check #1 Does the (Normal) Appearnce stream use any Resources which include transparency.
1159
     * We check this first, because this also checks for spot colour spaces. Once we've done that we
1160
     * can exit the checks as soon as we detect transparency.
1161
     */
1162
979k
    code = pdfi_dict_knownget_type(ctx, annot, "AP", PDF_DICT, (pdf_obj **)&ap);
1163
979k
    if (code > 0)
1164
438k
    {
1165
        /* Fetch without resolving indirect ref because pdfmark wants it that way later */
1166
438k
        code = pdfi_dict_get_no_store_R(ctx, ap, "N", (pdf_obj **)&N);
1167
438k
        if (code >= 0) {
1168
318k
            pdf_dict *dict = NULL;
1169
1170
318k
            code = pdfi_dict_from_obj(ctx, N, &dict);
1171
318k
            if (code == 0)
1172
318k
                code = pdfi_dict_knownget_type(ctx, dict, "Resources", PDF_DICT, (pdf_obj **)&Resources);
1173
318k
            if (code > 0)
1174
252k
                code = pdfi_check_Resources(ctx, (pdf_dict *)Resources, page_dict, tracker);
1175
318k
        }
1176
438k
        if (code == gs_error_undefined)
1177
111k
            code = 0;
1178
438k
    }
1179
979k
    pdfi_countdown(ap);
1180
979k
    pdfi_countdown(N);
1181
979k
    pdfi_countdown(Resources);
1182
1183
979k
    if (code < 0)
1184
35.0k
        return code;
1185
    /* We've checked the Resources, and nothing else in an annotation can define spot colours, so
1186
     * if we detected transparency in the Resources we need not check further.
1187
     */
1188
944k
    if (tracker->transparent == true)
1189
4.53k
        return 0;
1190
1191
939k
    code = pdfi_dict_get_type(ctx, annot, "Subtype", PDF_NAME, (pdf_obj **)&n);
1192
939k
    if (code < 0) {
1193
250
        if (ctx->args.pdfstoponerror)
1194
0
            return code;
1195
939k
    } else {
1196
        /* Check #2, Highlight annotations are always preformed with transparency */
1197
939k
        if (pdfi_name_is((const pdf_name *)n, "Highlight")) {
1198
395
            pdfi_countdown(n);
1199
395
            tracker->transparent = true;
1200
395
            return 0;
1201
395
        }
1202
939k
        pdfi_countdown(n);
1203
939k
        n = NULL;
1204
1205
        /* Check #3 Blend Mode (BM) not being 'Normal' or 'Compatible' */
1206
939k
        code = pdfi_dict_knownget_type(ctx, annot, "BM", PDF_NAME, (pdf_obj **)&n);
1207
939k
        if (code > 0) {
1208
0
            if (!pdfi_name_is((const pdf_name *)n, "Normal")) {
1209
0
                if (!pdfi_name_is((const pdf_name *)n, "Compatible")) {
1210
0
                    pdfi_countdown(n);
1211
0
                    tracker->transparent = true;
1212
0
                    return 0;
1213
0
                }
1214
0
            }
1215
0
            code = 0;
1216
0
        }
1217
939k
        pdfi_countdown(n);
1218
939k
        if (code < 0)
1219
0
            return code;
1220
1221
        /* Check #4 stroke constant alpha (CA) is not 1 (100% opaque) */
1222
939k
        code = pdfi_dict_knownget_number(ctx, annot, "CA", &f);
1223
939k
        if (code > 0) {
1224
662
            if (f != 1.0) {
1225
34
                tracker->transparent = true;
1226
34
                return 0;
1227
34
            }
1228
662
        }
1229
939k
        if (code < 0)
1230
0
            return code;
1231
1232
        /* Check #5 non-stroke constant alpha (ca) is not 1 (100% opaque) */
1233
939k
        code = pdfi_dict_knownget_number(ctx, annot, "ca", &f);
1234
939k
        if (code > 0) {
1235
0
            if (f != 1.0) {
1236
0
                tracker->transparent = true;
1237
0
                return 0;
1238
0
            }
1239
0
        }
1240
939k
        if (code < 0)
1241
0
            return code;
1242
939k
    }
1243
1244
939k
    return 0;
1245
939k
}
1246
1247
static int pdfi_check_Annots_for_transparency(pdf_context *ctx, pdf_array *annots_array,
1248
                                              pdf_dict *page_dict, pdfi_check_tracker_t *tracker)
1249
36.0k
{
1250
36.0k
    int i, code = 0;
1251
36.0k
    pdf_dict *annot = NULL;
1252
1253
36.0k
    if (resource_is_checked(tracker, (pdf_obj *)annots_array))
1254
0
        return 0;
1255
1256
36.0k
    if (pdfi_type_of(annots_array) != PDF_ARRAY)
1257
0
        return_error(gs_error_typecheck);
1258
1259
1.08M
    for (i=0; i < pdfi_array_size(annots_array); i++) {
1260
1.04M
        code = pdfi_array_get_type(ctx, annots_array, (uint64_t)i, PDF_DICT, (pdf_obj **)&annot);
1261
1.04M
        if (code >= 0) {
1262
979k
            code = pdfi_check_annot_for_transparency(ctx, annot, page_dict, tracker);
1263
979k
            if (code < 0 && ctx->args.pdfstoponerror)
1264
0
                goto exit;
1265
1266
            /* If we've found transparency, and don't need to continue checkign for spot colours
1267
             * just exit as fast as possible.
1268
             */
1269
979k
            if (tracker->transparent == true && tracker->spot_dict == NULL)
1270
1.85k
                goto exit;
1271
1272
977k
            pdfi_countdown(annot);
1273
977k
            annot = NULL;
1274
977k
        }
1275
1.04M
        if (code < 0 && ctx->args.pdfstoponerror)
1276
0
            goto exit;
1277
1.04M
        code = 0;
1278
1.04M
    }
1279
36.0k
exit:
1280
36.0k
    pdfi_countdown(annot);
1281
36.0k
    return code;
1282
36.0k
}
1283
1284
/* Check for transparency and spots on page.
1285
 *
1286
 * Sets ctx->spot_capable_device
1287
 * Builds a dictionary of the unique spot names in spot_dict
1288
 * Set 'transparent' to true if there is transparency on the page
1289
 *
1290
 * From the original PDF interpreter written in PostScript:
1291
 * Note: we deliberately don't check to see whether a Group is defined,
1292
 * because Adobe Illustrator 10 (and possibly other applications) define
1293
 * a page-level group whether transparency is actually used or not.
1294
 * Ignoring the presence of Group is justified because, in the absence
1295
 * of any other transparency features, they have no effect.
1296
 */
1297
static int pdfi_check_page_inner(pdf_context *ctx, pdf_dict *page_dict,
1298
                                 pdfi_check_tracker_t *tracker)
1299
86.9k
{
1300
86.9k
    int code;
1301
86.9k
    pdf_dict *Resources = NULL;
1302
86.9k
    pdf_array *Annots = NULL;
1303
86.9k
    pdf_dict *Group = NULL;
1304
86.9k
    pdf_obj *CS = NULL;
1305
1306
86.9k
    tracker->transparent = false;
1307
1308
86.9k
    if (pdfi_type_of(page_dict) != PDF_DICT)
1309
0
        return_error(gs_error_typecheck);
1310
1311
1312
    /* Check if the page dictionary has a page Group entry (for spots).
1313
     * Page group should mean the page has transparency but we ignore it for the purposes
1314
     * of transparency detection. See above.
1315
     */
1316
86.9k
    if (tracker->spot_dict) {
1317
9.19k
        code = pdfi_dict_knownget_type(ctx, page_dict, "Group", PDF_DICT, (pdf_obj **)&Group);
1318
9.19k
        if (code > 0) {
1319
            /* If Group has a ColorSpace (CS), then check it for spot colours */
1320
1.09k
            code = pdfi_dict_knownget(ctx, Group, "CS", &CS);
1321
1.09k
            if (code > 0)
1322
1.08k
                code = pdfi_check_ColorSpace_for_spots(ctx, CS, Group, page_dict, tracker->spot_dict);
1323
1.09k
            if (code < 0 && ctx->args.pdfstoponerror)
1324
0
                goto exit;
1325
1.09k
        }
1326
9.19k
    }
1327
1328
    /* Now check any Resources dictionary in the Page dictionary */
1329
86.9k
    code = pdfi_dict_knownget_type(ctx, page_dict, "Resources", PDF_DICT, (pdf_obj **)&Resources);
1330
86.9k
    if (code > 0)
1331
81.1k
        code = pdfi_check_Resources(ctx, Resources, page_dict, tracker);
1332
86.9k
    if ((code < 0 && ctx->args.pdfstoponerror) || (code == gs_error_pdf_stackoverflow))
1333
0
        goto exit;
1334
1335
    /* If we are drawing Annotations, check to see if the page uses any Annots */
1336
86.9k
    if (ctx->args.showannots) {
1337
86.9k
        code = pdfi_dict_knownget_type(ctx, page_dict, "Annots", PDF_ARRAY, (pdf_obj **)&Annots);
1338
86.9k
        if (code > 0)
1339
36.0k
            code = pdfi_check_Annots_for_transparency(ctx, Annots, page_dict,
1340
36.0k
                                                      tracker);
1341
86.9k
        if (code < 0 && ctx->args.pdfstoponerror)
1342
0
            goto exit;
1343
86.9k
    }
1344
1345
86.9k
    code = 0;
1346
86.9k
 exit:
1347
86.9k
    pdfi_countdown(Resources);
1348
86.9k
    pdfi_countdown(Annots);
1349
86.9k
    pdfi_countdown(CS);
1350
86.9k
    pdfi_countdown(Group);
1351
86.9k
    return code;
1352
86.9k
}
1353
1354
/* Checks page for transparency, and sets up device for spots, if applicable
1355
 * Sets ctx->page.has_transparency and ctx->page.num_spots
1356
 * do_setup -- indicates whether to actually set up the device with the spot count.
1357
 */
1358
int pdfi_check_page(pdf_context *ctx, pdf_dict *page_dict, pdf_array **fonts_array, pdf_array **spots_array, bool do_setup)
1359
86.9k
{
1360
86.9k
    int code;
1361
86.9k
    int spots = 0;
1362
86.9k
    pdfi_check_tracker_t tracker;
1363
1364
86.9k
    ctx->page.num_spots = 0;
1365
86.9k
    ctx->page.has_transparency = false;
1366
1367
86.9k
    code = pdfi_check_init_tracker(ctx, &tracker, fonts_array, spots_array);
1368
86.9k
    if (code < 0)
1369
0
        goto exit;
1370
1371
    /* Check for spots and transparency in this page */
1372
86.9k
    code = pdfi_check_page_inner(ctx, page_dict, &tracker);
1373
86.9k
    if (code < 0)
1374
0
        goto exit;
1375
1376
    /* Count the spots */
1377
86.9k
    if (tracker.spot_dict)
1378
9.19k
        spots = pdfi_dict_entries(tracker.spot_dict);
1379
1380
    /* If setup requested, tell the device about spots and transparency */
1381
86.9k
    if (do_setup) {
1382
0
        gs_c_param_list list;
1383
0
        int a = 0;
1384
0
        pdf_name *Key = NULL;
1385
0
        pdf_obj *Value = NULL;
1386
0
        uint64_t index = 0;
1387
1388
0
        gs_c_param_list_write(&list, ctx->memory);
1389
1390
        /* If there are spot colours (and by inference, the device renders spot plates) then
1391
         * send the number of Spots to the device, so it can setup correctly.
1392
         */
1393
0
        if (tracker.spot_dict) {
1394
            /* There is some awkwardness here. If the SeparationColorNames setting
1395
             * fails, we want to ignore it (this can mean that we exceeded the maximum
1396
             * number of colourants and some will be converted to CMYK). But if that happens,
1397
             * any other parameters in the same list which haven't already been prcoessed
1398
             * will be lost. So we need to send two lists, the SeparationColorNames and
1399
             * 'everything else'.
1400
             */
1401
0
            if (spots > 0) {
1402
0
                gs_param_string_array sa;
1403
0
                gs_param_string *table = NULL;
1404
1405
0
                table = (gs_param_string *)gs_alloc_byte_array(ctx->memory, spots, sizeof(gs_param_string), "SeparationNames");
1406
0
                if (table != NULL)
1407
0
                {
1408
0
                    memset(table, 0x00, spots * sizeof(gs_param_string));
1409
1410
0
                    code = pdfi_dict_first(ctx, tracker.spot_dict, (pdf_obj **)&Key, &Value, &index);
1411
0
                    while (code >= 0)
1412
0
                    {
1413
0
                        if (pdfi_type_of(Key) == PDF_NAME) {
1414
0
                            table[a].data = ((pdf_string *)Key)->data;
1415
0
                            table[a].size = ((pdf_string *)Key)->length;
1416
0
                            table[a++].persistent = false;
1417
0
                        }
1418
                        /* Although we count down the returned PDF objects here, the pointers
1419
                         * to the name data remain valid and won't move. Provided we don't
1420
                         * retain the pointers after we free the tracker dictionary this is
1421
                         * safe to do.
1422
                         */
1423
0
                        pdfi_countdown(Key);
1424
0
                        Key = NULL;
1425
0
                        pdfi_countdown(Value);
1426
0
                        Value = NULL;
1427
0
                        code = pdfi_dict_next(ctx, tracker.spot_dict, (pdf_obj **)&Key, &Value, &index);
1428
0
                    }
1429
0
                    sa.data = table;
1430
0
                    sa.size = spots;
1431
0
                    sa.persistent = false;
1432
1433
0
                    (void)param_write_string_array((gs_param_list *)&list, "SeparationColorNames", &sa);
1434
0
                    gs_c_param_list_read(&list);
1435
0
                    code = gs_putdeviceparams(ctx->pgs->device, (gs_param_list *)&list);
1436
0
                    gs_c_param_list_release(&list);
1437
1438
0
                    gs_free_object(ctx->memory, table, "SeparationNames");
1439
0
                    if (code > 0) {
1440
                        /* The device was closed, we need to reopen it */
1441
0
                        code = gs_setdevice_no_erase(ctx->pgs, ctx->pgs->device);
1442
0
                        if (code < 0)
1443
0
                            goto exit;
1444
0
                        gs_erasepage(ctx->pgs);
1445
0
                    }
1446
1447
                    /* Reset the list back to being writeable */
1448
0
                    gs_c_param_list_write(&list, ctx->memory);
1449
0
                }
1450
0
                else {
1451
0
                    code = gs_note_error(gs_error_VMerror);
1452
0
                    goto exit;
1453
0
                }
1454
0
            }
1455
            /* Update the number of spots */
1456
0
            param_write_int((gs_param_list *)&list, "PageSpotColors", &spots);
1457
0
        }
1458
        /* Update the page transparency */
1459
0
        (void)param_write_bool((gs_param_list *)&list, "PageUsesTransparency",
1460
0
                                    &tracker.transparent);
1461
0
        gs_c_param_list_read(&list);
1462
0
        code = gs_putdeviceparams(ctx->pgs->device, (gs_param_list *)&list);
1463
0
        gs_c_param_list_release(&list);
1464
1465
0
        if (code > 0) {
1466
            /* The device was closed, we need to reopen it */
1467
0
            code = gs_setdevice_no_erase(ctx->pgs, ctx->pgs->device);
1468
0
            if (code < 0)
1469
0
                goto exit;
1470
0
            gs_erasepage(ctx->pgs);
1471
0
        }
1472
0
    }
1473
1474
    /* Set our values in the context, for caller */
1475
86.9k
    if (!ctx->args.notransparency)
1476
86.9k
        ctx->page.has_transparency = tracker.transparent;
1477
86.9k
    ctx->page.num_spots = spots;
1478
86.9k
    ctx->page.has_OP = tracker.has_overprint;
1479
1480
    /* High level devices do not render overprint */
1481
86.9k
    if (ctx->device_state.HighLevelDevice)
1482
50.1k
        ctx->page.has_OP = false;
1483
1484
86.9k
 exit:
1485
86.9k
    if (fonts_array != NULL) {
1486
0
        *fonts_array = tracker.font_array;
1487
0
        pdfi_countup(*fonts_array);
1488
0
    }
1489
1490
86.9k
    if (spots_array != NULL && tracker.spot_dict != NULL && pdfi_dict_entries(tracker.spot_dict) != 0) {
1491
0
        pdf_array *new_array = NULL;
1492
0
        pdf_name *Key = NULL;
1493
0
        pdf_obj *Value = NULL;
1494
0
        uint64_t index = 0, a_index = 0;
1495
1496
0
        index = pdfi_dict_entries(tracker.spot_dict);
1497
1498
0
        code = pdfi_array_alloc(ctx, index, &new_array);
1499
0
        if (code < 0)
1500
0
            goto error;
1501
0
        pdfi_countup(new_array);
1502
1503
0
        code = pdfi_dict_first(ctx, tracker.spot_dict, (pdf_obj **)&Key, &Value, &index);
1504
0
        while (code >= 0)
1505
0
        {
1506
0
            if (pdfi_type_of(Key) == PDF_NAME) {
1507
0
                code = pdfi_array_put(ctx, new_array, a_index++, (pdf_obj *)Key);
1508
0
                if (code < 0) {
1509
0
                    pdfi_countdown(new_array);
1510
0
                    pdfi_countdown(Key);
1511
0
                    pdfi_countdown(Value);
1512
0
                    goto error;
1513
0
                }
1514
0
            }
1515
1516
0
            pdfi_countdown(Key);
1517
0
            Key = NULL;
1518
0
            pdfi_countdown(Value);
1519
0
            Value = NULL;
1520
0
            code = pdfi_dict_next(ctx, tracker.spot_dict, (pdf_obj **)&Key, &Value, &index);
1521
0
        }
1522
0
        code = 0;
1523
0
        *spots_array = new_array;
1524
0
    }
1525
86.9k
error:
1526
86.9k
    (void)pdfi_check_free_tracker(ctx, &tracker);
1527
86.9k
    return code;
1528
86.9k
}