Coverage Report

Created: 2025-06-24 07:01

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