Coverage Report

Created: 2026-04-01 07:17

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