Coverage Report

Created: 2026-09-14 07:34

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