Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/psi/zdps1.c
Line
Count
Source (jump to first uncovered line)
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 / Display PostScript graphics extensions */
18
#include "ghost.h"
19
#include "oper.h"
20
#include "gsmatrix.h"
21
#include "gspath.h"
22
#include "gspath2.h"
23
#include "gsstate.h"
24
#include "gxgstate.h"
25
#include "ialloc.h"
26
#include "igstate.h"
27
#include "ivmspace.h"
28
#include "store.h"
29
#include "stream.h"
30
#include "ibnum.h"
31
32
/* Forward references */
33
static int gstate_unshare(i_ctx_t *);
34
35
/* Declare exported procedures (for zupath.c) */
36
int zsetbbox(i_ctx_t *);
37
38
/* Structure descriptors */
39
public_st_igstate_obj();
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
static int
45
z1copy(i_ctx_t *i_ctx_p)
46
0
{
47
0
    os_ptr op = osp;
48
0
    int code = zcopy(i_ctx_p);
49
50
0
    if (code >= 0)
51
0
        return code;
52
0
    if (!r_has_type(op, t_astruct))
53
0
        return code;
54
0
    return zcopy_gstate(i_ctx_p);
55
0
}
56
57
/* ------ Graphics state ------ */
58
59
/* <bool> setstrokeadjust - */
60
static int
61
zsetstrokeadjust(i_ctx_t *i_ctx_p)
62
162k
{
63
162k
    os_ptr op = osp;
64
65
162k
    check_op(1);
66
162k
    check_type(*op, t_boolean);
67
162k
    gs_setstrokeadjust(igs, op->value.boolval);
68
162k
    pop(1);
69
162k
    return 0;
70
162k
}
71
72
/* - currentstrokeadjust <bool> */
73
static int
74
zcurrentstrokeadjust(i_ctx_t *i_ctx_p)
75
6
{
76
6
    os_ptr op = osp;
77
78
6
    push(1);
79
6
    make_bool(op, gs_currentstrokeadjust(igs));
80
6
    return 0;
81
6
}
82
83
/* ------ Graphics state objects ------ */
84
85
/*
86
 * Check to make sure that all the elements of a graphics state
87
 * can be stored in the given allocation space.
88
 */
89
/* ****** DOESN'T CHECK THE NON-REFS. ****** */
90
static int
91
gstate_check_space(i_ctx_t *i_ctx_p, int_gstate *isp, uint space)
92
326
{
93
    /*
94
     * ****** WORKAROUND ALERT ******
95
     * This code doesn't check the space of the non-refs, or copy their
96
     * contents, so it can create dangling references from global VM to
97
     * local VM.  Because of this, we simply disallow writing into gstates
98
     * in global VM (including creating them in the first place) if the
99
     * save level is greater than 0.
100
     * ****** WORKAROUND ALERT ******
101
     */
102
326
#if 1       /* ****** WORKAROUND ****** */
103
326
    if (space != avm_local && imemory_save_level(iimemory) > 0)
104
0
        return_error(gs_error_invalidaccess);
105
326
#endif        /* ****** END ****** */
106
12.0k
#define gsref_check(p) store_check_space(space, p)
107
12.0k
    int_gstate_map_refs(isp, gsref_check);
108
326
#undef gsref_check
109
326
    return 0;
110
326
}
111
112
/* - gstate <gstate> */
113
int
114
zgstate(i_ctx_t *i_ctx_p)
115
324
{
116
324
    os_ptr op = osp;
117
118
324
    int code = gstate_check_space(i_ctx_p, istate, icurrent_space);
119
324
    igstate_obj *pigo;
120
324
    gs_gstate *pnew;
121
324
    int_gstate *isp;
122
123
324
    if (code < 0)
124
0
        return code;
125
324
    pigo = ialloc_struct(igstate_obj, &st_igstate_obj, "gstate");
126
324
    if (pigo == 0)
127
0
        return_error(gs_error_VMerror);
128
324
    pnew = gs_gstate_copy(igs, imemory);
129
324
    if (pnew == 0) {
130
0
        ifree_object(pigo, "gstate");
131
0
        return_error(gs_error_VMerror);
132
0
    }
133
324
    isp = gs_int_gstate(pnew);
134
11.9k
    int_gstate_map_refs(isp, ref_mark_new);
135
324
    push(1);
136
    /*
137
     * Since igstate_obj isn't a ref, but only contains a ref, save won't
138
     * clear its l_new bit automatically, and restore won't set it
139
     * automatically; we have to make sure this ref is on the changes chain.
140
     */
141
324
    make_iastruct(op, a_all, pigo);
142
#if 0 /* Bug 689849 "gstate leaks memory" */
143
    make_null(&pigo->gstate);
144
    ref_save(op, &pigo->gstate, "gstate");
145
    make_istruct_new(&pigo->gstate, 0, pnew);
146
#else
147
324
    make_istruct(&pigo->gstate, 0, pnew);
148
324
#endif
149
324
    return 0;
150
324
}
151
152
/* copy for gstates */
153
int
154
zcopy_gstate(i_ctx_t *i_ctx_p)
155
0
{
156
0
    os_ptr op = osp;
157
0
    os_ptr op1 = op - 1;
158
0
    gs_gstate *pgs;
159
0
    gs_gstate *pgs1;
160
0
    int_gstate *pistate;
161
0
    gs_memory_t *mem;
162
0
    int code;
163
164
0
    check_op(2);
165
0
    check_stype(*op, st_igstate_obj);
166
0
    check_stype(*op1, st_igstate_obj);
167
0
    check_write(*op);
168
0
    code = gstate_unshare(i_ctx_p);
169
0
    if (code < 0)
170
0
        return code;
171
0
    pgs = igstate_ptr(op);
172
0
    pgs1 = igstate_ptr(op1);
173
174
0
    if (pgs == pgs1)
175
0
        return 0;
176
177
0
    pistate = gs_int_gstate(pgs);
178
0
    code = gstate_check_space(i_ctx_p, gs_int_gstate(pgs1), r_space(op));
179
0
    if (code < 0)
180
0
        return code;
181
0
#define gsref_save(p) ref_save(op, p, "copygstate")
182
0
    int_gstate_map_refs(pistate, gsref_save);
183
0
#undef gsref_save
184
0
    mem = gs_gstate_swap_memory(pgs, imemory);
185
0
    code = gs_copygstate(pgs, pgs1);
186
0
    gs_gstate_swap_memory(pgs, mem);
187
0
    if (code < 0)
188
0
        return code;
189
0
    int_gstate_map_refs(pistate, ref_mark_new);
190
0
    *op1 = *op;
191
0
    pop(1);
192
0
    return 0;
193
0
}
194
195
/* <gstate> currentgstate <gstate> */
196
int
197
zcurrentgstate(i_ctx_t *i_ctx_p)
198
14
{
199
14
    os_ptr op = osp;
200
14
    gs_gstate *pgs;
201
14
    int_gstate *pistate;
202
14
    int code;
203
14
    gs_memory_t *mem;
204
205
14
    check_op(1);
206
14
    check_stype(*op, st_igstate_obj);
207
2
    check_write(*op);
208
2
    code = gstate_unshare(i_ctx_p);
209
2
    if (code < 0)
210
0
        return code;
211
2
    pgs = igstate_ptr(op);
212
2
    pistate = gs_int_gstate(pgs);
213
2
    code = gstate_check_space(i_ctx_p, istate, r_space(op));
214
2
    if (code < 0)
215
0
        return code;
216
74
#define gsref_save(p) ref_save(op, p, "currentgstate")
217
74
    int_gstate_map_refs(pistate, gsref_save);
218
2
#undef gsref_save
219
2
    mem = gs_gstate_swap_memory(pgs, imemory);
220
2
    code = gs_currentgstate(pgs, igs);
221
2
    gs_gstate_swap_memory(pgs, mem);
222
2
    if (code < 0)
223
0
        return code;
224
74
    int_gstate_map_refs(pistate, ref_mark_new);
225
2
    return 0;
226
2
}
227
228
/* <gstate> setgstate - */
229
int
230
zsetgstate(i_ctx_t *i_ctx_p)
231
6
{
232
6
    os_ptr op = osp;
233
6
    int code;
234
235
6
    check_op(1);
236
6
    check_stype(*op, st_igstate_obj);
237
6
    check_read(*op);
238
6
    code = gs_setgstate(igs, igstate_ptr(op));
239
6
    if (code < 0)
240
0
        return code;
241
6
    pop(1);
242
6
    return 0;
243
6
}
244
245
/* ------ Rectangles ------- */
246
247
/*
248
 * We preallocate a short list for rectangles, because
249
 * the rectangle operators usually will involve very few rectangles.
250
 */
251
15
#define MAX_LOCAL_RECTS 5
252
typedef struct local_rects_s {
253
    gs_rect *pr;
254
    uint count;
255
    gs_rect rl[MAX_LOCAL_RECTS];
256
} local_rects_t;
257
258
/* Forward references */
259
static int rect_get(i_ctx_t *i_ctx_p, local_rects_t *, os_ptr, gs_memory_t *);
260
static void rect_release(local_rects_t *, gs_memory_t *);
261
262
/* <x> <y> <width> <height> .rectappend - */
263
/* <numarray|numstring> .rectappend - */
264
static int
265
zrectappend(i_ctx_t *i_ctx_p)
266
0
{
267
0
    os_ptr op = osp;
268
0
    local_rects_t lr;
269
0
    int npop = rect_get(i_ctx_p, &lr, op, imemory);
270
0
    int code;
271
272
0
    if (npop < 0)
273
0
        return npop;
274
0
    code = gs_rectappend(igs, lr.pr, lr.count);
275
0
    rect_release(&lr, imemory);
276
0
    if (code < 0)
277
0
        return code;
278
0
    pop(npop);
279
0
    return 0;
280
0
}
281
282
/* <x> <y> <width> <height> rectclip - */
283
/* <numarray|numstring> rectclip - */
284
static int
285
zrectclip(i_ctx_t *i_ctx_p)
286
105k
{
287
105k
    os_ptr op = osp;
288
105k
    local_rects_t lr;
289
105k
    int npop = rect_get(i_ctx_p, &lr, op, imemory);
290
105k
    int code;
291
292
105k
    if (npop < 0)
293
18
        return npop;
294
105k
    code = gs_rectclip(igs, lr.pr, lr.count);
295
105k
    rect_release(&lr, imemory);
296
105k
    if (code < 0)
297
0
        return code;
298
105k
    pop(npop);
299
105k
    return 0;
300
105k
}
301
302
/* <x> <y> <width> <height> rectfill - */
303
/* <numarray|numstring> rectfill - */
304
static int
305
zrectfill(i_ctx_t *i_ctx_p)
306
37.3k
{
307
37.3k
    os_ptr op = osp;
308
37.3k
    local_rects_t lr;
309
37.3k
    int npop = rect_get(i_ctx_p, &lr, op, imemory);
310
37.3k
    int code;
311
312
37.3k
    if (npop < 0)
313
48
        return npop;
314
37.3k
    code = gs_rectfill(igs, lr.pr, lr.count);
315
37.3k
    rect_release(&lr, imemory);
316
37.3k
    if (code < 0)
317
4
        return code;
318
37.3k
    pop(npop);
319
37.3k
    return 0;
320
37.3k
}
321
322
/* <x> <y> <width> <height> rectstroke - */
323
/* <numarray|numstring> rectstroke - */
324
static int
325
zrectstroke(i_ctx_t *i_ctx_p)
326
179
{
327
179
    os_ptr op = osp;
328
179
    gs_matrix mat;
329
179
    local_rects_t lr;
330
179
    int npop, code;
331
332
179
    if (read_matrix(imemory, op, &mat) >= 0) {
333
        /* Concatenate the matrix to the CTM just before stroking the path. */
334
2
        npop = rect_get(i_ctx_p, &lr, op - 1, imemory);
335
2
        if (npop < 0)
336
2
            return npop;
337
0
        code = gs_rectstroke(igs, lr.pr, lr.count, &mat);
338
0
        npop++;
339
177
    } else {
340
        /* No matrix. */
341
177
        npop = rect_get(i_ctx_p, &lr, op, imemory);
342
177
        if (npop < 0)
343
17
            return npop;
344
160
        code = gs_rectstroke(igs, lr.pr, lr.count, (gs_matrix *) 0);
345
160
    }
346
160
    rect_release(&lr, imemory);
347
160
    if (code < 0)
348
0
        return code;
349
160
    pop(npop);
350
160
    return 0;
351
160
}
352
353
/* --- Internal routines --- */
354
355
/* Get rectangles from the stack. */
356
/* Return the number of elements to pop (>0) if OK, <0 if error. */
357
static int
358
rect_get(i_ctx_t *i_ctx_p, local_rects_t * plr, os_ptr op, gs_memory_t *mem)
359
142k
{
360
142k
    int format, code;
361
142k
    uint n, count;
362
142k
    gs_rect *pr;
363
142k
    double rv[4];
364
365
142k
    check_op(1);
366
142k
    switch (r_type(op)) {
367
18
        case t_array:
368
18
        case t_mixedarray:
369
18
        case t_shortarray:
370
27
        case t_string:
371
27
            code = num_array_format(op);
372
27
            if (code < 0)
373
9
                return code;
374
18
            format = code;
375
18
            count = num_array_size(op, format);
376
18
            if (count % 4)
377
3
                return_error(gs_error_typecheck);
378
15
            count /= 4;
379
15
            break;
380
142k
        default:    /* better be 4 numbers */
381
142k
            check_op(4);
382
142k
            code = num_params(op, 4, rv);
383
142k
            if (code < 0)
384
21
                return code;
385
142k
            plr->pr = plr->rl;
386
142k
            plr->count = 1;
387
142k
            plr->rl[0].q.x = (plr->rl[0].p.x = rv[0]) + rv[2];
388
142k
            plr->rl[0].q.y = (plr->rl[0].p.y = rv[1]) + rv[3];
389
142k
            return 4;
390
142k
    }
391
15
    plr->count = count;
392
15
    if (count <= MAX_LOCAL_RECTS)
393
15
        pr = plr->rl;
394
0
    else {
395
0
        pr = (gs_rect *)gs_alloc_byte_array(mem, count, sizeof(gs_rect),
396
0
                                            "rect_get");
397
0
        if (pr == 0)
398
0
            return_error(gs_error_VMerror);
399
0
    }
400
15
    plr->pr = pr;
401
15
    for (n = 0; n < count; n++, pr++) {
402
0
        ref rnum;
403
0
        int i;
404
405
0
        for (i = 0; i < 4; i++) {
406
0
            code = num_array_get(mem, (const ref *)op, format,
407
0
                                 (n << 2) + i, &rnum);
408
0
            switch (code) {
409
0
                case t_integer:
410
0
                    rv[i] = (double)rnum.value.intval;
411
0
                    break;
412
0
                case t_real:
413
0
                    rv[i] = rnum.value.realval;
414
0
                    break;
415
0
                default:  /* code < 0 */
416
0
                    return code;
417
0
            }
418
0
        }
419
0
        pr->q.x = (pr->p.x = rv[0]) + rv[2];
420
0
        pr->q.y = (pr->p.y = rv[1]) + rv[3];
421
0
    }
422
15
    return 1;
423
15
}
424
425
/* Release the rectangle list if needed. */
426
static void
427
rect_release(local_rects_t * plr, gs_memory_t *mem)
428
142k
{
429
142k
    if (plr->pr != plr->rl)
430
0
        gs_free_object(mem, plr->pr, "rect_release");
431
142k
}
432
433
/* ------ Graphics state ------ */
434
435
/* <llx> <lly> <urx> <ury> setbbox - */
436
int
437
zsetbbox(i_ctx_t *i_ctx_p)
438
35
{
439
35
    os_ptr op = osp;
440
35
    double box[4];
441
35
    int code;
442
443
35
    check_op(4);
444
21
    code = num_params(op, 4, box);
445
446
21
    if (code < 0)
447
2
        return code;
448
19
    if ((code = gs_setbbox(igs, box[0], box[1], box[2], box[3])) < 0)
449
11
        return code;
450
8
    pop(4);
451
8
    return 0;
452
19
}
453
454
/* ------ Initialization procedure ------ */
455
456
const op_def zdps1_l2_op_defs[] =
457
{
458
    op_def_begin_level2(),
459
                /* Graphics state */
460
    {"0currentstrokeadjust", zcurrentstrokeadjust},
461
    {"1setstrokeadjust", zsetstrokeadjust},
462
                /* Graphics state objects */
463
    {"1copy", z1copy},
464
    {"1currentgstate", zcurrentgstate},
465
    {"0gstate", zgstate},
466
    {"1setgstate", zsetgstate},
467
                /* Rectangles */
468
    {"1.rectappend", zrectappend},
469
    {"1rectclip", zrectclip},
470
    {"1rectfill", zrectfill},
471
    {"1rectstroke", zrectstroke},
472
                /* Graphics state components */
473
    {"4setbbox", zsetbbox},
474
    op_def_end(0)
475
};
476
477
/* ------ Internal routines ------ */
478
479
/* Ensure that a gstate is not shared with an outer save level. */
480
/* *op is of type t_astruct(igstate_obj). */
481
static int
482
gstate_unshare(i_ctx_t *i_ctx_p)
483
2
{
484
2
    os_ptr op = osp;
485
2
    ref *pgsref = &r_ptr(op, igstate_obj)->gstate;
486
2
    gs_gstate *pgs = r_ptr(pgsref, gs_gstate);
487
2
    gs_gstate *pnew;
488
2
    int_gstate *isp;
489
490
2
    if (!ref_must_save(pgsref))
491
0
        return 0;
492
    /* Copy the gstate. */
493
2
    pnew = gs_gstate_copy(pgs, pgs->memory);
494
2
    if (pnew == 0)
495
0
        return_error(gs_error_VMerror);
496
2
    isp = gs_int_gstate(pnew);
497
74
    int_gstate_map_refs(isp, ref_mark_new);
498
2
    ref_do_save(op, pgsref, "gstate_unshare");
499
2
    make_istruct_new(pgsref, 0, pnew);
500
2
    return 0;
501
2
}