/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 | | }; |