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