Coverage Report

Created: 2025-06-24 07:01

/src/ghostpdl/psi/zupath.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
/* Operators related to user paths */
18
#include "ghost.h"
19
#include "oper.h"
20
#include "oparc.h"
21
#include "idict.h"
22
#include "dstack.h"
23
#include "igstate.h"
24
#include "iname.h"
25
#include "iutil.h"
26
#include "store.h"
27
#include "stream.h"
28
#include "ibnum.h"
29
#include "gsmatrix.h"
30
#include "gsstate.h"
31
#include "gscoord.h"
32
#include "gspaint.h"
33
#include "gxfixed.h"
34
#include "gxdevice.h"
35
#include "gspath.h"
36
#include "gzpath.h"   /* for saving path */
37
#include "gzstate.h"    /* for accessing path */
38
39
/* Imported data */
40
extern const gx_device gs_hit_device;
41
extern const int gs_hit_detected;
42
43
/*
44
 * CPSI mode affects two algorithms in this file:
45
 * - CPSI allows ucache to appear anywhere in user paths, even though the
46
 *   PLRM says ucache must appear (if at all) at the beginning
47
 *   (PLRM3 p, 199);
48
 * - After appending an empty user path, in CPSI the current point is
49
 *   defined, even though the PLRM strongly implies this is incorrect
50
 *   (PLRM3 p. 712).
51
 * The 'upath_compat' Boolean controls this behavior.
52
 */
53
54
/* Forward references */
55
static int upath_append(os_ptr, i_ctx_t *, bool);
56
static int upath_stroke(i_ctx_t *, gs_matrix *, bool);
57
58
/* ---------------- Insideness testing ---------------- */
59
60
/* Forward references */
61
static int in_test(i_ctx_t *, int (*)(gs_gstate *));
62
static int in_path(os_ptr, i_ctx_t *, gx_device *);
63
static int in_path_result(i_ctx_t *, int, int);
64
static int in_utest(i_ctx_t *, int (*)(gs_gstate *));
65
static int in_upath(i_ctx_t *, gx_device *);
66
static int in_upath_result(i_ctx_t *, int, int);
67
68
/* <x> <y> ineofill <bool> */
69
/* <userpath> ineofill <bool> */
70
static int
71
zineofill(i_ctx_t *i_ctx_p)
72
45
{
73
45
    return in_test(i_ctx_p, gs_eofill);
74
45
}
75
76
/* <x> <y> infill <bool> */
77
/* <userpath> infill <bool> */
78
static int
79
zinfill(i_ctx_t *i_ctx_p)
80
95
{
81
95
    return in_test(i_ctx_p, gs_fill);
82
95
}
83
84
/* <x> <y> instroke <bool> */
85
/* <userpath> instroke <bool> */
86
static int
87
zinstroke(i_ctx_t *i_ctx_p)
88
8
{
89
8
    return in_test(i_ctx_p, gs_stroke);
90
8
}
91
92
/* <x> <y> <userpath> inueofill <bool> */
93
/* <userpath1> <userpath2> inueofill <bool> */
94
static int
95
zinueofill(i_ctx_t *i_ctx_p)
96
18
{
97
18
    return in_utest(i_ctx_p, gs_eofill);
98
18
}
99
100
/* <x> <y> <userpath> inufill <bool> */
101
/* <userpath1> <userpath2> inufill <bool> */
102
static int
103
zinufill(i_ctx_t *i_ctx_p)
104
37
{
105
37
    return in_utest(i_ctx_p, gs_fill);
106
37
}
107
108
/* <x> <y> <userpath> inustroke <bool> */
109
/* <x> <y> <userpath> <matrix> inustroke <bool> */
110
/* <userpath1> <userpath2> inustroke <bool> */
111
/* <userpath1> <userpath2> <matrix> inustroke <bool> */
112
static int
113
zinustroke(i_ctx_t *i_ctx_p)
114
11
{ /* This is different because of the optional matrix operand. */
115
11
    os_ptr op = osp;
116
11
    int code = gs_gsave(igs);
117
11
    int spop, npop;
118
11
    gs_matrix mat;
119
11
    gx_device hdev;
120
121
11
    if (code < 0)
122
0
        return code;
123
11
    if ((spop = upath_stroke(i_ctx_p, &mat, false)) < 0) {
124
11
        gs_grestore(igs);
125
11
        return spop;
126
11
    }
127
0
    if ((npop = in_path(op - spop, i_ctx_p, &hdev)) < 0) {
128
0
        gs_grestore(igs);
129
0
        return npop;
130
0
    }
131
0
    if (npop > 1)   /* matrix was supplied */
132
0
        code = gs_concat(igs, &mat);
133
0
    if (code >= 0) {
134
0
        dev_proc(&hdev, set_graphics_type_tag)(&hdev, GS_VECTOR_TAG); /* so that fills don't unset dev_color */
135
0
        code = gs_stroke(igs);
136
0
    }
137
0
    return in_upath_result(i_ctx_p, npop + spop, code);
138
0
}
139
140
/* ------ Internal routines ------ */
141
142
/* Do the work of the non-user-path insideness operators. */
143
static int
144
in_test(i_ctx_t *i_ctx_p, int (*paintproc)(gs_gstate *))
145
148
{
146
148
    os_ptr op = osp;
147
148
    gx_device hdev;
148
148
    int npop = in_path(op, i_ctx_p, &hdev);
149
148
    int code;
150
151
148
    if (npop < 0)
152
40
        return npop;
153
108
    dev_proc(&hdev, set_graphics_type_tag)(&hdev, GS_VECTOR_TAG); /* so that fills don't unset dev_color */
154
108
    code = (*paintproc)(igs);
155
108
    return in_path_result(i_ctx_p, npop, code);
156
148
}
157
158
/* Set up a clipping path and device for insideness testing. */
159
static int
160
in_path(os_ptr oppath, i_ctx_t *i_ctx_p, gx_device * phdev)
161
148
{
162
148
    int code = gs_gsave(igs);
163
148
    int npop;
164
148
    double uxy[2];
165
166
148
    if (code < 0)
167
0
        return code;
168
148
    code = num_params(oppath, 2, uxy);
169
148
    if (code >= 0) {   /* Aperture is a single pixel. */
170
108
        gs_point dxy;
171
108
        gs_fixed_rect fr;
172
173
108
        gs_transform(igs, uxy[0], uxy[1], &dxy);
174
108
        fr.p.x = fixed_floor(float2fixed(dxy.x));
175
108
        fr.p.y = fixed_floor(float2fixed(dxy.y));
176
108
        fr.q.x = fr.p.x + fixed_1;
177
108
        fr.q.y = fr.p.y + fixed_1;
178
108
        code = gx_clip_to_rectangle(igs, &fr);
179
108
        npop = 2;
180
108
    } else if (code == gs_error_stackunderflow) {
181
        /* If 0 elements, definitely a stackunderflow; otherwise, */
182
        /* only 1 number, also a stackunderflow. */
183
27
        npop = code;
184
27
    } else {     /* Aperture is a user path. */
185
        /* We have to set the clipping path without disturbing */
186
        /* the current path. */
187
13
        gx_path *ipath = igs->path;
188
13
        gx_path save;
189
190
13
        gx_path_init_local(&save, imemory);
191
13
        gx_path_assign_preserve(&save, ipath);
192
13
        gs_newpath(igs);
193
13
        code = upath_append(oppath, i_ctx_p, false);
194
13
        if (code >= 0)
195
0
            code = gx_clip_to_path(igs);
196
13
        gx_path_assign_free(igs->path, &save);
197
13
        npop = 1;
198
13
    }
199
148
    if (code < 0) {
200
40
        gs_grestore(igs);
201
40
        return code;
202
40
    }
203
108
    code = gx_set_device_color_1(igs);
204
108
    if (code < 0)
205
0
        return code;
206
207
    /* Install the hit detection device. */
208
108
    gx_device_init_on_stack((gx_device *) phdev, (const gx_device *)&gs_hit_device,
209
108
                            imemory);
210
108
    phdev->width = phdev->height = max_int;
211
108
    gx_device_fill_in_procs(phdev);
212
108
    gx_set_device_only(igs, phdev);
213
108
    return npop;
214
108
}
215
216
/* Finish an insideness test. */
217
static int
218
in_path_result(i_ctx_t *i_ctx_p, int npop, int code)
219
108
{
220
108
    os_ptr op = osp;
221
108
    bool result;
222
223
108
    gs_grestore(igs);    /* matches gsave in in_path */
224
108
    if (code == gs_hit_detected)
225
0
        result = true;
226
108
    else if (code == 0)   /* completed painting without a hit */
227
108
        result = false;
228
0
    else      /* error */
229
0
        return code;
230
108
    npop--;
231
108
    ref_stack_pop(&o_stack, npop);
232
108
    op -= npop;
233
108
    make_bool(op, result);
234
108
    return 0;
235
236
108
}
237
238
/* Do the work of the user-path insideness operators. */
239
static int
240
in_utest(i_ctx_t *i_ctx_p, int (*paintproc)(gs_gstate *))
241
55
{
242
55
    gx_device hdev;
243
55
    int npop = in_upath(i_ctx_p, &hdev);
244
55
    int code;
245
246
55
    if (npop < 0)
247
55
        return npop;
248
0
    dev_proc(&hdev, set_graphics_type_tag)(&hdev, GS_VECTOR_TAG); /* so that fills don't unset dev_color */
249
0
    code = (*paintproc)(igs);
250
0
    return in_upath_result(i_ctx_p, npop, code);
251
55
}
252
253
/* Set up a clipping path and device for insideness testing */
254
/* with a user path. */
255
static int
256
in_upath(i_ctx_t *i_ctx_p, gx_device * phdev)
257
55
{
258
55
    os_ptr op = osp;
259
55
    int code = gs_gsave(igs);
260
55
    int npop;
261
262
55
    if (code < 0)
263
0
        return code;
264
55
    if ((code = upath_append(op, i_ctx_p, false)) < 0 ||
265
55
        (code = npop = in_path(op - 1, i_ctx_p, phdev)) < 0
266
55
        ) {
267
55
        gs_grestore(igs);
268
55
        return code;
269
55
    }
270
0
    return npop + 1;
271
55
}
272
273
/* Finish an insideness test with a user path. */
274
static int
275
in_upath_result(i_ctx_t *i_ctx_p, int npop, int code)
276
0
{
277
0
    gs_grestore(igs);    /* matches gsave in in_upath */
278
0
    return in_path_result(i_ctx_p, npop, code);
279
0
}
280
281
/* ---------------- User paths ---------------- */
282
283
/* User path operator codes */
284
typedef enum {
285
    upath_op_setbbox = 0,
286
    upath_op_moveto = 1,
287
    upath_op_rmoveto = 2,
288
    upath_op_lineto = 3,
289
    upath_op_rlineto = 4,
290
    upath_op_curveto = 5,
291
    upath_op_rcurveto = 6,
292
    upath_op_arc = 7,
293
    upath_op_arcn = 8,
294
    upath_op_arct = 9,
295
    upath_op_closepath = 10,
296
    upath_op_ucache = 11
297
} upath_op;
298
299
/* User path interpretation states */
300
typedef enum {
301
    UPS_INITIAL = 1,    /* (no ops yet) */
302
    UPS_UCACHE = 2,   /* ucache */
303
    UPS_SETBBOX = 4,    /* [ucache] setbbox */
304
    UPS_PATH = 8    /* (within path) */
305
} upath_state;
306
307
typedef struct up_data_s {
308
    byte num_args;
309
    byte states_before;
310
    byte state_after;
311
} up_data_t;
312
#define UP_DATA_PATH(n) {n, UPS_SETBBOX | UPS_PATH, UPS_PATH}
313
314
0
#define UPATH_MAX_OP 11
315
0
#define UPATH_REPEAT 32
316
static const up_data_t up_data[UPATH_MAX_OP + 1] = {
317
    {4, UPS_INITIAL | UPS_UCACHE, UPS_SETBBOX}, /* setbbox */
318
    UP_DATA_PATH(2),
319
    UP_DATA_PATH(2),
320
    UP_DATA_PATH(2),
321
    UP_DATA_PATH(2),
322
    UP_DATA_PATH(6),
323
    UP_DATA_PATH(6),
324
    UP_DATA_PATH(5),
325
    UP_DATA_PATH(5),
326
    UP_DATA_PATH(5),
327
    UP_DATA_PATH(0),
328
    {0, UPS_INITIAL, UPS_UCACHE}  /* ucache */
329
};
330
331
/* Declare operator procedures not declared in opextern.h. */
332
int zsetbbox(i_ctx_t *);
333
static int zucache(i_ctx_t *);
334
335
#undef zp
336
static const op_proc_t up_ops[UPATH_MAX_OP + 1] = {
337
    zsetbbox, zmoveto, zrmoveto, zlineto, zrlineto,
338
    zcurveto, zrcurveto, zarc, zarcn, zarct,
339
    zclosepath, zucache
340
};
341
342
/* - ucache - */
343
static int
344
zucache(i_ctx_t *i_ctx_p)
345
196
{
346
    /* A no-op for now. */
347
196
    return 0;
348
196
}
349
350
/* <userpath> uappend - */
351
static int
352
zuappend(i_ctx_t *i_ctx_p)
353
13
{
354
13
    os_ptr op = osp;
355
13
    int code = gs_gsave(igs);
356
357
13
    if (code < 0)
358
0
        return code;
359
13
    if ((code = upath_append(op, i_ctx_p, false)) >= 0)
360
0
        code = gs_upmergepath(igs);
361
13
    gs_grestore(igs);
362
13
    if (code < 0)
363
13
        return code;
364
0
    pop(1);
365
0
    return 0;
366
13
}
367
368
/* <userpath> ueofill - */
369
static int
370
zueofill(i_ctx_t *i_ctx_p)
371
20
{
372
20
    os_ptr op = osp;
373
20
    int code = gs_gsave(igs);
374
375
20
    if (code < 0)
376
0
        return code;
377
20
    if ((code = upath_append(op, i_ctx_p, gs_currentcpsimode(imemory))) >= 0)
378
0
        code = gs_eofill(igs);
379
20
    gs_grestore(igs);
380
20
    if (code < 0)
381
20
        return code;
382
0
    pop(1);
383
0
    return 0;
384
20
}
385
386
/* <userpath> ufill - */
387
static int
388
zufill(i_ctx_t *i_ctx_p)
389
14
{
390
14
    os_ptr op = osp;
391
14
    int code = gs_gsave(igs);
392
393
14
    if (code < 0)
394
0
        return code;
395
14
    if ((code = upath_append(op, i_ctx_p, gs_currentcpsimode(imemory))) >= 0)
396
0
        code = gs_fill(igs);
397
14
    gs_grestore(igs);
398
14
    if (code < 0)
399
14
        return code;
400
0
    pop(1);
401
0
    return 0;
402
14
}
403
404
/* <userpath> ustroke - */
405
/* <userpath> <matrix> ustroke - */
406
static int
407
zustroke(i_ctx_t *i_ctx_p)
408
15
{
409
15
    int code = gs_gsave(igs);
410
15
    int npop;
411
412
15
    if (code < 0)
413
0
        return code;
414
15
    if ((code = npop = upath_stroke(i_ctx_p, NULL, gs_currentcpsimode(imemory))) >= 0)
415
0
        code = gs_stroke(igs);
416
15
    gs_grestore(igs);
417
15
    if (code < 0)
418
15
        return code;
419
0
    pop(npop);
420
0
    return 0;
421
15
}
422
423
/* <userpath> ustrokepath - */
424
/* <userpath> <matrix> ustrokepath - */
425
static int
426
zustrokepath(i_ctx_t *i_ctx_p)
427
4
{
428
4
    gx_path save;
429
4
    gs_matrix saved_matrix;
430
4
    int npop, code = gs_currentmatrix(igs, &saved_matrix);
431
432
4
    if (code < 0)
433
0
        return code;
434
    /* Save and reset the path. */
435
4
    gx_path_init_local(&save, imemory);
436
4
    gx_path_assign_preserve(&save, igs->path);
437
4
    if ((code = npop = upath_stroke(i_ctx_p, NULL, false)) < 0 ||
438
4
        (code = gs_strokepath(igs)) < 0
439
4
        ) {
440
4
        gx_path_assign_free(igs->path, &save);
441
4
        return code;
442
4
    }
443
    /*
444
     * If a matrix was specified then restore the previous matrix.
445
     */
446
0
    if (npop > 1) {
447
0
        if ((code = gs_setmatrix(igs, &saved_matrix)) < 0) {
448
0
            gx_path_assign_free(igs->path, &save);
449
0
            return code;
450
0
        }
451
0
    }
452
0
    gx_path_free(&save, "ustrokepath");
453
0
    pop(npop);
454
0
    return 0;
455
0
}
456
457
/* Compute the path length for user path purposes. */
458
static int
459
path_length_for_upath(const gx_path *ppath)
460
0
{
461
0
    gs_path_enum penum;
462
0
    int op, size = 0;
463
0
    gs_fixed_point pts[3];
464
465
0
    gx_path_enum_init(&penum, ppath);
466
0
    while ((op = gx_path_enum_next(&penum, pts)) != 0) {
467
0
        switch (op) {
468
0
            case gs_pe_moveto:
469
0
            case gs_pe_lineto:
470
0
                size += 3;
471
0
                continue;
472
0
            case gs_pe_curveto:
473
0
                size += 7;
474
0
                continue;
475
0
            case gs_pe_closepath:
476
0
                size += 1;
477
0
                continue;
478
0
            default:
479
0
                return_error(gs_error_unregistered);
480
0
        }
481
0
    }
482
0
    return size;
483
0
}
484
485
static int
486
make_upath(i_ctx_t *i_ctx_p, ref *rupath, gs_gstate *pgs, gx_path *ppath,
487
           bool with_ucache)
488
0
{
489
0
    int size = (with_ucache ? 6 : 5);
490
0
    gs_path_enum penum;
491
0
    gs_rect bbox;
492
0
    int op;
493
0
    ref *next;
494
0
    int code;
495
496
    /* Compute the bounding box. */
497
0
    if ((code = gs_upathbbox(pgs, &bbox, true)) < 0) {
498
        /*
499
         * Note: Adobe throws 'nocurrentpoint' error, but the PLRM does
500
         * not list this as a possible error from 'upath', so if we are
501
         * not in CPSI compatibility mode, we set a reasonable default
502
         * bbox instead.
503
         */
504
0
        if (code != gs_error_nocurrentpoint || gs_currentcpsimode(imemory))
505
0
            return code;
506
0
        bbox.p.x = bbox.p.y = bbox.q.x = bbox.q.y = 0;
507
0
    }
508
509
0
    code = path_length_for_upath(ppath);
510
0
    if (code < 0)
511
0
        return code;
512
0
    size += code;
513
0
    if (size >= 65536)
514
0
        return_error(gs_error_limitcheck);
515
516
0
    code = ialloc_ref_array(rupath, a_all | a_executable, size,
517
0
                            "make_upath");
518
0
    if (code < 0)
519
0
        return code;
520
    /* Construct the path. */
521
0
    next = rupath->value.refs;
522
0
    if (with_ucache) {
523
0
        if ((code = name_enter_string(pgs->memory, "ucache", next)) < 0)
524
0
            return code;
525
0
        r_set_attrs(next, a_executable | l_new);
526
0
        ++next;
527
0
    }
528
0
    make_real_new(next, bbox.p.x);
529
0
    make_real_new(next + 1, bbox.p.y);
530
0
    make_real_new(next + 2, bbox.q.x);
531
0
    make_real_new(next + 3, bbox.q.y);
532
0
    next += 4;
533
0
    if ((code = name_enter_string(pgs->memory, "setbbox", next)) < 0)
534
0
        return code;
535
0
    r_set_attrs(next, a_executable | l_new);
536
0
    ++next;
537
0
    {
538
0
        gs_point pts[3];
539
540
        /* Patch the path in the gstate to set up the enumerator. */
541
0
        gx_path *save_path = pgs->path;
542
543
0
        pgs->path = ppath;
544
0
        gs_path_enum_copy_init(pgs->memory, &penum, pgs, false);
545
0
        pgs->path = save_path;
546
0
        while ((op = gs_path_enum_next(&penum, pts)) != 0) {
547
0
            const char *opstr;
548
549
0
            switch (op) {
550
0
                case gs_pe_moveto:
551
0
                    opstr = "moveto";
552
0
                    goto ml;
553
0
                case gs_pe_lineto:
554
0
                    opstr = "lineto";
555
0
                  ml:make_real_new(next, pts[0].x);
556
0
                    make_real_new(next + 1, pts[0].y);
557
0
                    next += 2;
558
0
                    break;
559
0
                case gs_pe_curveto:
560
0
                    opstr = "curveto";
561
0
                    make_real_new(next, pts[0].x);
562
0
                    make_real_new(next + 1, pts[0].y);
563
0
                    make_real_new(next + 2, pts[1].x);
564
0
                    make_real_new(next + 3, pts[1].y);
565
0
                    make_real_new(next + 4, pts[2].x);
566
0
                    make_real_new(next + 5, pts[2].y);
567
0
                    next += 6;
568
0
                    break;
569
0
                case gs_pe_closepath:
570
0
                    opstr = "closepath";
571
0
                    break;
572
0
                default:
573
0
                    return_error(gs_error_unregistered);
574
0
            }
575
0
            if ((code = name_enter_string(pgs->memory, opstr, next)) < 0)
576
0
                return code;
577
0
            r_set_attrs(next, a_executable);
578
0
            ++next;
579
0
        }
580
0
    }
581
0
    return 0;
582
0
}
583
584
/* <with_ucache> upath <userpath> */
585
static int
586
zupath(i_ctx_t *i_ctx_p)
587
13
{
588
13
    os_ptr op = osp;
589
590
13
    check_type(*op, t_boolean);
591
0
    return make_upath(i_ctx_p, op, igs, igs->path, op->value.boolval);
592
13
}
593
594
static int
595
zgetpath(i_ctx_t *i_ctx_p)
596
0
{
597
0
    os_ptr op = osp;
598
0
    int i, code, path_size, leaf_count;
599
0
    ref *main_ref, *operators[5];
600
601
0
    push(1);
602
0
    path_size = code = path_length_for_upath(igs->path);
603
0
    if (code < 0)
604
0
        return code;
605
0
    leaf_count = (path_size + max_array_size - 1) / max_array_size;
606
0
    code = ialloc_ref_array(op, a_all, leaf_count, "zgetpath_master");
607
0
    if (code < 0)
608
0
        return code;
609
0
    if (path_size == 0)
610
0
        return 0;
611
612
0
    if (dict_find_string(systemdict, "moveto", &operators[1]) <= 0 ||
613
0
        dict_find_string(systemdict, "lineto", &operators[2]) <= 0 ||
614
0
        dict_find_string(systemdict, "curveto", &operators[3]) <= 0 ||
615
0
        dict_find_string(systemdict, "closepath", &operators[4]) <= 0)
616
0
          return_error(gs_error_undefined);
617
618
0
    main_ref = op->value.refs;
619
0
    for (i = 0; i < leaf_count; i++) {
620
0
       int leaf_size = ( i == leaf_count - 1) ? path_size - i * max_array_size : max_array_size;
621
0
       code = ialloc_ref_array(&main_ref[i], a_all | a_executable, leaf_size, "zgetpath_leaf");
622
0
       if (code < 0)
623
0
            return code;
624
0
    }
625
626
0
    {
627
0
        int pe, j, k;
628
0
        gs_path_enum penum;
629
0
        static const int oper_count[5] = { 0, 2, 2, 6, 0 };
630
0
        gs_point pts[3];
631
0
        const double  *fts[6];
632
633
0
        fts[0] = &pts[0].x;
634
0
        fts[1] = &pts[0].y;
635
0
        fts[2] = &pts[1].x;
636
0
        fts[3] = &pts[1].y;
637
0
        fts[4] = &pts[2].x;
638
0
        fts[5] = &pts[2].y;
639
640
0
        main_ref = op->value.refs;
641
0
        gs_path_enum_copy_init(igs->memory, &penum, igs, false);
642
0
        pe = gs_path_enum_next(&penum, pts);
643
0
        if (pe < 0)
644
0
            return pe;
645
0
        k = 0;
646
647
0
        for (i = 0; i < leaf_count; i++) {
648
0
            int leaf_size = ( i == leaf_count - 1) ? path_size - i * max_array_size : max_array_size;
649
0
            ref *leaf_ref = main_ref[i].value.refs;
650
651
0
            for (j = 0; j < leaf_size; j++) {
652
0
                if (k < oper_count[pe])
653
0
                   make_real_new(&leaf_ref[j], (float)*fts[k++]);
654
0
                else {
655
0
                    k = 0;
656
0
                    ref_assign(&leaf_ref[j], operators[pe]);
657
0
                    pe = gs_path_enum_next(&penum, pts);
658
0
                    if (pe <= 0)
659
0
                        return pe;
660
0
                    if (pe >= 5)
661
0
                        return_error(gs_error_unregistered);
662
0
                }
663
0
            }
664
0
        }
665
0
     }
666
0
  return 0;
667
0
}
668
669
/* ------ Internal routines ------ */
670
671
/* Append a user path to the current path. */
672
static inline int
673
upath_append_aux(os_ptr oppath, i_ctx_t *i_ctx_p, int *pnargs, bool upath_compat)
674
145
{
675
145
    upath_state ups = UPS_INITIAL;
676
145
    ref opcodes;
677
678
145
    if (r_has_type(oppath, t__invalid))
679
82
        return_error(gs_error_stackunderflow);
680
63
    if (!r_is_array(oppath))
681
39
        return_error(gs_error_typecheck);
682
24
    check_read(*oppath);
683
24
    gs_newpath(igs);
684
    /****** ROUND tx AND ty ******/
685
686
24
    if ( r_size(oppath) == 2 &&
687
24
         array_get(imemory, oppath, 1, &opcodes) >= 0 &&
688
24
         r_has_type(&opcodes, t_string)
689
24
        ) {     /* 1st element is operands, 2nd is operators */
690
0
        ref operands;
691
0
        int code, format;
692
0
        int repcount = 1;
693
0
        const byte *opp;
694
0
        uint ocount, i = 0;
695
696
0
        array_get(imemory, oppath, 0, &operands);
697
0
        code = num_array_format(&operands);
698
0
        if (code < 0)
699
0
            return code;
700
0
        format = code;
701
0
        check_read(opcodes);
702
0
        opp = opcodes.value.bytes;
703
0
        ocount = r_size(&opcodes);
704
0
        while (ocount--) {
705
0
            byte opx = *opp++;
706
707
0
            if (opx > UPATH_REPEAT)
708
0
                repcount = opx - UPATH_REPEAT;
709
0
            else if (opx > UPATH_MAX_OP)
710
0
                return_error(gs_error_rangecheck);
711
0
            else {   /* operator */
712
0
                const up_data_t data = up_data[opx];
713
714
0
                *pnargs = 0;  /* in case of error */
715
0
                if (upath_compat && opx == upath_op_ucache) {
716
                    /* CPSI does not complain about incorrect ucache
717
                       placement, even though PLRM3 says it's illegal. */
718
0
                    ups = ups > UPS_UCACHE ? ups : data.state_after;
719
0
                } else {
720
0
                    if (!(ups & data.states_before))
721
0
                        return_error(gs_error_typecheck);
722
0
                    ups = data.state_after;
723
0
                }
724
0
                do {
725
0
                    os_ptr op = osp;
726
0
                    byte opargs = data.num_args;
727
728
0
                    while (opargs--) {
729
0
                        push(1);
730
0
                        (*pnargs)++; /* in case of error */
731
0
                        code = num_array_get(imemory, &operands, format, i++, op);
732
0
                        switch (code) {
733
0
                            case t_integer:
734
0
                                r_set_type_attrs(op, t_integer, 0);
735
0
                                break;
736
0
                            case t_real:
737
0
                                r_set_type_attrs(op, t_real, 0);
738
0
                                break;
739
0
                            default:
740
0
                                return_error(gs_error_typecheck);
741
0
                        }
742
0
                    }
743
0
                    code = (*up_ops[opx])(i_ctx_p);
744
0
                    if (code < 0)
745
0
                        return code;
746
0
                }
747
0
                while (--repcount);
748
0
                repcount = 1;
749
0
            }
750
0
        }
751
24
    } else { /* Ordinary executable array. */
752
24
        const ref *arp = oppath;
753
24
        uint ocount = r_size(oppath);
754
24
        long index = 0;
755
24
        int argcount = 0;
756
24
        op_proc_t oproc;
757
24
        int opx, code;
758
759
52
        for (; index < ocount; index++) {
760
33
            ref rup;
761
33
            ref *defp;
762
33
            os_ptr op = osp;
763
33
            up_data_t data;
764
765
33
            *pnargs = argcount;
766
33
            array_get(imemory, arp, index, &rup);
767
33
            switch (r_type(&rup)) {
768
24
                case t_integer:
769
28
                case t_real:
770
28
                    argcount++;
771
28
                    push(1);
772
28
                    *op = rup;
773
28
                    break;
774
5
                case t_name:
775
5
                    if (!r_has_attr(&rup, a_executable) ||
776
5
                        dict_find(systemdict, &rup, &defp) <= 0 ||
777
5
                        r_btype(defp) != t_operator)
778
5
                        return_error(gs_error_typecheck); /* all errors = typecheck */
779
0
                    goto xop;
780
0
                case t_operator:
781
0
                    defp = &rup;
782
0
                  xop:if (!r_has_attr(defp, a_executable))
783
0
                        return_error(gs_error_typecheck);
784
0
                    oproc = real_opproc(defp);
785
0
                    for (opx = 0; opx <= UPATH_MAX_OP; opx++)
786
0
                        if (oproc == up_ops[opx])
787
0
                            break;
788
0
                    if (opx > UPATH_MAX_OP)
789
0
                        return_error(gs_error_typecheck);
790
0
                    data = up_data[opx];
791
0
                    if (argcount != data.num_args)
792
0
                        return_error(gs_error_typecheck);
793
0
                    if (upath_compat && opx == upath_op_ucache) {
794
                        /* CPSI does not complain about incorrect ucache
795
                           placement, even though PLRM3 says it's illegal. */
796
0
                        ups = ups > UPS_UCACHE ? ups : data.state_after;
797
0
                    } else {
798
0
                        if (!(ups & data.states_before))
799
0
                            return_error(gs_error_typecheck);
800
0
                        ups = data.state_after;
801
0
                    }
802
0
                    code = (*up_ops[opx])(i_ctx_p);
803
0
                    if (code < 0) {
804
0
                        if (code == gs_error_nocurrentpoint)
805
0
                            return_error(gs_error_rangecheck); /* CET 11-22 */
806
0
                        return code;
807
0
                    }
808
0
                    argcount = 0;
809
0
                    break;
810
0
                default:
811
0
                    return_error(gs_error_typecheck);
812
33
            }
813
33
        }
814
19
        if (argcount) {
815
17
            *pnargs = argcount;
816
17
            return_error(gs_error_typecheck); /* leftover args */
817
17
        }
818
19
    }
819
2
    if (ups < UPS_SETBBOX)
820
2
        return_error(gs_error_typecheck);  /* no setbbox */
821
0
    if (ups == UPS_SETBBOX && upath_compat) {
822
        /*
823
         * In CPSI compatibility mode, an empty path with a setbbox also
824
         * does a moveto (but only if the path is empty).  Since setbbox
825
         * was the last operator, its operands are still on the o-stack.
826
         */
827
0
        osp += 2;
828
0
        return zmoveto(i_ctx_p);
829
0
    }
830
0
    return 0;
831
0
}
832
static int
833
upath_append(os_ptr oppath, i_ctx_t *i_ctx_p, bool upath_compat)
834
145
{
835
145
    int nargs = 0;
836
145
    int code = upath_append_aux(oppath, i_ctx_p, &nargs, upath_compat);
837
838
145
    if (code < 0) {
839
        /* Pop args on error, to match Adobe interpreters. */
840
145
        pop(nargs);
841
145
        return code;
842
145
    }
843
0
    return 0;
844
145
}
845
846
/* Append a user path to the current path, and then apply or return */
847
/* a transformation if one is supplied. */
848
static int
849
upath_stroke(i_ctx_t *i_ctx_p, gs_matrix *pmat, bool upath_compat)
850
30
{
851
30
    os_ptr op = osp;
852
30
    int code, npop;
853
30
    gs_matrix mat;
854
855
30
    if ((code = read_matrix(imemory, op, &mat)) >= 0) {
856
0
        if ((code = upath_append(op - 1, i_ctx_p, upath_compat)) >= 0) {
857
0
            if (pmat)
858
0
                *pmat = mat;
859
0
            else
860
0
                code = gs_concat(igs, &mat);
861
0
        }
862
0
        npop = 2;
863
30
    } else {
864
30
        if ((code = upath_append(op, i_ctx_p, upath_compat)) >= 0)
865
0
            if (pmat)
866
0
                gs_make_identity(pmat);
867
30
        npop = 1;
868
30
    }
869
30
    return (code < 0 ? code : npop);
870
30
}
871
872
/* ---------------- Initialization procedure ---------------- */
873
874
const op_def zupath_l2_op_defs[] =
875
{
876
    op_def_begin_level2(),
877
                /* Insideness testing */
878
    {"1ineofill", zineofill},
879
    {"1infill", zinfill},
880
    {"1instroke", zinstroke},
881
    {"2inueofill", zinueofill},
882
    {"2inufill", zinufill},
883
    {"2inustroke", zinustroke},
884
                /* User paths */
885
    {"1uappend", zuappend},
886
    {"0ucache", zucache},
887
    {"1ueofill", zueofill},
888
    {"1ufill", zufill},
889
    {"1upath", zupath},
890
    {"1ustroke", zustroke},
891
    {"1ustrokepath", zustrokepath},
892
    op_def_end(0)
893
};
894
895
const op_def zupath_op_defs[] =
896
{
897
    /* Path access for PDF */
898
    {"0.getpath", zgetpath},
899
    op_def_end(0)
900
};