Coverage Report

Created: 2025-06-24 07:01

/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
174
{
40
174
    os_ptr op = osp;
41
174
    double param;
42
174
    int code = real_param(op, &param);
43
44
174
    if (code < 0)
45
174
        return_op_typecheck(op);
46
161
    code = set_proc(igs, param);
47
161
    if (code == 0)
48
160
        pop(1);
49
161
    return code;
50
174
}
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
8
{
88
8
    os_ptr op = osp;
89
90
8
    check_type(*op, t_integer);
91
1
    set_proc(igs, op->value.intval);
92
1
    pop(1);
93
1
    return 0;
94
8
}
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
162k
{
125
162k
    int_gstate *iigs;
126
162k
    ref proc0;
127
162k
    int_remap_color_info_t *prci;
128
162k
    gs_ref_memory_t *lmem = dmem->space_local;
129
162k
    gs_ref_memory_t *gmem = dmem->space_global;
130
162k
    gs_gstate *pgs = gs_gstate_alloc((gs_memory_t *)lmem);
131
132
162k
    if (pgs == NULL)
133
0
        return NULL;
134
135
162k
    iigs = gs_alloc_struct((gs_memory_t *)lmem, int_gstate, &st_int_gstate,
136
162k
                           "int_gstate_alloc(int_gstate)");
137
162k
    if (iigs == NULL)
138
0
        return NULL;
139
6.01M
    int_gstate_map_refs(iigs, make_null);
140
162k
    make_empty_array(&iigs->dash_pattern_array, a_all);
141
162k
    if (gs_alloc_ref_array(lmem, &proc0, a_readonly + a_executable, 2,
142
162k
                       "int_gstate_alloc(proc0)") < 0)
143
0
        return NULL;
144
162k
    make_oper(proc0.value.refs, 0, zpop);
145
162k
    make_real(proc0.value.refs + 1, 0.0);
146
162k
    iigs->black_generation = proc0;
147
162k
    iigs->undercolor_removal = proc0;
148
162k
    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
162k
    prci = gs_alloc_struct((gs_memory_t *)gmem, int_remap_color_info_t,
155
162k
                           &st_int_remap_color_info,
156
162k
                           "int_gstate_alloc(remap color info)");
157
162k
    if (prci == NULL)
158
0
        return NULL;
159
162k
    make_struct(&iigs->remap_color_info, imemory_space(gmem), prci);
160
162k
    clear_pagedevice(iigs);
161
162k
    gs_gstate_set_client(pgs, iigs, &istate_procs, true);
162
    /* PostScript code wants limit clamping enabled. */
163
162k
    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
162k
    return pgs;
170
162k
}
171
172
/* - gsave - */
173
int
174
zgsave(i_ctx_t *i_ctx_p)
175
767
{
176
767
    return gs_gsave(igs);
177
767
}
178
179
/* - grestore - */
180
int
181
zgrestore(i_ctx_t *i_ctx_p)
182
767
{
183
767
    return gs_grestore(igs);
184
767
}
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
1.06M
{
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
1.06M
     make_empty_array(&istate->dash_pattern_array, a_all);
204
1.06M
     return gs_initgraphics(igs);
205
1.06M
}
206
207
/* ------ Operations on graphics state elements ------ */
208
209
/* <num> setlinewidth - */
210
static int
211
zsetlinewidth(i_ctx_t *i_ctx_p)
212
5.60k
{
213
5.60k
    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
5.60k
    double width;
220
5.60k
    int code;
221
222
5.60k
    check_op(1);
223
5.58k
    code = real_param(op, &width);
224
225
5.58k
    if (code < 0)
226
5.58k
        return_op_typecheck(op);
227
5.57k
    code = gs_setlinewidth(igs, fabs(width));
228
5.57k
    if (code >= 0)
229
5.57k
        pop(1);
230
5.57k
    return code;
231
5.58k
}
232
233
/* - currentlinewidth <num> */
234
static int
235
zcurrentlinewidth(i_ctx_t *i_ctx_p)
236
604
{
237
604
    os_ptr op = osp;
238
239
604
    push(1);
240
604
    make_real(op, gs_currentlinewidth(igs));
241
604
    return 0;
242
604
}
243
244
/* <cap_int> .setlinecap - */
245
static int
246
zsetlinecap(i_ctx_t *i_ctx_p)
247
112
{
248
112
    os_ptr op = osp;
249
112
    int param;
250
112
    int code;
251
252
112
    check_op(1);
253
112
    code = int_param(op, max_int, &param);
254
255
112
    if (code < 0 || (code = gs_setlinecap(igs, (gs_line_cap) param)) < 0)
256
14
        return code;
257
98
    pop(1);
258
98
    return 0;
259
112
}
260
261
/* - currentlinecap <cap_int> */
262
static int
263
zcurrentlinecap(i_ctx_t *i_ctx_p)
264
263
{
265
263
    os_ptr op = osp;
266
267
263
    push(1);
268
263
    make_int(op, (int)gs_currentlinecap(igs));
269
263
    return 0;
270
263
}
271
272
/* <join_int> .setlinejoin - */
273
static int
274
zsetlinejoin(i_ctx_t *i_ctx_p)
275
115
{
276
115
    os_ptr op = osp;
277
115
    int param;
278
115
    int code;
279
280
115
    check_op(1);
281
115
    code = int_param(op, max_int, &param);
282
283
115
    if (code < 0 || (code = gs_setlinejoin(igs, (gs_line_join) param)) < 0)
284
28
        return code;
285
87
    pop(1);
286
87
    return 0;
287
115
}
288
289
/* - currentlinejoin <join_int> */
290
static int
291
zcurrentlinejoin(i_ctx_t *i_ctx_p)
292
1.35k
{
293
1.35k
    os_ptr op = osp;
294
295
1.35k
    push(1);
296
1.35k
    make_int(op, (int)gs_currentlinejoin(igs));
297
1.35k
    return 0;
298
1.35k
}
299
300
/* <num> setmiterlimit - */
301
static int
302
zsetmiterlimit(i_ctx_t *i_ctx_p)
303
122
{
304
122
    os_ptr op = osp;
305
122
    check_op(1);
306
110
    return zset_real(i_ctx_p, gs_setmiterlimit);
307
122
}
308
309
/* - currentmiterlimit <num> */
310
static int
311
zcurrentmiterlimit(i_ctx_t *i_ctx_p)
312
58
{
313
58
    os_ptr op = osp;
314
315
58
    push(1);
316
58
    make_real(op, gs_currentmiterlimit(igs));
317
58
    return 0;
318
58
}
319
320
/* <array> <offset> setdash - */
321
static int
322
zsetdash(i_ctx_t *i_ctx_p)
323
516
{
324
516
    os_ptr op = osp;
325
516
    os_ptr op1 = op - 1;
326
516
    double offset;
327
516
    int code;
328
516
    uint i, n;
329
516
    gs_memory_t *mem = imemory;
330
516
    float *pattern;
331
332
516
    check_op(2);
333
508
    code = real_param(op, &offset);
334
508
    if (code < 0)
335
508
        return_op_typecheck(op);
336
508
    if (!r_is_array(op1))
337
508
        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
498
    n = r_size(op1);
343
498
    pattern =
344
498
        (float *)gs_alloc_byte_array(mem, n, sizeof(float), "setdash");
345
346
498
    if (pattern == 0)
347
0
        return_error(gs_error_VMerror);
348
499
    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
498
    if (code >= 0)
355
497
        code = gs_setdash(igs, pattern, n, offset);
356
498
    gs_free_object(mem, pattern, "setdash");  /* gs_setdash copies this */
357
498
    if (code < 0)
358
1
        return code;
359
497
    ref_assign(&istate->dash_pattern_array, op1);
360
497
    pop(2);
361
497
    return code;
362
498
}
363
364
/* - currentdash <array> <offset> */
365
static int
366
zcurrentdash(i_ctx_t *i_ctx_p)
367
1.68k
{
368
1.68k
    os_ptr op = osp;
369
370
1.68k
    push(2);
371
1.68k
    ref_assign(op - 1, &istate->dash_pattern_array);
372
1.68k
    make_real(op, gs_currentdash_offset(igs));
373
1.68k
    return 0;
374
1.68k
}
375
376
/* <num> setflat - */
377
static int
378
zsetflat(i_ctx_t *i_ctx_p)
379
64
{
380
64
    return zset_real(i_ctx_p, gs_setflat);
381
64
}
382
383
/* - currentflat <num> */
384
static int
385
zcurrentflat(i_ctx_t *i_ctx_p)
386
105
{
387
105
    os_ptr op = osp;
388
389
105
    push(1);
390
105
    make_real(op, gs_currentflat(igs));
391
105
    return 0;
392
105
}
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
162k
{
442
162k
    os_ptr op = osp;
443
162k
    double adjust[2];
444
162k
    int code = num_params(op, 2, adjust);
445
446
162k
    if (code < 0)
447
10
        return code;
448
162k
    code = gs_setfilladjust(igs, adjust[0], adjust[1]);
449
162k
    if (code < 0)
450
0
        return code;
451
162k
    pop(2);
452
162k
    return 0;
453
162k
}
454
455
/* - .currentfilladjust2 <adjust.x> <adjust.y> */
456
static int
457
zcurrentfilladjust2(i_ctx_t *i_ctx_p)
458
6
{
459
6
    os_ptr op = osp;
460
6
    gs_point adjust;
461
462
6
    push(2);
463
6
    gs_currentfilladjust(igs, &adjust);
464
6
    make_real(op - 1, adjust.x);
465
6
    make_real(op, adjust.y);
466
6
    return 0;
467
6
}
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
8
{
549
8
    return zset_uint(i_ctx_p, gs_settextrenderingmode);
550
8
}
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
25.8M
{
618
25.8M
    return gs_alloc_struct(mem, int_gstate, &st_int_gstate, "int_gsave");
619
25.8M
}
620
621
/* Copy the interpreter's part of a graphics state. */
622
static int
623
gs_istate_copy(void *to, const void *from)
624
51.5M
{
625
51.5M
    *(int_gstate *) to = *(const int_gstate *)from;
626
51.5M
    return 0;
627
51.5M
}
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
26.0M
{
633
26.0M
    gs_free_object(mem, old, "int_grestore");
634
26.0M
}