Coverage Report

Created: 2025-12-31 07:31

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