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