Coverage Report

Created: 2025-06-10 06:56

/src/ghostpdl/psi/zgstate.c
Line
Count
Source (jump to first uncovered line)
1
/* Copyright (C) 2001-2024 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
/* Graphics state operators */
18
#include "math_.h"
19
#include "ghost.h"
20
#include "oper.h"
21
#include "ialloc.h"
22
#include "icremap.h"
23
#include "idict.h"
24
#include "istruct.h"
25
#include "igstate.h"
26
#include "gsmatrix.h"
27
#include "store.h"
28
#include "gscspace.h"
29
#include "iname.h"
30
31
/* Structure descriptors */
32
private_st_int_gstate();
33
private_st_int_remap_color_info();
34
35
/* ------ Utilities ------ */
36
37
static int
38
zset_real(i_ctx_t *i_ctx_p, int (*set_proc)(gs_gstate *, double))
39
34
{
40
34
    os_ptr op = osp;
41
34
    double param;
42
34
    int code = real_param(op, &param);
43
44
34
    if (code < 0)
45
34
        return_op_typecheck(op);
46
33
    code = set_proc(igs, param);
47
33
    if (code == 0)
48
33
        pop(1);
49
33
    return code;
50
34
}
51
52
#if 0 /* Currently unused */
53
static int
54
zcurrent_real(i_ctx_t *i_ctx_p, double (*current_proc)(const gs_gstate *))
55
{
56
    os_ptr op = osp;
57
58
    push(1);
59
    make_real(op, current_proc(igs));
60
    return 0;
61
}
62
#endif
63
64
static int
65
zset_bool(i_ctx_t *i_ctx_p, void (*set_proc)(gs_gstate *, bool))
66
0
{
67
0
    os_ptr op = osp;
68
69
0
    check_type(*op, t_boolean);
70
0
    set_proc(igs, op->value.boolval);
71
0
    pop(1);
72
0
    return 0;
73
0
}
74
75
static int
76
zcurrent_bool(i_ctx_t *i_ctx_p, bool (*current_proc)(const gs_gstate *))
77
0
{
78
0
    os_ptr op = osp;
79
80
0
    push(1);
81
0
    make_bool(op, current_proc(igs));
82
0
    return 0;
83
0
}
84
85
static int
86
zset_uint(i_ctx_t *i_ctx_p, void (*set_proc)(gs_gstate *, uint))
87
0
{
88
0
    os_ptr op = osp;
89
90
0
    check_type(*op, t_integer);
91
0
    set_proc(igs, op->value.intval);
92
0
    pop(1);
93
0
    return 0;
94
0
}
95
96
#if 0 /* Currently unused */
97
static int
98
zcurrent_uint(i_ctx_t *i_ctx_p, uint (*current_proc)(const gs_gstate *))
99
{
100
    os_ptr op = osp;
101
102
    push(1);
103
    make_int(op, current_proc(igs));
104
    return 0;
105
}
106
#endif
107
108
/* ------ Operations on the entire graphics state ------ */
109
110
/* "Client" procedures */
111
static void *gs_istate_alloc(gs_memory_t * mem);
112
static int gs_istate_copy(void *to, const void *from);
113
static void gs_istate_free(void *old, gs_memory_t * mem, gs_gstate *pgs);
114
static const gs_gstate_client_procs istate_procs = {
115
    gs_istate_alloc,
116
    gs_istate_copy,
117
    gs_istate_free,
118
    0,      /* copy_for */
119
};
120
121
/* Initialize the graphics stack. */
122
gs_gstate *
123
int_gstate_alloc(const gs_dual_memory_t * dmem)
124
9.66k
{
125
9.66k
    int_gstate *iigs;
126
9.66k
    ref proc0;
127
9.66k
    int_remap_color_info_t *prci;
128
9.66k
    gs_ref_memory_t *lmem = dmem->space_local;
129
9.66k
    gs_ref_memory_t *gmem = dmem->space_global;
130
9.66k
    gs_gstate *pgs = gs_gstate_alloc((gs_memory_t *)lmem);
131
132
9.66k
    if (pgs == NULL)
133
0
        return NULL;
134
135
9.66k
    iigs = gs_alloc_struct((gs_memory_t *)lmem, int_gstate, &st_int_gstate,
136
9.66k
                           "int_gstate_alloc(int_gstate)");
137
9.66k
    if (iigs == NULL)
138
0
        return NULL;
139
357k
    int_gstate_map_refs(iigs, make_null);
140
9.66k
    make_empty_array(&iigs->dash_pattern_array, a_all);
141
9.66k
    if (gs_alloc_ref_array(lmem, &proc0, a_readonly + a_executable, 2,
142
9.66k
                       "int_gstate_alloc(proc0)") < 0)
143
0
        return NULL;
144
9.66k
    make_oper(proc0.value.refs, 0, zpop);
145
9.66k
    make_real(proc0.value.refs + 1, 0.0);
146
9.66k
    iigs->black_generation = proc0;
147
9.66k
    iigs->undercolor_removal = proc0;
148
9.66k
    make_false(&iigs->use_cie_color);
149
    /*
150
     * Even though the gstate itself is allocated in local VM, the
151
     * container for the color remapping procedure must be allocated in
152
     * global VM so that the gstate can be copied into global VM.
153
     */
154
9.66k
    prci = gs_alloc_struct((gs_memory_t *)gmem, int_remap_color_info_t,
155
9.66k
                           &st_int_remap_color_info,
156
9.66k
                           "int_gstate_alloc(remap color info)");
157
9.66k
    if (prci == NULL)
158
0
        return NULL;
159
9.66k
    make_struct(&iigs->remap_color_info, imemory_space(gmem), prci);
160
9.66k
    clear_pagedevice(iigs);
161
9.66k
    gs_gstate_set_client(pgs, iigs, &istate_procs, true);
162
    /* PostScript code wants limit clamping enabled. */
163
9.66k
    gs_setlimitclamp(pgs, true);
164
    /*
165
     * gsave and grestore only work properly
166
     * if there are always at least 2 entries on the stack.
167
     * We count on the PostScript initialization code to do a gsave.
168
     */
169
9.66k
    return pgs;
170
9.66k
}
171
172
/* - gsave - */
173
int
174
zgsave(i_ctx_t *i_ctx_p)
175
9
{
176
9
    return gs_gsave(igs);
177
9
}
178
179
/* - grestore - */
180
int
181
zgrestore(i_ctx_t *i_ctx_p)
182
9
{
183
9
    return gs_grestore(igs);
184
9
}
185
186
/* - grestoreall - */
187
int
188
zgrestoreall(i_ctx_t *i_ctx_p)
189
0
{
190
0
    return gs_grestoreall(igs);
191
0
}
192
193
/* - initgraphics - */
194
static int
195
zinitgraphics(i_ctx_t *i_ctx_p)
196
63.0k
{
197
    /*
198
     * Although gs_initgraphics resets the color space to DeviceGray, it does
199
     * not modify the 'interpreter' gstate, which stores a copy of the PostScript
200
     * object used to set the colour space. We could do this here, with effort,
201
     * but instead we choose t do it in gs_cspace.ps and handle it all in PostScript.
202
     */
203
63.0k
     make_empty_array(&istate->dash_pattern_array, a_all);
204
63.0k
     return gs_initgraphics(igs);
205
63.0k
}
206
207
/* ------ Operations on graphics state elements ------ */
208
209
/* <num> setlinewidth - */
210
static int
211
zsetlinewidth(i_ctx_t *i_ctx_p)
212
297
{
213
297
    os_ptr op = osp;
214
        /*
215
         * The Red Book doesn't say anything about this, but Adobe
216
         * interpreters return (or perhaps store) the absolute value
217
         * of the width.
218
         */
219
297
    double width;
220
297
    int code;
221
222
297
    check_op(1);
223
297
    code = real_param(op, &width);
224
225
297
    if (code < 0)
226
297
        return_op_typecheck(op);
227
296
    code = gs_setlinewidth(igs, fabs(width));
228
296
    if (code >= 0)
229
296
        pop(1);
230
296
    return code;
231
297
}
232
233
/* - currentlinewidth <num> */
234
static int
235
zcurrentlinewidth(i_ctx_t *i_ctx_p)
236
354
{
237
354
    os_ptr op = osp;
238
239
354
    push(1);
240
354
    make_real(op, gs_currentlinewidth(igs));
241
354
    return 0;
242
354
}
243
244
/* <cap_int> .setlinecap - */
245
static int
246
zsetlinecap(i_ctx_t *i_ctx_p)
247
4
{
248
4
    os_ptr op = osp;
249
4
    int param;
250
4
    int code;
251
252
4
    check_op(1);
253
4
    code = int_param(op, max_int, &param);
254
255
4
    if (code < 0 || (code = gs_setlinecap(igs, (gs_line_cap) param)) < 0)
256
1
        return code;
257
3
    pop(1);
258
3
    return 0;
259
4
}
260
261
/* - currentlinecap <cap_int> */
262
static int
263
zcurrentlinecap(i_ctx_t *i_ctx_p)
264
18
{
265
18
    os_ptr op = osp;
266
267
18
    push(1);
268
18
    make_int(op, (int)gs_currentlinecap(igs));
269
18
    return 0;
270
18
}
271
272
/* <join_int> .setlinejoin - */
273
static int
274
zsetlinejoin(i_ctx_t *i_ctx_p)
275
5
{
276
5
    os_ptr op = osp;
277
5
    int param;
278
5
    int code;
279
280
5
    check_op(1);
281
5
    code = int_param(op, max_int, &param);
282
283
5
    if (code < 0 || (code = gs_setlinejoin(igs, (gs_line_join) param)) < 0)
284
2
        return code;
285
3
    pop(1);
286
3
    return 0;
287
5
}
288
289
/* - currentlinejoin <join_int> */
290
static int
291
zcurrentlinejoin(i_ctx_t *i_ctx_p)
292
9
{
293
9
    os_ptr op = osp;
294
295
9
    push(1);
296
9
    make_int(op, (int)gs_currentlinejoin(igs));
297
9
    return 0;
298
9
}
299
300
/* <num> setmiterlimit - */
301
static int
302
zsetmiterlimit(i_ctx_t *i_ctx_p)
303
19
{
304
19
    os_ptr op = osp;
305
19
    check_op(1);
306
18
    return zset_real(i_ctx_p, gs_setmiterlimit);
307
19
}
308
309
/* - currentmiterlimit <num> */
310
static int
311
zcurrentmiterlimit(i_ctx_t *i_ctx_p)
312
10
{
313
10
    os_ptr op = osp;
314
315
10
    push(1);
316
10
    make_real(op, gs_currentmiterlimit(igs));
317
10
    return 0;
318
10
}
319
320
/* <array> <offset> setdash - */
321
static int
322
zsetdash(i_ctx_t *i_ctx_p)
323
235
{
324
235
    os_ptr op = osp;
325
235
    os_ptr op1 = op - 1;
326
235
    double offset;
327
235
    int code;
328
235
    uint i, n;
329
235
    gs_memory_t *mem = imemory;
330
235
    float *pattern;
331
332
235
    check_op(2);
333
235
    code = real_param(op, &offset);
334
235
    if (code < 0)
335
235
        return_op_typecheck(op);
336
235
    if (!r_is_array(op1))
337
235
        return_op_typecheck(op1);
338
    /* Adobe interpreters apparently don't check the array for */
339
    /* read access, so we won't either. */
340
    /*check_read(*op1); */
341
    /* Unpack the dash pattern and check it */
342
234
    n = r_size(op1);
343
234
    pattern =
344
234
        (float *)gs_alloc_byte_array(mem, n, sizeof(float), "setdash");
345
346
234
    if (pattern == 0)
347
0
        return_error(gs_error_VMerror);
348
235
    for (i = 0, code = 0; i < n && code >= 0; ++i) {
349
1
        ref element;
350
351
1
        array_get(mem, op1, (long)i, &element);
352
1
        code = float_param(&element, &pattern[i]);
353
1
    }
354
234
    if (code >= 0)
355
233
        code = gs_setdash(igs, pattern, n, offset);
356
234
    gs_free_object(mem, pattern, "setdash");  /* gs_setdash copies this */
357
234
    if (code < 0)
358
1
        return code;
359
233
    ref_assign(&istate->dash_pattern_array, op1);
360
233
    pop(2);
361
233
    return code;
362
234
}
363
364
/* - currentdash <array> <offset> */
365
static int
366
zcurrentdash(i_ctx_t *i_ctx_p)
367
63
{
368
63
    os_ptr op = osp;
369
370
63
    push(2);
371
63
    ref_assign(op - 1, &istate->dash_pattern_array);
372
63
    make_real(op, gs_currentdash_offset(igs));
373
63
    return 0;
374
63
}
375
376
/* <num> setflat - */
377
static int
378
zsetflat(i_ctx_t *i_ctx_p)
379
16
{
380
16
    return zset_real(i_ctx_p, gs_setflat);
381
16
}
382
383
/* - currentflat <num> */
384
static int
385
zcurrentflat(i_ctx_t *i_ctx_p)
386
14
{
387
14
    os_ptr op = osp;
388
389
14
    push(1);
390
14
    make_real(op, gs_currentflat(igs));
391
14
    return 0;
392
14
}
393
394
/* ------ Extensions ------ */
395
396
/* <bool> .setaccuratecurves - */
397
static int
398
zsetaccuratecurves(i_ctx_t *i_ctx_p)
399
0
{
400
0
    return zset_bool(i_ctx_p, gs_setaccuratecurves);
401
0
}
402
403
/* - .currentaccuratecurves <bool> */
404
static int
405
zcurrentaccuratecurves(i_ctx_t *i_ctx_p)
406
0
{
407
0
    return zcurrent_bool(i_ctx_p, gs_currentaccuratecurves);
408
0
}
409
410
/* <join_int|-1> .setcurvejoin - */
411
static int
412
zsetcurvejoin(i_ctx_t *i_ctx_p)
413
0
{
414
0
    os_ptr op = osp;
415
0
    int code;
416
417
0
    check_type(*op, t_integer);
418
0
    if (op->value.intval < -1 || op->value.intval > max_int)
419
0
        return_error(gs_error_rangecheck);
420
0
    code = gs_setcurvejoin(igs, (int)op->value.intval);
421
0
    if (code < 0)
422
0
        return code;
423
0
    pop(1);
424
0
    return 0;
425
0
}
426
427
/* - .currentcurvejoin <join_int|-1> */
428
static int
429
zcurrentcurvejoin(i_ctx_t *i_ctx_p)
430
0
{
431
0
    os_ptr op = osp;
432
433
0
    push(1);
434
0
    make_int(op, gs_currentcurvejoin(igs));
435
0
    return 0;
436
0
}
437
438
/* <adjust.x> <adjust.y> .setfilladjust2 - */
439
static int
440
zsetfilladjust2(i_ctx_t *i_ctx_p)
441
9.66k
{
442
9.66k
    os_ptr op = osp;
443
9.66k
    double adjust[2];
444
9.66k
    int code = num_params(op, 2, adjust);
445
446
9.66k
    if (code < 0)
447
1
        return code;
448
9.66k
    code = gs_setfilladjust(igs, adjust[0], adjust[1]);
449
9.66k
    if (code < 0)
450
0
        return code;
451
9.66k
    pop(2);
452
9.66k
    return 0;
453
9.66k
}
454
455
/* - .currentfilladjust2 <adjust.x> <adjust.y> */
456
static int
457
zcurrentfilladjust2(i_ctx_t *i_ctx_p)
458
0
{
459
0
    os_ptr op = osp;
460
0
    gs_point adjust;
461
462
0
    push(2);
463
0
    gs_currentfilladjust(igs, &adjust);
464
0
    make_real(op - 1, adjust.x);
465
0
    make_real(op, adjust.y);
466
0
    return 0;
467
0
}
468
469
/* <bool> .setdashadapt - */
470
static int
471
zsetdashadapt(i_ctx_t *i_ctx_p)
472
0
{
473
0
    return zset_bool(i_ctx_p, gs_setdashadapt);
474
0
}
475
476
/* - .currentdashadapt <bool> */
477
static int
478
zcurrentdashadapt(i_ctx_t *i_ctx_p)
479
0
{
480
0
    return zcurrent_bool(i_ctx_p, gs_currentdashadapt);
481
0
}
482
483
/* <num> <bool> .setdotlength - */
484
static int
485
zsetdotlength(i_ctx_t *i_ctx_p)
486
0
{
487
0
    os_ptr op = osp;
488
0
    double length;
489
0
    int code = real_param(op - 1, &length);
490
491
0
    if (code < 0)
492
0
        return code;
493
0
    check_type(*op, t_boolean);
494
0
    code = gs_setdotlength(igs, length, op->value.boolval);
495
0
    if (code < 0)
496
0
        return code;
497
0
    pop(2);
498
0
    return 0;
499
0
}
500
501
/* - .currentdotlength <num> <bool> */
502
static int
503
zcurrentdotlength(i_ctx_t *i_ctx_p)
504
0
{
505
0
    os_ptr op = osp;
506
507
0
    push(2);
508
0
    make_real(op - 1, gs_currentdotlength(igs));
509
0
    make_bool(op, gs_currentdotlength_absolute(igs));
510
0
    return 0;
511
0
}
512
513
/* - .setdotorientation - */
514
static int
515
zsetdotorientation(i_ctx_t *i_ctx_p)
516
0
{
517
0
    return gs_setdotorientation(igs);
518
0
}
519
520
/* - .dotorientation - */
521
static int
522
zdotorientation(i_ctx_t *i_ctx_p)
523
0
{
524
0
    return gs_dotorientation(igs);
525
0
}
526
527
/* <bool> .setlimitclamp - */
528
static int
529
zsetlimitclamp(i_ctx_t *i_ctx_p)
530
0
{
531
0
    return zset_bool(i_ctx_p, gs_setlimitclamp);
532
0
}
533
534
/* - .currentlimitclamp <bool> */
535
static int
536
zcurrentlimitclamp(i_ctx_t *i_ctx_p)
537
0
{
538
0
    return zcurrent_bool(i_ctx_p, gs_currentlimitclamp);
539
0
}
540
541
/* We need this for the PScript5.DLL Idiom recognition, when outputting
542
 * to pdfwrite. This is used to turn the 'fake bold' text into text
543
 * rendering mode 2 (fill and then stroke).
544
 */
545
/* <int> .settextrenderingmode - */
546
static int
547
zsettextrenderingmode(i_ctx_t *i_ctx_p)
548
0
{
549
0
    return zset_uint(i_ctx_p, gs_settextrenderingmode);
550
0
}
551
552
/* <bool> .sethpglpathmode - */
553
static int
554
zsethpglpathmode(i_ctx_t *i_ctx_p)
555
0
{
556
0
    return zset_bool(i_ctx_p, gs_sethpglpathmode);
557
0
}
558
559
/* - .currenthpglpathmode <int> */
560
static int
561
zcurrenthpglpathmode(i_ctx_t *i_ctx_p)
562
0
{
563
0
    return zcurrent_bool(i_ctx_p, gs_currenthpglpathmode);
564
0
}
565
566
/* ------ Initialization procedure ------ */
567
568
/* We need to split the table because of the 16-element limit. */
569
const op_def zgstate1_op_defs[] = {
570
    {"0.currentaccuratecurves", zcurrentaccuratecurves},
571
    {"0.currentcurvejoin", zcurrentcurvejoin},
572
    {"0currentdash", zcurrentdash},
573
    {"0.currentdashadapt", zcurrentdashadapt},
574
    {"0.currentdotlength", zcurrentdotlength},
575
    {"0.currentfilladjust2", zcurrentfilladjust2},
576
    {"0currentflat", zcurrentflat},
577
    {"0.currentlimitclamp", zcurrentlimitclamp},
578
    {"0currentlinecap", zcurrentlinecap},
579
    {"0currentlinejoin", zcurrentlinejoin},
580
    {"0currentlinewidth", zcurrentlinewidth},
581
    {"0currentmiterlimit", zcurrentmiterlimit},
582
    {"0.dotorientation", zdotorientation},
583
    {"0grestore", zgrestore},
584
    {"0grestoreall", zgrestoreall},
585
    op_def_end(0)
586
};
587
const op_def zgstate2_op_defs[] = {
588
    {"0gsave", zgsave},
589
    {"0initgraphics", zinitgraphics},
590
    {"1.setaccuratecurves", zsetaccuratecurves},
591
    {"1.setcurvejoin", zsetcurvejoin},
592
    {"2setdash", zsetdash},
593
    {"1.setdashadapt", zsetdashadapt},
594
    {"2.setdotlength", zsetdotlength},
595
    {"0.setdotorientation", zsetdotorientation},
596
    {"2.setfilladjust2", zsetfilladjust2},
597
    {"1.setlimitclamp", zsetlimitclamp},
598
    {"1setflat", zsetflat},
599
    {"1.setlinecap", zsetlinecap},
600
    {"1.setlinejoin", zsetlinejoin},
601
    {"1setlinewidth", zsetlinewidth},
602
    {"1setmiterlimit", zsetmiterlimit},
603
    op_def_end(0)
604
};
605
const op_def zgstate3_op_defs[] = {
606
    {"1.settextrenderingmode", zsettextrenderingmode},
607
    {"0.sethpglpathmode", zsethpglpathmode},
608
    {"0.currenthpglpathmode", zcurrenthpglpathmode},
609
    op_def_end(0)
610
};
611
612
/* ------ Internal routines ------ */
613
614
/* Allocate the interpreter's part of a graphics state. */
615
static void *
616
gs_istate_alloc(gs_memory_t * mem)
617
153k
{
618
153k
    return gs_alloc_struct(mem, int_gstate, &st_int_gstate, "int_gsave");
619
153k
}
620
621
/* Copy the interpreter's part of a graphics state. */
622
static int
623
gs_istate_copy(void *to, const void *from)
624
297k
{
625
297k
    *(int_gstate *) to = *(const int_gstate *)from;
626
297k
    return 0;
627
297k
}
628
629
/* Free the interpreter's part of a graphics state. */
630
static void
631
gs_istate_free(void *old, gs_memory_t * mem, gs_gstate *pgs)
632
163k
{
633
163k
    gs_free_object(mem, old, "int_grestore");
634
163k
}