Coverage Report

Created: 2026-10-28 08:05

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