Coverage Report

Created: 2025-06-10 07:27

/src/ghostpdl/pdf/pdf_page.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
/* Page-level operations for the PDF interpreter */
17
18
#include "pdf_int.h"
19
#include "pdf_stack.h"
20
#include "pdf_doc.h"
21
#include "pdf_deref.h"
22
#include "pdf_page.h"
23
#include "pdf_file.h"
24
#include "pdf_dict.h"
25
#include "pdf_array.h"
26
#include "pdf_loop_detect.h"
27
#include "pdf_colour.h"
28
#include "pdf_trans.h"
29
#include "pdf_font_types.h"
30
#include "pdf_gstate.h"
31
#include "pdf_misc.h"
32
#include "pdf_optcontent.h"
33
#include "pdf_device.h"
34
#include "pdf_annot.h"
35
#include "pdf_check.h"
36
#include "pdf_mark.h"
37
#include "pdf_font.h"
38
39
#include "gscoord.h"        /* for gs_concat() and others */
40
#include "gspaint.h"        /* For gs_erasepage() */
41
#include "gsstate.h"        /* For gs_initgraphics() */
42
#include "gspath2.h"        /* For gs_rectclip() */
43
44
static int pdfi_process_page_contents(pdf_context *ctx, pdf_dict *page_dict)
45
6.07k
{
46
6.07k
    int i, code = 0;
47
6.07k
    pdf_obj *o, *o1;
48
49
#if PURGE_CACHE_PER_PAGE
50
    pdfi_purge_obj_cache(ctx);
51
#endif
52
53
6.07k
    code = pdfi_dict_get(ctx, page_dict, "Contents", &o);
54
6.07k
    if (code == gs_error_undefined)
55
        /* Don't throw an error if there are no contents, just render nothing.... */
56
494
        return 0;
57
5.58k
    if (code < 0)
58
79
        return code;
59
60
5.50k
    if (pdfi_type_of(o) == PDF_INDIRECT) {
61
0
        if (((pdf_indirect_ref *)o)->ref_object_num == page_dict->object_num)
62
0
            return_error(gs_error_circular_reference);
63
64
0
        code = pdfi_dereference(ctx, ((pdf_indirect_ref *)o)->ref_object_num, ((pdf_indirect_ref *)o)->ref_generation_num, &o1);
65
0
        pdfi_countdown(o);
66
0
        if (code < 0) {
67
0
            if (code == gs_error_VMerror)
68
0
                return code;
69
0
            return 0;
70
0
        }
71
0
        o = o1;
72
0
    }
73
74
5.50k
    code = pdfi_gsave(ctx);
75
5.50k
    if (code < 0) {
76
0
        pdfi_countdown(o);
77
0
        return code;
78
0
    }
79
    /* Increment the saved gsave_level by 1 to allow for the gsave we've just
80
     * done. Otherwise excess 'Q' operators in the stream can cause us to pop
81
     * one higher than we should. Bug 707753. */
82
5.50k
    ctx->current_stream_save.gsave_level++;
83
84
5.50k
    ctx->encryption.decrypt_strings = false;
85
5.50k
    if (pdfi_type_of(o) == PDF_ARRAY) {
86
737
        pdf_array *a = (pdf_array *)o;
87
88
3.22k
        for (i=0;i < pdfi_array_size(a); i++) {
89
2.58k
            pdf_indirect_ref *r;
90
2.58k
            code = pdfi_array_get_no_deref(ctx, a, i, (pdf_obj **)&r);
91
2.58k
            if (code < 0)
92
0
                goto page_error;
93
2.58k
            if (pdfi_type_of (r) == PDF_STREAM) {
94
0
                code = pdfi_interpret_content_stream(ctx, NULL, (pdf_stream *)r, page_dict);
95
0
                pdfi_countdown(r);
96
0
                if (code < 0)
97
0
                    goto page_error;
98
2.58k
            } else {
99
2.58k
                if (pdfi_type_of(r) != PDF_INDIRECT) {
100
1
                        pdfi_countdown(r);
101
1
                        code = gs_note_error(gs_error_typecheck);
102
1
                        goto page_error;
103
2.58k
                } else {
104
2.58k
                    if (r->ref_object_num == page_dict->object_num) {
105
0
                        pdfi_countdown(r);
106
0
                        code = gs_note_error(gs_error_circular_reference);
107
0
                        goto page_error;
108
0
                    }
109
2.58k
                    code = pdfi_dereference(ctx, r->ref_object_num, r->ref_generation_num, &o1);
110
2.58k
                    pdfi_countdown(r);
111
2.58k
                    if (code < 0) {
112
81
                        if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_NOERROR, "pdfi_process_page_contents", NULL)) < 0) {
113
0
                            goto page_error;
114
0
                        }
115
81
                        code = 0;
116
81
                        goto page_error;
117
81
                    }
118
2.50k
                    if (pdfi_type_of(o1) != PDF_STREAM) {
119
14
                        pdfi_countdown(o1);
120
14
                        code = gs_note_error(gs_error_typecheck);
121
14
                        goto page_error;
122
14
                    }
123
2.48k
                    code = pdfi_interpret_content_stream(ctx, NULL, (pdf_stream *)o1, page_dict);
124
2.48k
                    pdfi_countdown(o1);
125
2.48k
                    if (code < 0) {
126
67
                        if ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_NOERROR, "pdfi_process_page_contents", NULL)) < 0) {
127
0
                            goto page_error;
128
0
                        }
129
67
                    }
130
2.48k
                }
131
2.58k
            }
132
2.58k
        }
133
4.76k
    } else {
134
4.76k
        if (pdfi_type_of(o) == PDF_STREAM) {
135
4.74k
            code = pdfi_interpret_content_stream(ctx, NULL, (pdf_stream *)o, page_dict);
136
4.74k
        } else
137
18
            code = gs_note_error(gs_error_typecheck);
138
4.76k
    }
139
5.50k
page_error:
140
    /* Decrement the stream level to counterbalance the increment above. */
141
5.50k
    ctx->current_stream_save.gsave_level--;
142
5.50k
    ctx->encryption.decrypt_strings = true;
143
5.50k
    pdfi_clearstack(ctx);
144
5.50k
    pdfi_grestore(ctx);
145
5.50k
    pdfi_countdown(o);
146
5.50k
    return code;
147
5.50k
}
148
149
/* Render one page (including annotations) (see pdf_main.ps/showpagecontents) */
150
static int pdfi_process_one_page(pdf_context *ctx, pdf_dict *page_dict)
151
6.07k
{
152
6.07k
    stream_save local_entry_save;
153
6.07k
    int code, code1;
154
155
    /* Save the current stream state, for later cleanup, in a local variable */
156
6.07k
    local_save_stream_state(ctx, &local_entry_save);
157
6.07k
    initialise_stream_save(ctx);
158
159
6.07k
    code = pdfi_process_page_contents(ctx, page_dict);
160
161
    /* Put our state back the way it was before we ran the contents
162
     * and check if the stream had problems
163
     */
164
#if PROBE_STREAMS
165
    if (ctx->pgs->level > ctx->current_stream_save.gsave_level ||
166
        pdfi_count_stack(ctx) > ctx->current_stream_save.stack_count)
167
        code = ((pdf_context *)0)->first_page;
168
#endif
169
170
6.07k
    cleanup_context_interpretation(ctx, &local_entry_save);
171
6.07k
    local_restore_stream_state(ctx, &local_entry_save);
172
173
6.07k
    local_save_stream_state(ctx, &local_entry_save);
174
6.07k
    initialise_stream_save(ctx);
175
176
6.07k
    code1 = pdfi_do_annotations(ctx, page_dict);
177
6.07k
    if (code >= 0) code = code1;
178
179
6.07k
    cleanup_context_interpretation(ctx, &local_entry_save);
180
6.07k
    local_restore_stream_state(ctx, &local_entry_save);
181
182
6.07k
    local_save_stream_state(ctx, &local_entry_save);
183
6.07k
    initialise_stream_save(ctx);
184
185
6.07k
    code1 = pdfi_do_acroform(ctx, page_dict);
186
6.07k
    if (code >= 0) code = code1;
187
188
6.07k
    cleanup_context_interpretation(ctx, &local_entry_save);
189
6.07k
    local_restore_stream_state(ctx, &local_entry_save);
190
191
6.07k
    if (ctx->text.BlockDepth != 0) {
192
721
        pdfi_set_warning(ctx, 0, NULL, W_PDF_UNBLANACED_BT, "pdfi_process_one_page", "");
193
721
        ctx->text.BlockDepth = 0;
194
721
    }
195
6.07k
    return code;
196
6.07k
}
197
198
/* See pdf_PDF2PS_matrix and .pdfshowpage_Install */
199
static void pdfi_set_ctm(pdf_context *ctx)
200
0
{
201
0
    gs_matrix mat;
202
203
    /* Adjust for page.UserUnits */
204
0
    mat.xx = ctx->page.UserUnit;
205
0
    mat.xy = 0;
206
0
    mat.yx = 0;
207
0
    mat.yy = ctx->page.UserUnit;
208
0
    mat.tx = 0;
209
0
    mat.ty = 0;
210
0
    gs_concat(ctx->pgs, &mat);
211
212
    /* We need to make sure the default matrix is properly set.
213
     * If we do gs_initgraphics() later (such as for annotations)
214
     * then it uses this default matrix if it is set.
215
     * Needed for page rotations to work correctly with Annotations.
216
     */
217
0
    gs_setdefaultmatrix(ctx->pgs, &ctm_only(ctx->pgs));
218
219
0
}
220
221
/* Get .MediaSize from the device to setup page.Size in context */
222
static int pdfi_get_media_size(pdf_context *ctx, pdf_dict *page_dict)
223
6.07k
{
224
6.07k
    pdf_array *a = NULL, *default_media = NULL;
225
6.07k
    double d[4];
226
6.07k
    int code = 0;
227
6.07k
    uint64_t i;
228
6.07k
    double userunit = 1.0;
229
230
6.07k
    code = pdfi_dict_get_type(ctx, page_dict, "MediaBox", PDF_ARRAY, (pdf_obj **)&default_media);
231
6.07k
    if (code < 0) {
232
72
        pdfi_set_warning(ctx, code, NULL, W_PDF_BAD_MEDIABOX, "pdfi_get_media_size", NULL);
233
72
        code = gs_erasepage(ctx->pgs);
234
72
        return 0;
235
72
    }
236
237
6.00k
    if (ctx->args.usecropbox) {
238
0
        if (a != NULL)
239
0
            pdfi_countdown(a);
240
0
        (void)pdfi_dict_get_type(ctx, page_dict, "CropBox", PDF_ARRAY, (pdf_obj **)&a);
241
0
    }
242
6.00k
    if (ctx->args.useartbox) {
243
0
        if (a != NULL)
244
0
            pdfi_countdown(a);
245
0
        (void)pdfi_dict_get_type(ctx, page_dict, "ArtBox", PDF_ARRAY, (pdf_obj **)&a);
246
0
    }
247
6.00k
    if (ctx->args.usebleedbox) {
248
0
        if (a != NULL)
249
0
            pdfi_countdown(a);
250
0
        (void)pdfi_dict_get_type(ctx, page_dict, "BleedBox", PDF_ARRAY, (pdf_obj **)&a);
251
0
    }
252
6.00k
    if (ctx->args.usetrimbox) {
253
0
        if (a != NULL)
254
0
            pdfi_countdown(a);
255
0
        (void)pdfi_dict_get_type(ctx, page_dict, "TrimBox", PDF_ARRAY, (pdf_obj **)&a);
256
0
    }
257
6.00k
    if (a == NULL) {
258
6.00k
        a = default_media;
259
6.00k
        pdfi_countup(a);
260
6.00k
    }
261
262
6.00k
    if (!ctx->args.nouserunit && !ctx->device_state.PassUserUnit) {
263
6.00k
        (void)pdfi_dict_knownget_number(ctx, page_dict, "UserUnit", &userunit);
264
6.00k
    }
265
6.00k
    ctx->page.UserUnit = userunit;
266
267
30.0k
    for (i=0;i<4;i++) {
268
24.0k
        code = pdfi_array_get_number(ctx, a, i, &d[i]);
269
24.0k
        d[i] *= userunit;
270
24.0k
    }
271
6.00k
    pdfi_countdown(a);
272
6.00k
    pdfi_countdown(default_media);
273
274
6.00k
    normalize_rectangle(d);
275
6.00k
    memcpy(ctx->page.Size, d, 4 * sizeof(double));
276
277
6.00k
    return code;
278
6.07k
}
279
280
static int pdfi_set_media_size(pdf_context *ctx, pdf_dict *page_dict)
281
0
{
282
0
    gs_c_param_list list;
283
0
    gs_param_float_array fa;
284
0
    pdf_array *a = NULL, *default_media = NULL;
285
0
    float fv[2];
286
0
    double d[4], d_crop[4];
287
0
    gs_rect bbox;
288
0
    int code, do_crop = false;
289
0
    uint64_t i;
290
0
    int64_t rotate = 0;
291
0
    double userunit = 1.0;
292
293
0
    code = pdfi_dict_get_type(ctx, page_dict, "MediaBox", PDF_ARRAY, (pdf_obj **)&default_media);
294
0
    if (code < 0) {
295
0
        pdfi_set_warning(ctx, code, NULL, W_PDF_BAD_MEDIABOX, "pdfi_get_media_size", NULL);
296
0
        code = gs_erasepage(ctx->pgs);
297
0
        return 0;
298
0
    }
299
300
0
    if (ctx->args.usecropbox) {
301
0
        if (a != NULL)
302
0
            pdfi_countdown(a);
303
0
        (void)pdfi_dict_get_type(ctx, page_dict, "CropBox", PDF_ARRAY, (pdf_obj **)&a);
304
0
    }
305
0
    if (ctx->args.useartbox) {
306
0
        if (a != NULL)
307
0
            pdfi_countdown(a);
308
0
        (void)pdfi_dict_get_type(ctx, page_dict, "ArtBox", PDF_ARRAY, (pdf_obj **)&a);
309
0
    }
310
0
    if (ctx->args.usebleedbox) {
311
0
        if (a != NULL)
312
0
            pdfi_countdown(a);
313
0
        (void)pdfi_dict_get_type(ctx, page_dict, "BleedBox", PDF_ARRAY, (pdf_obj **)&a);
314
0
    }
315
0
    if (ctx->args.usetrimbox) {
316
0
        if (a != NULL)
317
0
            pdfi_countdown(a);
318
0
        (void)pdfi_dict_get_type(ctx, page_dict, "TrimBox", PDF_ARRAY, (pdf_obj **)&a);
319
0
    }
320
0
    if (a == NULL) {
321
0
        code = pdfi_dict_get_type(ctx, page_dict, "CropBox", PDF_ARRAY, (pdf_obj **)&a);
322
0
        if (code >= 0 && pdfi_array_size(a) >= 4) {
323
0
            pdf_obj *box_obj = NULL;
324
325
0
            for (i=0;i<4;i++) {
326
0
                code = pdfi_array_get_no_store_R(ctx, a, i, &box_obj);
327
0
                if (code >= 0) {
328
0
                    code = pdfi_obj_to_real(ctx, box_obj, &d_crop[i]);
329
0
                    pdfi_countdown(box_obj);
330
0
                }
331
0
                if (code < 0)
332
0
                    break;
333
0
            }
334
0
            pdfi_countdown(a);
335
0
            if (code >= 0) {
336
0
                normalize_rectangle(d_crop);
337
0
                memcpy(ctx->page.Crop, d_crop, 4 * sizeof(double));
338
0
                do_crop = true;
339
0
            }
340
0
        }
341
0
        a = default_media;
342
0
    }
343
344
0
    if (!ctx->args.nouserunit) {
345
0
        if (ctx->device_state.PassUserUnit) {
346
0
            double unit = 1.0;
347
0
            (void)pdfi_dict_knownget_number(ctx, page_dict, "UserUnit", &unit);
348
0
            (void)pdfi_device_set_param_float(ctx->pgs->device, "UserUnit", unit);
349
0
        } else {
350
0
            (void)pdfi_dict_knownget_number(ctx, page_dict, "UserUnit", &userunit);
351
0
        }
352
0
    }
353
0
    ctx->page.UserUnit = userunit;
354
355
0
    for (i=0;i<4;i++) {
356
0
        pdf_obj *box_obj = NULL;
357
358
0
        code = pdfi_array_get_no_store_R(ctx, a, i, &box_obj);
359
0
        if (code >= 0) {
360
0
            code = pdfi_obj_to_real(ctx, box_obj, &d[i]);
361
0
            pdfi_countdown(box_obj);
362
0
        }
363
364
0
        if (code < 0) {
365
0
            pdfi_countdown(a);
366
0
            pdfi_set_warning(ctx, code, NULL, W_PDF_BAD_MEDIABOX, "pdfi_get_media_size", NULL);
367
0
            code = gs_erasepage(ctx->pgs);
368
0
            return 0;
369
0
        }
370
0
        d[i] *= userunit;
371
0
    }
372
0
    pdfi_countdown(a);
373
374
0
    normalize_rectangle(d);
375
0
    memcpy(ctx->page.Size, d, 4 * sizeof(double));
376
377
0
    code = pdfi_dict_get_int(ctx, page_dict, "Rotate", &rotate);
378
379
0
    rotate = rotate % 360;
380
381
0
    switch(rotate) {
382
0
        default:
383
0
        case 0:
384
0
        case 360:
385
0
        case -180:
386
0
        case 180:
387
0
            fv[0] = (float)(d[2] - d[0]);
388
0
            fv[1] = (float)(d[3] - d[1]);
389
0
            break;
390
0
        case -90:
391
0
        case 90:
392
0
        case -270:
393
0
        case 270:
394
0
            fv[1] = (float)(d[2] - d[0]);
395
0
            fv[0] = (float)(d[3] - d[1]);
396
0
            break;
397
0
    }
398
399
0
    fa.persistent = false;
400
0
    fa.data = fv;
401
0
    fa.size = 2;
402
403
    /* ----- setup specific device parameters ----- */
404
0
    gs_c_param_list_write(&list, ctx->memory);
405
406
0
    code = param_write_float_array((gs_param_list *)&list, ".MediaSize", &fa);
407
0
    if (code >= 0)
408
0
    {
409
0
        gx_device *dev = gs_currentdevice(ctx->pgs);
410
411
0
        gs_c_param_list_read(&list);
412
0
        code = gs_putdeviceparams(dev, (gs_param_list *)&list);
413
0
        if (code < 0) {
414
0
            gs_c_param_list_release(&list);
415
0
            return code;
416
0
        }
417
0
    }
418
0
    gs_c_param_list_release(&list);
419
    /* ----- end setup specific device parameters ----- */
420
421
    /* Resets the default matrix to NULL before doing initgraphics, because
422
     * otherwise initgraphics would keep old matrix.
423
     * (see pdfi_set_ctm())
424
     */
425
0
    gs_setdefaultmatrix(ctx->pgs, NULL);
426
0
    gs_initgraphics(ctx->pgs);
427
428
0
    switch(rotate) {
429
0
        case 0:
430
0
            break;
431
0
        case -270:
432
0
        case 90:
433
0
            gs_translate(ctx->pgs, 0, fv[1]);
434
0
            gs_rotate(ctx->pgs, -90);
435
0
            break;
436
0
        case -180:
437
0
        case 180:
438
0
            gs_translate(ctx->pgs, fv[0], fv[1]);
439
0
            gs_rotate(ctx->pgs, 180);
440
0
            break;
441
0
        case -90:
442
0
        case 270:
443
0
            gs_translate(ctx->pgs, fv[0], 0);
444
0
            gs_rotate(ctx->pgs, 90);
445
0
            break;
446
0
        default:
447
0
            break;
448
0
    }
449
450
0
    if (do_crop) {
451
0
        bbox.p.x = d_crop[0] - d[0];
452
0
        bbox.p.y = d_crop[1] - d[1];
453
0
        bbox.q.x = d_crop[2] - d[0];
454
0
        bbox.q.y = d_crop[3] - d[1];
455
456
0
        code = gs_rectclip(ctx->pgs, &bbox, 1);
457
0
        if (code < 0)
458
0
            return code;
459
0
    }
460
461
0
    gs_translate(ctx->pgs, d[0] * -1, d[1] * -1);
462
463
0
    code = gs_erasepage(ctx->pgs);
464
0
    return 0;
465
0
}
466
467
/* Setup default transfer functions */
468
static void pdfi_setup_transfers(pdf_context *ctx)
469
6.07k
{
470
6.07k
    if (ctx->pgs->set_transfer.red == 0x00) {
471
6.07k
        ctx->page.DefaultTransfers[0].proc = gs_identity_transfer;
472
6.07k
        memset(ctx->page.DefaultTransfers[0].values, 0x00, transfer_map_size * sizeof(frac));
473
6.07k
    } else {
474
0
        ctx->page.DefaultTransfers[0].proc = ctx->pgs->set_transfer.red->proc;
475
0
        memcpy(ctx->page.DefaultTransfers[0].values, ctx->pgs->set_transfer.red->values, transfer_map_size * sizeof(frac));
476
0
    }
477
6.07k
    if (ctx->pgs->set_transfer.green == 0x00) {
478
6.07k
        ctx->page.DefaultTransfers[1].proc = gs_identity_transfer;
479
6.07k
        memset(ctx->page.DefaultTransfers[1].values, 0x00, transfer_map_size * sizeof(frac));
480
6.07k
    } else {
481
0
        ctx->page.DefaultTransfers[1].proc = ctx->pgs->set_transfer.green->proc;
482
0
        memcpy(ctx->page.DefaultTransfers[1].values, ctx->pgs->set_transfer.green->values, transfer_map_size * sizeof(frac));
483
0
    }
484
6.07k
    if (ctx->pgs->set_transfer.blue == 0x00) {
485
6.07k
        ctx->page.DefaultTransfers[2].proc = gs_identity_transfer;
486
6.07k
        memset(ctx->page.DefaultTransfers[2].values, 0x00, transfer_map_size * sizeof(frac));
487
6.07k
    } else {
488
0
        ctx->page.DefaultTransfers[2].proc = ctx->pgs->set_transfer.blue->proc;
489
0
        memcpy(ctx->page.DefaultTransfers[2].values, ctx->pgs->set_transfer.blue->values, transfer_map_size * sizeof(frac));
490
0
    }
491
6.07k
    if (ctx->pgs->set_transfer.gray == 0x00) {
492
0
        ctx->page.DefaultTransfers[3].proc = gs_identity_transfer;
493
0
        memset(ctx->page.DefaultTransfers[3].values, 0x00, transfer_map_size * sizeof(frac));
494
6.07k
    } else {
495
6.07k
        ctx->page.DefaultTransfers[3].proc = ctx->pgs->set_transfer.gray->proc;
496
6.07k
        memcpy(ctx->page.DefaultTransfers[3].values, ctx->pgs->set_transfer.gray->values, transfer_map_size * sizeof(frac));
497
6.07k
    }
498
6.07k
    if (ctx->pgs->black_generation == 0x00) {
499
0
        ctx->page.DefaultBG.proc = gs_identity_transfer;
500
0
        memset(ctx->page.DefaultBG.values, 0x00, transfer_map_size * sizeof(frac));
501
6.07k
    } else {
502
6.07k
        ctx->page.DefaultBG.proc = ctx->pgs->black_generation->proc;
503
6.07k
        memcpy(ctx->page.DefaultBG.values, ctx->pgs->black_generation->values, transfer_map_size * sizeof(frac));
504
6.07k
    }
505
6.07k
    if (ctx->pgs->undercolor_removal == 0x00) {
506
0
        ctx->page.DefaultUCR.proc = gs_identity_transfer;
507
0
        memset(ctx->page.DefaultUCR.values, 0x00, transfer_map_size * sizeof(frac));
508
6.07k
    } else {
509
6.07k
        ctx->page.DefaultUCR.proc = ctx->pgs->undercolor_removal->proc;
510
6.07k
        memcpy(ctx->page.DefaultUCR.values, ctx->pgs->undercolor_removal->values, transfer_map_size * sizeof(frac));
511
6.07k
    }
512
6.07k
}
513
514
/* Return a dictionary containing information about the page. Basic information is that
515
 * required to render the page; if extended is true then additionally contains an
516
 * array of spot ink names and an array of dictionaries each of which contains
517
 * information about a font used on the page. THis is normally only used for tools
518
 * like pdf_info.ps
519
 */
520
int pdfi_page_info(pdf_context *ctx, uint64_t page_num, pdf_dict **info, bool extended)
521
8.75k
{
522
8.75k
    int code = 0, i=0;
523
8.75k
    pdf_dict *page_dict = NULL, *info_dict = NULL;
524
8.75k
    pdf_array *fonts_array = NULL, *spots_array = NULL;
525
8.75k
    pdf_array *a = NULL;
526
8.75k
    pdf_obj *o = NULL;
527
8.75k
    bool known = false;
528
8.75k
    double dummy;
529
530
8.75k
    code = pdfi_page_get_dict(ctx, page_num, &page_dict);
531
8.75k
    if (code < 0)
532
2.65k
        return code;
533
534
6.09k
    if (code > 0) {
535
0
        code = gs_note_error(gs_error_unknownerror);
536
0
        goto done;
537
0
    }
538
539
6.09k
    code = pdfi_dict_alloc(ctx, 6, &info_dict);
540
6.09k
    if (code < 0)
541
0
        goto done;
542
543
6.09k
    pdfi_countup(info_dict);
544
545
6.09k
    if (extended)
546
0
        code = pdfi_check_page(ctx, page_dict, &fonts_array, &spots_array, false);
547
6.09k
    else
548
6.09k
        code = pdfi_check_page(ctx, page_dict, NULL, NULL, false);
549
6.09k
    if (code < 0)
550
0
        goto done;
551
552
6.09k
    if (spots_array != NULL) {
553
0
        code = pdfi_dict_put(ctx, info_dict, "Spots", (pdf_obj *)spots_array);
554
0
        if (code < 0)
555
0
            goto done;
556
0
        pdfi_countdown(spots_array);
557
0
    }
558
559
6.09k
    if (fonts_array != NULL) {
560
0
        code = pdfi_dict_put(ctx, info_dict, "Fonts", (pdf_obj *)fonts_array);
561
0
        if (code < 0)
562
0
            goto done;
563
0
        pdfi_countdown(fonts_array);
564
0
    }
565
566
6.09k
    code = pdfi_dict_get_type(ctx, page_dict, "MediaBox", PDF_ARRAY, (pdf_obj **)&a);
567
6.09k
    if (code < 0)
568
72
        pdfi_set_warning(ctx, code, NULL, W_PDF_BAD_MEDIABOX, "pdfi_page_info", NULL);
569
570
6.09k
    if (code >= 0) {
571
6.02k
        pdf_obj *box_obj = NULL;
572
573
30.1k
        for (i = 0;i < pdfi_array_size(a); i++) {
574
24.0k
            code = pdfi_array_get_no_store_R(ctx, a, i, &box_obj);
575
24.0k
            if (code >= 0) {
576
24.0k
                code = pdfi_obj_to_real(ctx, box_obj, &dummy);
577
24.0k
                pdfi_countdown(box_obj);
578
24.0k
            }
579
24.0k
            if (code < 0) {
580
4
                pdfi_set_warning(ctx, code, NULL, W_PDF_BAD_MEDIABOX, "pdfi_page_info", NULL);
581
4
                goto done;
582
4
            }
583
24.0k
        }
584
585
6.02k
        code = pdfi_dict_put(ctx, info_dict, "MediaBox", (pdf_obj *)a);
586
6.02k
        if (code < 0)
587
0
            goto done;
588
6.02k
        pdfi_countdown(a);
589
6.02k
        a = NULL;
590
6.02k
    }
591
592
6.09k
    code = pdfi_dict_get_type(ctx, page_dict, "ArtBox", PDF_ARRAY, (pdf_obj **)&a);
593
6.09k
    if (code >= 0) {
594
484
        pdf_obj *box_obj = NULL;
595
596
2.41k
        for (i = 0;i < pdfi_array_size(a); i++) {
597
1.93k
            code = pdfi_array_get_no_store_R(ctx, a, i, &box_obj);
598
1.93k
            if (code >= 0) {
599
1.93k
                code = pdfi_obj_to_real(ctx, box_obj, &dummy);
600
1.93k
                pdfi_countdown(box_obj);
601
1.93k
            }
602
1.93k
            if (code < 0)
603
1
                break;
604
1.93k
        }
605
484
        if (code >= 0) {
606
483
            code = pdfi_dict_put(ctx, info_dict, "ArtBox", (pdf_obj *)a);
607
483
            if (code < 0)
608
0
                goto done;
609
483
        }
610
484
        pdfi_countdown(a);
611
484
        a = NULL;
612
484
    }
613
614
6.09k
    code = pdfi_dict_get_type(ctx, page_dict, "CropBox", PDF_ARRAY, (pdf_obj **)&a);
615
6.09k
    if (code >= 0) {
616
1.83k
        pdf_obj *box_obj = NULL;
617
618
9.17k
        for (i = 0;i < pdfi_array_size(a); i++) {
619
7.34k
            code = pdfi_array_get_no_store_R(ctx, a, i, &box_obj);
620
7.34k
            if (code >= 0) {
621
7.34k
                code = pdfi_obj_to_real(ctx, box_obj, &dummy);
622
7.34k
                pdfi_countdown(box_obj);
623
7.34k
            }
624
7.34k
            if (code < 0)
625
6
                break;
626
7.34k
        }
627
1.83k
        if (code >= 0) {
628
1.83k
            code = pdfi_dict_put(ctx, info_dict, "CropBox", (pdf_obj *)a);
629
1.83k
            if (code < 0)
630
0
                goto done;
631
1.83k
        }
632
1.83k
        pdfi_countdown(a);
633
1.83k
        a = NULL;
634
1.83k
    }
635
636
6.09k
    code = pdfi_dict_get_type(ctx, page_dict, "TrimBox", PDF_ARRAY, (pdf_obj **)&a);
637
6.09k
    if (code >= 0) {
638
535
        pdf_obj *box_obj = NULL;
639
640
2.67k
        for (i = 0;i < pdfi_array_size(a); i++) {
641
2.14k
            code = pdfi_array_get_no_store_R(ctx, a, i, &box_obj);
642
2.14k
            if (code >= 0) {
643
2.14k
                code = pdfi_obj_to_real(ctx, box_obj, &dummy);
644
2.14k
                pdfi_countdown(box_obj);
645
2.14k
            }
646
2.14k
            if (code < 0)
647
0
                break;
648
2.14k
        }
649
535
        if (code >= 0) {
650
535
            code = pdfi_dict_put(ctx, info_dict, "TrimBox", (pdf_obj *)a);
651
535
            if (code < 0)
652
0
                goto done;
653
535
        }
654
535
        pdfi_countdown(a);
655
535
        a = NULL;
656
535
    }
657
658
6.09k
    code = pdfi_dict_get_type(ctx, page_dict, "BleedBox", PDF_ARRAY, (pdf_obj **)&a);
659
6.09k
    if (code >= 0) {
660
519
        pdf_obj *box_obj = NULL;
661
662
2.59k
        for (i = 0;i < pdfi_array_size(a); i++) {
663
2.07k
            code = pdfi_array_get_no_store_R(ctx, a, i, &box_obj);
664
2.07k
            if (code >= 0) {
665
2.07k
                code = pdfi_obj_to_real(ctx, box_obj, &dummy);
666
2.07k
                pdfi_countdown(box_obj);
667
2.07k
            }
668
2.07k
            if (code < 0)
669
0
                break;
670
2.07k
        }
671
519
        if (code >= 0) {
672
519
            code = pdfi_dict_put(ctx, info_dict, "BleedBox", (pdf_obj *)a);
673
519
            if (code < 0)
674
0
                goto done;
675
519
        }
676
519
        pdfi_countdown(a);
677
519
        a = NULL;
678
519
    }
679
6.09k
    code = 0;
680
681
6.09k
    code = pdfi_dict_get(ctx, page_dict, "Rotate", &o);
682
6.09k
    if (code >= 0) {
683
2.08k
        if (pdfi_type_of(o) == PDF_INT || pdfi_type_of(o) == PDF_REAL) {
684
2.08k
            code = pdfi_dict_put(ctx, info_dict, "Rotate", o);
685
2.08k
            if (code < 0)
686
0
                goto done;
687
2.08k
        }
688
2.08k
        pdfi_countdown(o);
689
2.08k
    }
690
691
6.09k
    code = pdfi_dict_get(ctx, page_dict, "UserUnit", &o);
692
6.09k
    if (code >= 0) {
693
25
        if (pdfi_type_of(o) == PDF_INT || pdfi_type_of(o) == PDF_REAL) {
694
25
            code = pdfi_dict_put(ctx, info_dict, "UserUnit", o);
695
25
            if (code < 0)
696
0
                goto done;
697
25
        }
698
25
        pdfi_countdown(o);
699
25
    }
700
701
6.09k
    if (ctx->page.has_transparency)
702
1.00k
        code = pdfi_dict_put(ctx, info_dict, "UsesTransparency", PDF_TRUE_OBJ);
703
5.09k
    else
704
5.09k
        code = pdfi_dict_put(ctx, info_dict, "UsesTransparency", PDF_FALSE_OBJ);
705
6.09k
    if (code < 0)
706
0
        goto done;
707
708
6.09k
    code = pdfi_dict_known(ctx, page_dict, "Annots", &known);
709
6.09k
    if (code >= 0 && known)
710
1.29k
        code = pdfi_dict_put(ctx, info_dict, "Annots", PDF_TRUE_OBJ);
711
4.80k
    else
712
4.80k
        code = pdfi_dict_put(ctx, info_dict, "Annots", PDF_FALSE_OBJ);
713
6.09k
    if (code < 0)
714
0
        goto done;
715
716
6.09k
    code = pdfi_object_alloc(ctx, PDF_INT, 0, &o);
717
6.09k
    if (code >= 0) {
718
6.09k
        pdfi_countup(o);
719
6.09k
        ((pdf_num *)o)->value.i = ctx->page.num_spots;
720
6.09k
        code = pdfi_dict_put(ctx, info_dict, "NumSpots", o);
721
6.09k
        pdfi_countdown(o);
722
6.09k
        o = NULL;
723
6.09k
        if (code < 0)
724
0
            goto done;
725
6.09k
    }
726
727
6.09k
done:
728
6.09k
    if (code < 0) {
729
4
        pdfi_countdown(info_dict);
730
4
        info_dict = NULL;
731
4
        *info = NULL;
732
4
    } else
733
6.09k
        *info = info_dict;
734
735
6.09k
    pdfi_countdown(a);
736
6.09k
    pdfi_countdown(page_dict);
737
6.09k
    return code;
738
6.09k
}
739
740
int pdfi_page_get_dict(pdf_context *ctx, uint64_t page_num, pdf_dict **dict)
741
14.8k
{
742
14.8k
    int code = 0;
743
14.8k
    uint64_t page_offset = 0;
744
745
14.8k
    code = pdfi_loop_detector_mark(ctx);
746
14.8k
    if (code < 0)
747
0
        return code;
748
749
14.8k
    if (ctx->PagesTree == NULL) {
750
79
        pdf_obj *o = NULL;
751
79
        pdf_name *n = NULL;
752
        /* The only way this should be true is if the Pages entry in the Root dictionary
753
         * points to a single instance of a Page dictionary, instead of to a Pages dictionary.
754
         * in which case, simply retrieve that dictionary and return.
755
         */
756
79
        code = pdfi_dict_get(ctx, ctx->Root, "Pages", &o);
757
79
        if (code < 0)
758
0
            goto page_error;
759
79
        if (pdfi_type_of(o) != PDF_DICT) {
760
0
            code = gs_note_error(gs_error_typecheck);
761
0
            goto page_error;
762
0
        }
763
79
        code = pdfi_dict_get_type(ctx, (pdf_dict *)o, "Type", PDF_NAME, (pdf_obj **)&n);
764
79
        if (code == 0) {
765
79
            if(pdfi_name_is(n, "Page")) {
766
79
                *dict = (pdf_dict *)o;
767
79
                pdfi_countup(*dict);
768
79
            } else
769
0
                code = gs_note_error(gs_error_undefined);
770
79
        }
771
79
page_error:
772
79
        pdfi_loop_detector_cleartomark(ctx);
773
79
        pdfi_countdown(o);
774
79
        pdfi_countdown(n);
775
79
        return code;
776
79
    }
777
778
14.7k
    code = pdfi_loop_detector_add_object(ctx, ctx->PagesTree->object_num);
779
14.7k
    if (code < 0)
780
0
        goto exit;
781
782
14.7k
    code = pdfi_get_page_dict(ctx, ctx->PagesTree, page_num, &page_offset, dict, NULL);
783
14.7k
    if (code > 0)
784
0
        code = gs_error_unknownerror;
785
786
    /* Cache the page_dict number in page_array */
787
14.7k
    if (*dict)
788
12.0k
        ctx->page_array[page_num] = (*dict)->object_num;
789
790
14.7k
 exit:
791
14.7k
    pdfi_loop_detector_cleartomark(ctx);
792
14.7k
    return code;
793
14.7k
}
794
795
/* Find the page number that corresponds to a page dictionary
796
 * Uses page_array cache to minimize the number of times a page_dict needs to
797
 * be fetched, because this is expensive.
798
 */
799
int pdfi_page_get_number(pdf_context *ctx, pdf_dict *target_dict, uint64_t *page_num)
800
0
{
801
0
    uint64_t i;
802
0
    int code = 0;
803
0
    pdf_dict *page_dict = NULL;
804
0
    uint32_t object_num;
805
806
0
    for (i=0; i<ctx->num_pages; i++) {
807
        /* If the page has been processed before, then its object_num should already
808
         * be cached in the page_array, so check that first
809
         */
810
0
        object_num = ctx->page_array[i];
811
0
        if (object_num == 0) {
812
            /* It wasn't cached, so this will cache it */
813
0
            code = pdfi_page_get_dict(ctx, i, &page_dict);
814
0
            if (code < 0)
815
0
                continue;
816
0
            object_num = ctx->page_array[i];
817
0
        }
818
0
        if (target_dict->object_num == object_num) {
819
0
            *page_num = i;
820
0
            goto exit;
821
0
        }
822
823
0
        pdfi_countdown(page_dict);
824
0
        page_dict = NULL;
825
0
    }
826
827
0
    code = gs_note_error(gs_error_undefined);
828
829
0
 exit:
830
0
    pdfi_countdown(page_dict);
831
0
    return code;
832
0
}
833
834
static void release_page_DefaultSpaces(pdf_context *ctx)
835
12.1k
{
836
12.1k
    if (ctx->page.DefaultGray_cs != NULL) {
837
0
        if (ctx->page.DefaultGray_cs->interpreter_data != NULL) {
838
0
            pdf_obj *o = (pdf_obj *)(ctx->page.DefaultGray_cs->interpreter_data);
839
0
            if (o != NULL && pdfi_type_of(o) == PDF_NAME) {
840
0
                pdfi_countdown(o);
841
0
                ctx->page.DefaultGray_cs->interpreter_data = NULL;
842
0
            }
843
0
        }
844
0
        rc_decrement(ctx->page.DefaultGray_cs, "pdfi_page_render");
845
0
        ctx->page.DefaultGray_cs = NULL;
846
0
    }
847
12.1k
    if (ctx->page.DefaultRGB_cs != NULL) {
848
52
        if (ctx->page.DefaultRGB_cs->interpreter_data != NULL) {
849
52
            pdf_obj *o = (pdf_obj *)(ctx->page.DefaultRGB_cs->interpreter_data);
850
52
            if (o != NULL && pdfi_type_of(o) == PDF_NAME) {
851
6
                pdfi_countdown(o);
852
6
                ctx->page.DefaultRGB_cs->interpreter_data = NULL;
853
6
            }
854
52
        }
855
52
        rc_decrement(ctx->page.DefaultRGB_cs, "pdfi_page_render");
856
52
        ctx->page.DefaultRGB_cs = NULL;
857
52
    }
858
12.1k
    if (ctx->page.DefaultCMYK_cs != NULL) {
859
2
        if (ctx->page.DefaultCMYK_cs->interpreter_data != NULL) {
860
2
            pdf_obj *o = (pdf_obj *)(ctx->page.DefaultCMYK_cs->interpreter_data);
861
2
            if (o != NULL && pdfi_type_of(o) == PDF_NAME) {
862
0
                pdfi_countdown(o);
863
0
                ctx->page.DefaultCMYK_cs->interpreter_data = NULL;
864
0
            }
865
2
        }
866
2
        rc_decrement(ctx->page.DefaultCMYK_cs, "pdfi_page_render");
867
2
        ctx->page.DefaultCMYK_cs = NULL;
868
2
    }
869
12.1k
}
870
871
static int setup_page_DefaultSpaces(pdf_context *ctx, pdf_dict *page_dict)
872
6.07k
{
873
    /* First off, discard any dangling Default* colour spaces, just in case. */
874
6.07k
    release_page_DefaultSpaces(ctx);
875
876
6.07k
    return(pdfi_setup_DefaultSpaces(ctx, page_dict));
877
6.07k
}
878
879
int pdfi_page_render(pdf_context *ctx, uint64_t page_num, bool init_graphics)
880
6.07k
{
881
6.07k
    int code, code1=0;
882
6.07k
    pdf_dict *page_dict = NULL;
883
6.07k
    bool page_group_known = false;
884
6.07k
    pdf_dict *group_dict = NULL;
885
6.07k
    bool page_dict_error = false;
886
6.07k
    bool need_pdf14 = false; /* true if the device is needed and was successfully pushed */
887
6.07k
    int trans_depth = 0; /* -1 means special mode for transparency simulation */
888
889
6.07k
    if (page_num > ctx->num_pages)
890
0
        return_error(gs_error_rangecheck);
891
892
6.07k
    if (ctx->args.pdfdebug)
893
0
        outprintf(ctx->memory, "%% Processing Page %"PRIi64" content stream\n", page_num + 1);
894
895
6.07k
    code = pdfi_page_get_dict(ctx, page_num, &page_dict);
896
6.07k
    if (code < 0) {
897
0
        char extra_info[256];
898
899
0
        page_dict_error = true;
900
0
        gs_snprintf(extra_info, sizeof(extra_info), "*** ERROR: Page %ld has invalid Page dict, skipping\n", page_num+1);
901
0
        if (code == gs_error_VMerror ||
902
0
        ((code = pdfi_set_error_stop(ctx, code, NULL, E_PDF_PAGEDICTERROR, "pdfi_page_render", extra_info)) < 0)) {
903
0
            goto exit3;
904
0
        }
905
0
    }
906
907
6.07k
    code = pdfi_check_page(ctx, page_dict, NULL, NULL, init_graphics);
908
6.07k
    if (code < 0)
909
0
        goto exit3;
910
911
6.07k
    if (ctx->args.pdfdebug) {
912
0
        dbgmprintf2(ctx->memory, "Current page %ld transparency setting is %d", page_num+1,
913
0
                ctx->page.has_transparency);
914
915
0
        if (ctx->device_state.spot_capable)
916
0
            dbgmprintf1(ctx->memory, ", spots=%d\n", ctx->page.num_spots);
917
0
        else
918
0
            dbgmprintf(ctx->memory, "\n");
919
0
    }
920
921
6.07k
    code = pdfi_dict_knownget_type(ctx, page_dict, "Group", PDF_DICT, (pdf_obj **)&group_dict);
922
    /* Ignore errors retrieving the Group dictionary, we will just ignore it. This allows us
923
     * to handle files such as Bug #705206 where the Group dictionary is a free object in a
924
     * compressed object stream.
925
     */
926
6.07k
    if (code < 0 && (code = pdfi_set_error_stop(ctx, code, NULL, E_BAD_GROUP_DICT, "pdfi_page_render", NULL)) < 0)
927
0
        goto exit2;
928
929
6.07k
    if (group_dict != NULL)
930
869
        page_group_known = true;
931
932
6.07k
    pdfi_countdown(ctx->page.CurrentPageDict);
933
6.07k
    ctx->page.CurrentPageDict = page_dict;
934
6.07k
    pdfi_countup(ctx->page.CurrentPageDict);
935
936
    /* In case we don't call pdfi_set_media_size, which sets this up.
937
     * We shouldn't ever use it in that case, but best to be safe.
938
     */
939
6.07k
    ctx->page.UserUnit = 1.0f;
940
    /* If we are being called from the PDF interpreter then
941
     * we need to set up the page  and the default graphics state
942
     * but if we are being called from PostScript we do *not*
943
     * want to alter any of the graphics state or the media size.
944
     */
945
    /* TODO: I think this is a mix of things we might need to
946
     * still be setting up.
947
     * (for example, I noticed the blendmode and moved it outside the if)
948
     */
949
6.07k
    if (init_graphics) {
950
0
        code = pdfi_set_media_size(ctx, page_dict);
951
0
        if (code < 0)
952
0
            goto exit2;
953
954
0
        pdfi_set_ctm(ctx);
955
956
6.07k
    } else {
957
        /* Gets ctx->page.Size setup correctly
958
         * TODO: Probably not right if the page is rotated?
959
         * page.Size is needed by the transparency code,
960
         * not sure where else it might be used, if anywhere.
961
         */
962
6.07k
        pdfi_get_media_size(ctx, page_dict);
963
6.07k
    }
964
965
    /* Write the various CropBox, TrimBox etc to the device */
966
6.07k
    pdfi_pdfmark_write_boxes(ctx, page_dict);
967
968
6.07k
    code = setup_page_DefaultSpaces(ctx, page_dict);
969
6.07k
    if (code < 0)
970
0
        goto exit2;
971
972
6.07k
    pdfi_setup_transfers(ctx);
973
974
    /* Set whether device needs OP support
975
     * This needs to be before transparency device is pushed, if applicable
976
     */
977
6.07k
    pdfi_trans_set_needs_OP(ctx);
978
6.07k
    pdfi_oc_init(ctx);
979
980
6.07k
    code = pdfi_gsave(ctx);
981
6.07k
    if (code < 0)
982
0
        goto exit2;
983
984
    /* Figure out if pdf14 device is needed.
985
     * This can be either for normal transparency deviceN, or because we are using
986
     * Overprint=/simulate for other devices
987
     */
988
6.07k
    if (ctx->page.has_transparency) {
989
996
        need_pdf14 = true;
990
996
        if (ctx->page.simulate_op)
991
0
            trans_depth = -1;
992
5.07k
    } else {
993
        /* This is the case where we are simulating overprint without transparency */
994
5.07k
        if (ctx->page.simulate_op) {
995
0
            need_pdf14 = true;
996
0
            trans_depth = -1;
997
0
        }
998
5.07k
    }
999
6.07k
    if (need_pdf14) {
1000
        /* We don't retain the PDF14 device */
1001
996
        code = gs_push_pdf14trans_device(ctx->pgs, false, false, trans_depth, ctx->page.num_spots);
1002
996
        if (code >= 0) {
1003
996
            if (ctx->page.has_transparency && page_group_known) {
1004
402
                code = pdfi_trans_begin_page_group(ctx, page_dict, group_dict);
1005
                /* If setting the page group failed for some reason, abandon the page group,
1006
                 *  but continue with the page
1007
                 */
1008
402
                if (code < 0)
1009
12
                    page_group_known = false;
1010
402
            }
1011
996
        } else {
1012
            /* Couldn't push the transparency compositor.
1013
             * This is probably fatal, but attempt to recover by abandoning transparency
1014
             */
1015
0
            ctx->page.has_transparency = false;
1016
0
            need_pdf14 = false;
1017
0
        }
1018
996
    }
1019
1020
    /* Init a base_pgs graphics state for Patterns
1021
     * (this has to be after transparency device pushed, if applicable)
1022
     */
1023
6.07k
    pdfi_set_DefaultQState(ctx, ctx->pgs);
1024
1025
    /* Render one page (including annotations) */
1026
6.07k
    if (!ctx->args.QUIET)
1027
0
        outprintf(ctx->memory, "Page %"PRId64"\n", page_num + 1);
1028
1029
6.07k
    code = pdfi_process_one_page(ctx, page_dict);
1030
1031
6.07k
    if (need_pdf14 && ctx->page.has_transparency && page_group_known) {
1032
390
        code1 = pdfi_trans_end_group(ctx);
1033
390
    }
1034
1035
6.07k
    if (need_pdf14) {
1036
996
        if (code1 < 0) {
1037
0
            (void)gs_abort_pdf14trans_device(ctx->pgs);
1038
0
            code = code1;
1039
0
            goto exit1;
1040
0
        }
1041
1042
996
        code1 = gs_pop_pdf14trans_device(ctx->pgs, false);
1043
996
        if (code1 < 0) {
1044
0
            code = code1;
1045
0
            goto exit1;
1046
0
        }
1047
996
    }
1048
1049
6.07k
exit1:
1050
6.07k
    pdfi_free_DefaultQState(ctx);
1051
6.07k
    pdfi_grestore(ctx);
1052
1053
6.07k
exit2:
1054
6.07k
    pdfi_countdown(ctx->page.CurrentPageDict);
1055
6.07k
    ctx->page.CurrentPageDict = NULL;
1056
1057
6.07k
exit3:
1058
6.07k
    pdfi_countdown(page_dict);
1059
6.07k
    pdfi_countdown(group_dict);
1060
1061
6.07k
    release_page_DefaultSpaces(ctx);
1062
1063
    /* Flush any pattern tiles. We don't want to (potentially) return to PostScript
1064
     * with any pattern tiles referencing our objects, in case the garbager runs.
1065
     */
1066
6.07k
    gx_pattern_cache_flush(gstate_pattern_cache(ctx->pgs));
1067
    /* We could be smarter, but for now.. purge for each page */
1068
6.07k
    pdfi_purge_cache_resource_font(ctx);
1069
1070
6.07k
    if (code == 0 || (!ctx->args.pdfstoponerror && code != gs_error_pdf_stackoverflow))
1071
6.07k
        if (!page_dict_error && ctx->finish_page != NULL)
1072
0
            code = ctx->finish_page(ctx);
1073
6.07k
    return code;
1074
6.07k
}