Coverage Report

Created: 2026-09-14 07:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ghostpdl/psi/zdevice2.c
Line
Count
Source
1
/* Copyright (C) 2001-2026 Artifex Software, Inc.
2
   All Rights Reserved.
3
4
   This software is provided AS-IS with no warranty, either express or
5
   implied.
6
7
   This software is distributed under license and may not be copied,
8
   modified or distributed except as expressly authorized under the terms
9
   of the license contained in the file LICENSE in this distribution.
10
11
   Refer to licensing information at http://www.artifex.com or contact
12
   Artifex Software, Inc.,  39 Mesa Street, Suite 108A, San Francisco,
13
   CA 94129, USA, for further information.
14
*/
15
16
17
/* Level 2 device operators */
18
#include "math_.h"
19
#include "memory_.h"
20
#include "ghost.h"
21
#include "oper.h"
22
#include "dstack.h"   /* for dict_find_name */
23
#include "estack.h"
24
#include "idict.h"
25
#include "idparam.h"
26
#include "igstate.h"
27
#include "iname.h"
28
#include "iutil.h"
29
#include "isave.h"
30
#include "store.h"
31
#include "gxdevice.h"
32
#include "gsstate.h"
33
34
/* Exported for zfunc4.c */
35
int z2copy(i_ctx_t *);
36
37
/* Forward references */
38
static int z2copy_gstate(i_ctx_t *);
39
static int push_callout(i_ctx_t *, const char *);
40
41
/* Extend the `copy' operator to deal with gstates. */
42
/* This is done with a hack -- we know that gstates are the only */
43
/* t_astruct subtype that implements copy. */
44
/* We export this for recognition in FunctionType 4 functions. */
45
int
46
z2copy(i_ctx_t *i_ctx_p)
47
410M
{
48
410M
    os_ptr op = osp;
49
410M
    int code = zcopy(i_ctx_p);
50
51
410M
    if (code >= 0)
52
410M
        return code;
53
71
    if (!r_has_type(op, t_astruct))
54
71
        return code;
55
0
    return z2copy_gstate(i_ctx_p);
56
71
}
57
58
/* - .currentshowpagecount <count> true */
59
/* - .currentshowpagecount false */
60
static int
61
zcurrentshowpagecount(i_ctx_t *i_ctx_p)
62
1.72M
{
63
1.72M
    os_ptr op = osp;
64
1.72M
    gx_device *dev1, *dev = gs_currentdevice(igs);
65
66
1.72M
    if ((*dev_proc(dev, get_page_device))(dev) == 0) {
67
132
        push(1);
68
132
        make_false(op);
69
1.72M
    } else {
70
1.72M
        dev1 = (*dev_proc(dev, get_page_device))(dev);
71
1.72M
        push(2);
72
1.72M
        make_int(op - 1, dev1->ShowpageCount);
73
1.72M
        make_true(op);
74
1.72M
    }
75
1.72M
    return 0;
76
1.72M
}
77
78
/* - .currentpagedevice <dict> <bool> */
79
static int
80
zcurrentpagedevice(i_ctx_t *i_ctx_p)
81
4.96M
{
82
4.96M
    os_ptr op = osp;
83
4.96M
    gx_device *dev = gs_currentdevice(igs);
84
85
4.96M
    push(2);
86
4.96M
    if ((*dev_proc(dev, get_page_device))(dev) != 0) {
87
4.96M
        op[-1] = istate->pagedevice;
88
4.96M
        make_true(op);
89
4.96M
    } else {
90
307
        make_null(op - 1);
91
307
        make_false(op);
92
307
    }
93
4.96M
    return 0;
94
4.96M
}
95
96
/* <local_dict|null> .setpagedevice - */
97
static int
98
zsetpagedevice(i_ctx_t *i_ctx_p)
99
2.38M
{
100
2.38M
    os_ptr op = osp;
101
2.38M
    int code;
102
103
2.38M
    check_op(1);
104
/******
105
    if ( igs->in_cachedevice )
106
        return_error(gs_error_undefined);
107
 ******/
108
2.38M
    if (r_has_type(op, t_dictionary)) {
109
2.38M
        check_dict_read(*op);
110
#if 0 /****************/
111
        /*
112
         * In order to avoid invalidaccess errors on setpagedevice,
113
         * the dictionary must be allocated in local VM.
114
         */
115
        if (!(r_is_local(op)))
116
            return_error(gs_error_invalidaccess);
117
        /* Make the dictionary read-only. */
118
        /* This is now done in PostScript in the .postinstall procedure (gs_setpd.ps)
119
         * in order to avoid having to use .forceput to override the access
120
         * if a new /Install procedure fails.
121
         */
122
        code = zreadonly(i_ctx_p);
123
        if (code < 0)
124
            return code;
125
#endif  /****************/
126
2.38M
    } else {
127
101
        check_type(*op, t_null);
128
101
    }
129
2.38M
    istate->pagedevice = *op;
130
2.38M
    pop(1);
131
2.38M
    return 0;
132
2.38M
}
133
134
/* Default Install/BeginPage/EndPage procedures */
135
/* that just call the procedure in the device. */
136
137
/* - .callinstall - */
138
static int
139
zcallinstall(i_ctx_t *i_ctx_p)
140
595k
{
141
595k
    gx_device *dev = gs_currentdevice(igs);
142
143
595k
    if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
144
595k
        int code = (*dev->page_procs.install) (dev, igs);
145
146
595k
        if (code < 0)
147
0
            return code;
148
595k
    }
149
595k
    return 0;
150
595k
}
151
152
/* <showpage_count> .callbeginpage - */
153
static int
154
zcallbeginpage(i_ctx_t *i_ctx_p)
155
783k
{
156
783k
    os_ptr op = osp;
157
783k
    gx_device *dev = gs_currentdevice(igs);
158
159
783k
    check_op(1);
160
783k
    check_type(*op, t_integer);
161
783k
    if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
162
783k
        int code = (*dev->page_procs.begin_page)(dev, igs);
163
164
783k
        if (code < 0)
165
0
            return code;
166
783k
    }
167
783k
    pop(1);
168
783k
    return 0;
169
783k
}
170
171
/* <showpage_count> <reason_int> .callendpage <flush_bool> */
172
static int
173
zcallendpage(i_ctx_t *i_ctx_p)
174
937k
{
175
937k
    os_ptr op = osp;
176
937k
    gx_device *dev = gs_currentdevice(igs);
177
937k
    int code;
178
179
937k
    check_op(2);
180
937k
    check_type(op[-1], t_integer);
181
937k
    check_type(*op, t_integer);
182
937k
    if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
183
937k
        code = (*dev->page_procs.end_page)(dev, (int)op->value.intval, igs);
184
937k
        if (code < 0)
185
0
            return code;
186
937k
        if (code > 1)
187
0
            return_error(gs_error_rangecheck);
188
937k
    } else {
189
0
        code = (op->value.intval == 2 ? 0 : 1);
190
0
    }
191
937k
    make_bool(op - 1, code);
192
937k
    pop(1);
193
937k
    return 0;
194
937k
}
195
196
/* ------ Wrappers for operators that save the graphics state. ------ */
197
198
/* When saving the state with the current device a page device, */
199
/* we need to make sure that the page device dictionary exists */
200
/* so that grestore can use it to reset the device parameters. */
201
/* This may have significant performance consequences, but we don't see */
202
/* any way around it. */
203
204
/* Check whether we need to call out to create the page device dictionary. */
205
static bool
206
save_page_device(gs_gstate *pgs)
207
5.72M
{
208
5.72M
    return
209
5.72M
        (r_has_type(&gs_int_gstate(pgs)->pagedevice, t_null) &&
210
294k
         (*dev_proc(gs_currentdevice(pgs), get_page_device))(gs_currentdevice(pgs)) != 0);
211
5.72M
}
212
213
/* - gsave - */
214
static int
215
z2gsave(i_ctx_t *i_ctx_p)
216
4.62M
{
217
4.62M
    if (!save_page_device(igs))
218
4.47M
        return gs_gsave(igs);
219
146k
    return push_callout(i_ctx_p, "%gsavepagedevice");
220
4.62M
}
221
222
/* - save - */
223
static int
224
z2save(i_ctx_t *i_ctx_p)
225
1.09M
{
226
1.09M
    if (!save_page_device(igs))
227
951k
        return zsave(i_ctx_p);
228
147k
    return push_callout(i_ctx_p, "%savepagedevice");
229
1.09M
}
230
231
/* - gstate <gstate> */
232
static int
233
z2gstate(i_ctx_t *i_ctx_p)
234
262
{
235
262
    if (!save_page_device(igs))
236
262
        return zgstate(i_ctx_p);
237
0
    return push_callout(i_ctx_p, "%gstatepagedevice");
238
262
}
239
240
/* <gstate1> <gstate2> copy <gstate2> */
241
static int
242
z2copy_gstate(i_ctx_t *i_ctx_p)
243
0
{
244
0
    os_ptr op = osp;
245
0
    check_op(2);
246
0
    if (!save_page_device(igs))
247
0
        return zcopy_gstate(i_ctx_p);
248
0
    return push_callout(i_ctx_p, "%copygstatepagedevice");
249
0
}
250
251
/* <gstate> currentgstate <gstate> */
252
static int
253
z2currentgstate(i_ctx_t *i_ctx_p)
254
18
{
255
18
    os_ptr op = osp;
256
18
    check_op(1);
257
8
    if (!save_page_device(igs))
258
8
        return zcurrentgstate(i_ctx_p);
259
0
    return push_callout(i_ctx_p, "%currentgstatepagedevice");
260
8
}
261
262
/* ------ Wrappers for operators that reset the graphics state. ------ */
263
264
/* Check whether we need to call out to restore the page device. */
265
static int
266
restore_page_device(i_ctx_t *i_ctx_p, const gs_gstate * pgs_old, const gs_gstate * pgs_new)
267
9.24M
{
268
9.24M
    gx_device *dev_old = gs_currentdevice(pgs_old);
269
9.24M
    gx_device *dev_new;
270
9.24M
    gx_device *dev_t1;
271
9.24M
    gx_device *dev_t2;
272
9.24M
    bool samepagedevice = obj_eq(dev_old->memory, &gs_int_gstate(pgs_old)->pagedevice,
273
9.24M
        &gs_int_gstate(pgs_new)->pagedevice);
274
9.24M
    bool LockSafetyParams = dev_old->LockSafetyParams;
275
276
9.24M
    if ((dev_t1 = (*dev_proc(dev_old, get_page_device)) (dev_old)) == 0)
277
84.3k
        return 0;
278
    /* If we are going to putdeviceparams in a callout, we need to */
279
    /* unlock temporarily.  The device will be re-locked as needed */
280
    /* by putdeviceparams from the pgs_old->pagedevice dict state. */
281
9.16M
    if (!samepagedevice)
282
84.2k
        dev_old->LockSafetyParams = false;
283
9.16M
    dev_new = gs_currentdevice(pgs_new);
284
9.16M
    if (dev_old != dev_new) {
285
896
        if ((dev_t2 = (*dev_proc(dev_new, get_page_device)) (dev_new)) == 0)
286
0
            samepagedevice = true;
287
896
        else if (dev_t1 != dev_t2)
288
896
            samepagedevice = false;
289
896
    }
290
291
9.16M
    if (LockSafetyParams) {
292
0
        const int required_ops = 512;
293
0
        const int required_es = 32;
294
295
        /* The %grestorepagedevice must complete: the biggest danger
296
           is operand stack overflow. As we use get/putdeviceparams
297
           that means pushing all the device params onto the stack,
298
           pdfwrite having by far the largest number of parameters
299
           at (currently) 212 key/value pairs - thus needing (currently)
300
           424 entries on the op stack. Allowing for working stack
301
           space, and safety margin.....
302
         */
303
0
        if (required_ops + ref_stack_count(&o_stack) >= ref_stack_max_count(&o_stack)) {
304
0
           gs_currentdevice(pgs_old)->LockSafetyParams = LockSafetyParams;
305
0
           return_error(gs_error_stackoverflow);
306
0
        }
307
        /* We also want enough exec stack space - 32 is an overestimate of
308
           what we need to complete the Postscript call out.
309
         */
310
0
        if (required_es + ref_stack_count(&e_stack) >= ref_stack_max_count(&e_stack)) {
311
0
           gs_currentdevice(pgs_old)->LockSafetyParams = LockSafetyParams;
312
0
           return_error(gs_error_execstackoverflow);
313
0
        }
314
0
    }
315
    /*
316
     * The current implementation of setpagedevice just sets new
317
     * parameters in the same device object, so we have to check
318
     * whether the page device dictionaries are the same.
319
     */
320
9.16M
    return samepagedevice ? 0 : 1;
321
9.16M
}
322
323
/* - grestore - */
324
static int
325
z2grestore(i_ctx_t *i_ctx_p)
326
9.03M
{
327
9.03M
    int code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
328
9.03M
    if (code < 0) return code;
329
330
9.03M
    if (code == 0)
331
9.03M
        return gs_grestore(igs);
332
4.47k
    return push_callout(i_ctx_p, "%grestorepagedevice");
333
9.03M
}
334
335
/* - grestoreall - */
336
static int
337
z2grestoreall(i_ctx_t *i_ctx_p)
338
253
{
339
258
    for (;;) {
340
258
        int code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
341
258
        if (code < 0) return code;
342
258
        if (code == 0) {
343
128
            bool done = !gs_gstate_saved(gs_gstate_saved(igs));
344
345
128
            gs_grestore(igs);
346
128
            if (done)
347
123
                break;
348
128
        } else
349
130
            return push_callout(i_ctx_p, "%grestoreallpagedevice");
350
258
    }
351
123
    return 0;
352
253
}
353
/* This is the Level 2+ variant of restore - which adds restoring
354
   of the page device to the Level 1 variant in zvmem.c.
355
   Previous this restored the device state before calling zrestore.c
356
   which validated operands etc, meaning a restore could error out
357
   partially complete.
358
   The operand checking, and actual VM restore are now in two functions
359
   so they can called separately thus, here, we can do as much
360
   checking as possible, before embarking on actual changes
361
 */
362
/* <save> restore - */
363
static int
364
z2restore(i_ctx_t *i_ctx_p)
365
181k
{
366
181k
    alloc_save_t *asave;
367
181k
    bool saveLockSafety = gs_currentdevice_inline(igs)->LockSafetyParams;
368
181k
    int code = restore_check_save(i_ctx_p, &asave);
369
370
181k
    if (code < 0) return code;
371
372
208k
    while (gs_gstate_saved(gs_gstate_saved(igs))) {
373
26.7k
        code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
374
26.7k
        if (code < 0) return code;
375
26.7k
        if (code > 0)
376
0
            return push_callout(i_ctx_p, "%restore1pagedevice");
377
26.7k
        gs_grestore(igs);
378
26.7k
    }
379
181k
    code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs));
380
181k
    if (code < 0) return code;
381
181k
    if (code > 0)
382
79.6k
        return push_callout(i_ctx_p, "%restorepagedevice");
383
384
101k
    code = dorestore(i_ctx_p, asave);
385
386
101k
    if (code < 0) {
387
        /* An error here is basically fatal, but....
388
           restore_page_device() has to set LockSafetyParams false so it can
389
           configure the restored device correctly - in normal operation, that
390
           gets reset by that configuration. If we hit an error, though, that
391
           may not happen -  at least ensure we keep the setting through the
392
           error.
393
         */
394
0
        gs_currentdevice_inline(igs)->LockSafetyParams = saveLockSafety;
395
0
    }
396
101k
    return code;
397
181k
}
398
399
/* <gstate> setgstate - */
400
static int
401
z2setgstate(i_ctx_t *i_ctx_p)
402
18
{
403
18
    os_ptr op = osp;
404
18
    int code;
405
406
18
    check_op(1);
407
10
    check_stype(*op, st_igstate_obj);
408
6
    code = restore_page_device(i_ctx_p, igs, igstate_ptr(op));
409
6
    if (code < 0) return code;
410
6
    if (code == 0)
411
6
        return zsetgstate(i_ctx_p);
412
0
    return push_callout(i_ctx_p, "%setgstatepagedevice");
413
6
}
414
415
/* ------ Initialization procedure ------ */
416
417
const op_def zdevice2_l2_op_defs[] =
418
{
419
    op_def_begin_level2(),
420
    {"0.currentshowpagecount", zcurrentshowpagecount},
421
    {"0.currentpagedevice", zcurrentpagedevice},
422
    {"1.setpagedevice", zsetpagedevice},
423
                /* Note that the following replace prior definitions */
424
                /* in the indicated files: */
425
    {"1copy", z2copy},    /* zdps1.c */
426
    {"0gsave", z2gsave},  /* zgstate.c */
427
    {"0save", z2save},    /* zvmem.c */
428
    {"0gstate", z2gstate},  /* zdps1.c */
429
    {"1currentgstate", z2currentgstate},  /* zdps1.c */
430
    {"0grestore", z2grestore},  /* zgstate.c */
431
    {"0grestoreall", z2grestoreall},  /* zgstate.c */
432
    {"1restore", z2restore},  /* zvmem.c */
433
    {"1setgstate", z2setgstate},  /* zdps1.c */
434
                /* Default Install/BeginPage/EndPage procedures */
435
                /* that just call the procedure in the device. */
436
    {"0.callinstall", zcallinstall},
437
    {"1.callbeginpage", zcallbeginpage},
438
    {"2.callendpage", zcallendpage},
439
    op_def_end(0)
440
};
441
442
/* ------ Internal routines ------ */
443
444
/* Call out to a PostScript procedure. */
445
static int
446
push_callout(i_ctx_t *i_ctx_p, const char *callout_name)
447
378k
{
448
378k
    int code;
449
450
378k
    check_estack(1);
451
378k
    code = name_enter_string(imemory, callout_name, esp + 1);
452
378k
    if (code < 0)
453
0
        return code;
454
378k
    ++esp;
455
378k
    r_set_attrs(esp, a_executable);
456
378k
    return o_push_estack;
457
378k
}