/src/ghostpdl/psi/zdevice.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* Copyright (C) 2001-2024 Artifex Software, Inc. |
2 | | All Rights Reserved. |
3 | | |
4 | | This software is provided AS-IS with no warranty, either express or |
5 | | implied. |
6 | | |
7 | | This software is distributed under license and may not be copied, |
8 | | modified or distributed except as expressly authorized under the terms |
9 | | of the license contained in the file LICENSE in this distribution. |
10 | | |
11 | | Refer to licensing information at http://www.artifex.com or contact |
12 | | Artifex Software, Inc., 39 Mesa Street, Suite 108A, San Francisco, |
13 | | CA 94129, USA, for further information. |
14 | | */ |
15 | | |
16 | | |
17 | | /* Device-related operators */ |
18 | | #include "string_.h" |
19 | | #include "ghost.h" |
20 | | #include "oper.h" |
21 | | #include "ialloc.h" |
22 | | #include "idict.h" |
23 | | #include "igstate.h" |
24 | | #include "imain.h" |
25 | | #include "imemory.h" |
26 | | #include "iname.h" |
27 | | #include "interp.h" |
28 | | #include "iparam.h" |
29 | | #include "ivmspace.h" |
30 | | #include "gsmatrix.h" |
31 | | #include "gsstate.h" |
32 | | #include "gxdevice.h" |
33 | | #include "gxalloc.h" |
34 | | #include "gxgetbit.h" |
35 | | #include "store.h" |
36 | | #include "gsicc_manage.h" |
37 | | #include "gxdevsop.h" |
38 | | |
39 | | struct_proc_finalize(psi_device_ref_finalize); |
40 | | |
41 | | static |
42 | 12.3M | ENUM_PTRS_WITH(psi_device_ref_enum_ptrs, psi_device_ref *devref) |
43 | 2.94M | { |
44 | 2.94M | return 0; |
45 | 0 | } |
46 | 9.39M | case 0: |
47 | 9.39M | { |
48 | 9.39M | if (devref->device != NULL && devref->device->memory != NULL) { |
49 | 2.94M | ENUM_RETURN(gx_device_enum_ptr(devref->device)); |
50 | 2.94M | } |
51 | 6.45M | return 0; |
52 | 9.39M | } |
53 | 12.3M | ENUM_PTRS_END |
54 | | |
55 | | static |
56 | 9.39M | RELOC_PTRS_WITH(psi_device_ref_reloc_ptrs, psi_device_ref *devref) |
57 | 9.39M | if (devref->device != NULL && devref->device->memory != NULL) { |
58 | 2.94M | devref->device = gx_device_reloc_ptr(devref->device, gcst); |
59 | 2.94M | } |
60 | 9.39M | RELOC_PTRS_END |
61 | | |
62 | | gs_private_st_composite_use_final(st_psi_device_ref, psi_device_ref, "psi_device_ref_t", |
63 | | psi_device_ref_enum_ptrs, psi_device_ref_reloc_ptrs, psi_device_ref_finalize); |
64 | | |
65 | | void |
66 | | psi_device_ref_finalize(const gs_memory_t *cmem, void *vptr) |
67 | 12.6M | { |
68 | 12.6M | psi_device_ref *pdref = (psi_device_ref *)vptr; |
69 | 12.6M | (void)cmem; |
70 | | |
71 | | /* pdref->device->memory == NULL indicates either a device prototype |
72 | | or a device allocated on the stack rather than the heap |
73 | | */ |
74 | 12.6M | if (pdref->device != NULL && pdref->device->memory != NULL) |
75 | 12.6M | rc_decrement(pdref->device, "psi_device_ref_finalize"); |
76 | | |
77 | 12.6M | pdref->device = NULL; |
78 | 12.6M | } |
79 | | |
80 | | /* <device> <keep_open> .copydevice2 <newdevice> */ |
81 | | static int |
82 | | zcopydevice2(i_ctx_t *i_ctx_p) |
83 | 315k | { |
84 | 315k | os_ptr op = osp; |
85 | 315k | gx_device *new_dev; |
86 | 315k | int code; |
87 | 315k | psi_device_ref *psdev; |
88 | | |
89 | 315k | check_op(2); |
90 | 315k | check_read_type(op[-1], t_device); |
91 | 315k | check_type(*op, t_boolean); |
92 | 315k | if (op[-1].value.pdevice == NULL) |
93 | | /* This can happen if we invalidated devices on the stack by calling nulldevice after they were pushed */ |
94 | 0 | return_error(gs_error_undefined); |
95 | | |
96 | 315k | if (gs_is_path_control_active((const gs_memory_t *)i_ctx_p->memory.current)) { |
97 | 0 | const gx_device *dev = (const gx_device *)op[-1].value.pdevice->device; |
98 | |
|
99 | 0 | if (gs_check_device_permission((gs_memory_t *)i_ctx_p->memory.current, dev->dname, strlen(dev->dname)) == 0) |
100 | 0 | return_error(gs_error_invalidaccess); |
101 | 0 | } |
102 | | |
103 | 315k | code = gs_copydevice2(&new_dev, op[-1].value.pdevice->device, op->value.boolval, |
104 | 315k | imemory); |
105 | 315k | if (code < 0) |
106 | 0 | return code; |
107 | 315k | new_dev->memory = imemory; |
108 | | |
109 | 315k | psdev = gs_alloc_struct(imemory, psi_device_ref, &st_psi_device_ref, "zcopydevice2"); |
110 | 315k | if (!psdev) { |
111 | 0 | rc_decrement(new_dev, "zcopydevice2"); |
112 | 0 | return_error(gs_error_VMerror); |
113 | 0 | } |
114 | 315k | psdev->device = new_dev; |
115 | | |
116 | 315k | make_tav(op - 1, t_device, icurrent_space | a_all, pdevice, psdev); |
117 | 315k | pop(1); |
118 | 315k | return 0; |
119 | 315k | } |
120 | | |
121 | | /* - currentdevice <device> */ |
122 | | /* Returns the current device in the graphics state */ |
123 | | int |
124 | | zcurrentdevice(i_ctx_t *i_ctx_p) |
125 | 4.75M | { |
126 | 4.75M | os_ptr op = osp; |
127 | 4.75M | gx_device *dev = gs_currentdevice(igs); |
128 | 4.75M | gs_ref_memory_t *mem = (gs_ref_memory_t *) dev->memory; |
129 | 4.75M | psi_device_ref *psdev; |
130 | | |
131 | 4.75M | psdev = gs_alloc_struct(dev->memory, psi_device_ref, &st_psi_device_ref, "zcurrentdevice"); |
132 | 4.75M | if (!psdev) { |
133 | 0 | return_error(gs_error_VMerror); |
134 | 0 | } |
135 | 4.75M | psdev->device = dev; |
136 | 4.75M | rc_increment(dev); |
137 | | |
138 | 4.75M | push(1); |
139 | 4.75M | make_tav(op, t_device, imemory_space(mem) | a_all, pdevice, psdev); |
140 | 4.75M | return 0; |
141 | 4.75M | } |
142 | | |
143 | | /* - .currentoutputdevice <device> */ |
144 | | /* Returns the *output* device - which will often |
145 | | be the same as above, but not always: if a compositor |
146 | | or other forwarding device, or subclassing device is |
147 | | in force, that will be referenced by the graphics state |
148 | | rather than the output device. |
149 | | This is equivalent of currentdevice device, but returns |
150 | | the *device* object, rather than the dictionary describing |
151 | | the device and device state. |
152 | | */ |
153 | | int |
154 | | zcurrentoutputdevice(i_ctx_t *i_ctx_p) |
155 | 789k | { |
156 | 789k | os_ptr op = osp; |
157 | 789k | gx_device *odev = NULL, *dev = gs_currentdevice(igs); |
158 | 789k | psi_device_ref *psdev; |
159 | 789k | gs_ref_memory_t *mem = (gs_ref_memory_t *) dev->memory; |
160 | 789k | int code = dev_proc(dev, dev_spec_op)(dev, |
161 | 789k | gxdso_current_output_device, (void *)&odev, 0); |
162 | 789k | if (code < 0) |
163 | 0 | return code; |
164 | | |
165 | 789k | psdev = gs_alloc_struct(dev->memory, psi_device_ref, &st_psi_device_ref, "zcurrentdevice"); |
166 | 789k | if (!psdev) { |
167 | 0 | return_error(gs_error_VMerror); |
168 | 0 | } |
169 | 789k | psdev->device = odev; |
170 | 789k | rc_increment(odev); |
171 | | |
172 | 789k | push(1); |
173 | 789k | make_tav(op, t_device, imemory_space(mem) | a_all, pdevice, psdev); |
174 | 789k | return 0; |
175 | 789k | } |
176 | | |
177 | | /* <device> .devicename <string> */ |
178 | | static int |
179 | | zdevicename(i_ctx_t *i_ctx_p) |
180 | 7.61M | { |
181 | 7.61M | os_ptr op = osp; |
182 | 7.61M | const char *dname; |
183 | | |
184 | 7.61M | check_op(1); |
185 | 7.61M | check_read_type(*op, t_device); |
186 | 7.61M | if (op->value.pdevice == NULL) |
187 | | /* This can happen if we invalidated devices on the stack by calling nulldevice after they were pushed */ |
188 | 0 | return_error(gs_error_undefined); |
189 | | |
190 | 7.61M | dname = op->value.pdevice->device->dname; |
191 | 7.61M | make_const_string(op, avm_foreign | a_readonly, strlen(dname), |
192 | 7.61M | (const byte *)dname); |
193 | 7.61M | return 0; |
194 | 7.61M | } |
195 | | |
196 | | /* - .doneshowpage - */ |
197 | | static int |
198 | | zdoneshowpage(i_ctx_t *i_ctx_p) |
199 | 133k | { |
200 | 133k | gx_device *dev = gs_currentdevice(igs); |
201 | 133k | gx_device *tdev = (*dev_proc(dev, get_page_device)) (dev); |
202 | | |
203 | 133k | if (tdev != 0) |
204 | 133k | tdev->ShowpageCount++; |
205 | 133k | return 0; |
206 | 133k | } |
207 | | |
208 | | /* - flushpage - */ |
209 | | int |
210 | | zflushpage(i_ctx_t *i_ctx_p) |
211 | 162k | { |
212 | 162k | return gs_flushpage(igs); |
213 | 162k | } |
214 | | |
215 | | /* <device> <x> <y> <width> <max_height> <alpha?> <std_depth|null> <string> */ |
216 | | /* .getbitsrect <height> <substring> */ |
217 | | static int |
218 | | zgetbitsrect(i_ctx_t *i_ctx_p) |
219 | 0 | { /* |
220 | | * alpha? is 0 for no alpha, -1 for alpha first, 1 for alpha last. |
221 | | * std_depth is null for native pixels, depth/component for |
222 | | * standard color space. |
223 | | */ |
224 | 0 | os_ptr op = osp; |
225 | 0 | gx_device *dev; |
226 | 0 | gs_int_rect rect; |
227 | 0 | gs_get_bits_params_t params; |
228 | 0 | int w, h; |
229 | 0 | gs_get_bits_options_t options = |
230 | 0 | GB_ALIGN_ANY | GB_RETURN_COPY | GB_OFFSET_0 | GB_RASTER_STANDARD | |
231 | 0 | GB_PACKING_CHUNKY; |
232 | 0 | int depth; |
233 | 0 | uint raster; |
234 | 0 | int num_rows; |
235 | 0 | int code; |
236 | |
|
237 | 0 | check_op(7); |
238 | 0 | check_read_type(op[-7], t_device); |
239 | 0 | if (op[-7].value.pdevice == NULL) |
240 | | /* This can happen if we invalidated devices on the stack by calling nulldevice after they were pushed */ |
241 | 0 | return_error(gs_error_undefined); |
242 | | |
243 | 0 | dev = op[-7].value.pdevice->device; |
244 | |
|
245 | 0 | check_int_leu(op[-6], dev->width); |
246 | 0 | rect.p.x = op[-6].value.intval; |
247 | 0 | check_int_leu(op[-5], dev->height); |
248 | 0 | rect.p.y = op[-5].value.intval; |
249 | 0 | check_int_leu(op[-4], dev->width); |
250 | 0 | w = op[-4].value.intval; |
251 | 0 | check_int_leu(op[-3], dev->height); |
252 | 0 | h = op[-3].value.intval; |
253 | 0 | check_type(op[-2], t_integer); |
254 | | /* |
255 | | * We use if/else rather than switch because the value is long, |
256 | | * which is not supported as a switch value in pre-ANSI C. |
257 | | */ |
258 | 0 | if (op[-2].value.intval == -1) |
259 | 0 | options |= GB_ALPHA_FIRST; |
260 | 0 | else if (op[-2].value.intval == 0) |
261 | 0 | options |= GB_ALPHA_NONE; |
262 | 0 | else if (op[-2].value.intval == 1) |
263 | 0 | options |= GB_ALPHA_LAST; |
264 | 0 | else |
265 | 0 | return_error(gs_error_rangecheck); |
266 | 0 | if (r_has_type(op - 1, t_null)) { |
267 | 0 | options |= GB_COLORS_NATIVE; |
268 | 0 | depth = dev->color_info.depth; |
269 | 0 | } else { |
270 | 0 | static const gs_get_bits_options_t depths[17] = { |
271 | 0 | 0, GB_DEPTH_1, GB_DEPTH_2, 0, GB_DEPTH_4, 0, 0, 0, GB_DEPTH_8, |
272 | 0 | 0, 0, 0, GB_DEPTH_12, 0, 0, 0, GB_DEPTH_16 |
273 | 0 | }; |
274 | 0 | gs_get_bits_options_t depth_option; |
275 | 0 | int std_depth; |
276 | |
|
277 | 0 | check_int_leu(op[-1], 16); |
278 | 0 | std_depth = (int)op[-1].value.intval; |
279 | 0 | depth_option = depths[std_depth]; |
280 | 0 | if (depth_option == 0) |
281 | 0 | return_error(gs_error_rangecheck); |
282 | 0 | options |= depth_option | GB_COLORS_NATIVE; |
283 | 0 | depth = (dev->color_info.num_components + |
284 | 0 | (options & GB_ALPHA_NONE ? 0 : 1)) * std_depth; |
285 | 0 | } |
286 | 0 | if (w == 0) |
287 | 0 | return_error(gs_error_rangecheck); |
288 | 0 | raster = (w * depth + 7) >> 3; |
289 | 0 | check_write_type(*op, t_string); |
290 | 0 | num_rows = r_size(op) / raster; |
291 | 0 | h = min(h, num_rows); |
292 | 0 | if (h == 0) |
293 | 0 | return_error(gs_error_rangecheck); |
294 | 0 | rect.q.x = rect.p.x + w; |
295 | 0 | rect.q.y = rect.p.y + h; |
296 | 0 | params.options = options; |
297 | 0 | params.data[0] = op->value.bytes; |
298 | 0 | code = (*dev_proc(dev, get_bits_rectangle))(dev, &rect, ¶ms); |
299 | 0 | if (code < 0) |
300 | 0 | return code; |
301 | 0 | make_int(op - 7, h); |
302 | 0 | op[-6] = *op; |
303 | 0 | r_set_size(op - 6, h * raster); |
304 | 0 | pop(6); |
305 | 0 | return 0; |
306 | 0 | } |
307 | | |
308 | | /* <int> .getdevice <device> */ |
309 | | static int |
310 | | zgetdevice(i_ctx_t *i_ctx_p) |
311 | 7.14M | { |
312 | 7.14M | os_ptr op = osp; |
313 | 7.14M | const gx_device *dev; |
314 | 7.14M | psi_device_ref *psdev; |
315 | | |
316 | 7.14M | check_op(1); |
317 | 7.14M | check_type(*op, t_integer); |
318 | 7.14M | if (op->value.intval != (int)(op->value.intval)) |
319 | 0 | return_error(gs_error_rangecheck); /* won't fit in an int */ |
320 | 7.14M | dev = gs_getdevice((int)(op->value.intval)); |
321 | 7.14M | if (dev == 0) /* index out of range */ |
322 | 324k | return_error(gs_error_rangecheck); |
323 | | |
324 | 6.82M | psdev = gs_alloc_struct(imemory, psi_device_ref, &st_psi_device_ref, "zgetdevice"); |
325 | 6.82M | if (!psdev) { |
326 | 0 | return_error(gs_error_VMerror); |
327 | 0 | } |
328 | | /* gs_getdevice() returns a device prototype, so no reference counting required */ |
329 | 6.82M | psdev->device = (gx_device *)dev; |
330 | | |
331 | | /* Device prototypes are read-only; */ |
332 | 6.82M | make_tav(op, t_device, imemory_space(iimemory) | a_readonly, pdevice, psdev); |
333 | 6.82M | return 0; |
334 | 6.82M | } |
335 | | |
336 | | /* - .getdefaultdevice <device> */ |
337 | | static int |
338 | | zgetdefaultdevice(i_ctx_t *i_ctx_p) |
339 | 0 | { |
340 | 0 | os_ptr op = osp; |
341 | 0 | const gx_device *dev; |
342 | 0 | psi_device_ref *psdev; |
343 | |
|
344 | 0 | dev = gs_getdefaultlibdevice(imemory); |
345 | 0 | if (dev == 0) /* couldn't find a default device */ |
346 | 0 | return_error(gs_error_unknownerror); |
347 | | |
348 | 0 | psdev = gs_alloc_struct(imemory, psi_device_ref, &st_psi_device_ref, "zgetdefaultdevice"); |
349 | 0 | if (!psdev) { |
350 | 0 | return_error(gs_error_VMerror); |
351 | 0 | } |
352 | | /* gs_getdefaultlibdevice() returns a device prototype, so no reference counting required */ |
353 | 0 | psdev->device = (gx_device *)dev; |
354 | |
|
355 | 0 | push(1); |
356 | 0 | make_tav(op, t_device, imemory_space(iimemory) | a_readonly, pdevice, psdev); |
357 | 0 | return 0; |
358 | 0 | } |
359 | | |
360 | | /* Common functionality of zgethardwareparms & zgetdeviceparams */ |
361 | | static int |
362 | | zget_device_params(i_ctx_t *i_ctx_p, bool is_hardware) |
363 | 4.05M | { |
364 | 4.05M | os_ptr op = osp; |
365 | 4.05M | ref rkeys; |
366 | 4.05M | gx_device *dev; |
367 | 4.05M | stack_param_list list; |
368 | 4.05M | int code; |
369 | 4.05M | ref *pmark; |
370 | | |
371 | 4.05M | check_op(2); |
372 | 4.05M | check_read_type(op[-1], t_device); |
373 | | |
374 | 4.05M | if(!r_has_type(op, t_null)) { |
375 | 3.56M | check_type(*op, t_dictionary); |
376 | 3.56M | } |
377 | 4.05M | rkeys = *op; |
378 | 4.05M | if (op[-1].value.pdevice == NULL) |
379 | | /* This can happen if we invalidated devices on the stack by calling nulldevice after they were pushed */ |
380 | 0 | return_error(gs_error_undefined); |
381 | | |
382 | 4.05M | dev = op[-1].value.pdevice->device; |
383 | | |
384 | 4.05M | ref_stack_pop(&o_stack, 1); |
385 | 4.05M | stack_param_list_write(&list, &o_stack, &rkeys, iimemory); |
386 | 4.05M | code = gs_get_device_or_hardware_params(dev, (gs_param_list *) & list, |
387 | 4.05M | is_hardware); |
388 | 4.05M | if (code < 0) { |
389 | | /* We have to put back the top argument. */ |
390 | 0 | if (list.count > 0) |
391 | 0 | ref_stack_pop(&o_stack, list.count * 2 - 1); |
392 | 0 | else { |
393 | 0 | code = ref_stack_push(&o_stack, 1); |
394 | 0 | if (code < 0) |
395 | 0 | return code; |
396 | 0 | } |
397 | 0 | *osp = rkeys; |
398 | 0 | return code; |
399 | 0 | } |
400 | 4.05M | pmark = ref_stack_index(&o_stack, list.count * 2); |
401 | 4.05M | if (pmark == NULL) |
402 | 0 | return_error(gs_error_stackunderflow); |
403 | 4.05M | make_mark(pmark); |
404 | 4.05M | return 0; |
405 | 4.05M | } |
406 | | /* <device> <key_dict|null> .getdeviceparams <mark> <name> <value> ... */ |
407 | | static int |
408 | | zgetdeviceparams(i_ctx_t *i_ctx_p) |
409 | 4.05M | { |
410 | 4.05M | return zget_device_params(i_ctx_p, false); |
411 | 4.05M | } |
412 | | /* <device> <key_dict|null> .gethardwareparams <mark> <name> <value> ... */ |
413 | | static int |
414 | | zgethardwareparams(i_ctx_t *i_ctx_p) |
415 | 0 | { |
416 | 0 | return zget_device_params(i_ctx_p, true); |
417 | 0 | } |
418 | | |
419 | | /* <matrix> <width> <height> <palette> <word?> makewordimagedevice <device> */ |
420 | | static int |
421 | | zmakewordimagedevice(i_ctx_t *i_ctx_p) |
422 | 0 | { |
423 | 0 | os_ptr op = osp; |
424 | 0 | os_ptr op1 = op - 1; |
425 | 0 | gs_matrix imat; |
426 | 0 | gx_device *new_dev; |
427 | 0 | const byte *colors; |
428 | 0 | int colors_size; |
429 | 0 | int code; |
430 | 0 | psi_device_ref *psdev; |
431 | |
|
432 | 0 | check_op(5); |
433 | 0 | check_int_leu(op[-3], max_uint >> 1); /* width */ |
434 | 0 | check_int_leu(op[-2], max_uint >> 1); /* height */ |
435 | 0 | check_type(*op, t_boolean); |
436 | 0 | if (r_has_type(op1, t_null)) { /* true color */ |
437 | 0 | colors = 0; |
438 | 0 | colors_size = -24; /* 24-bit true color */ |
439 | 0 | } else if (r_has_type(op1, t_integer)) { |
440 | | /* |
441 | | * We use if/else rather than switch because the value is long, |
442 | | * which is not supported as a switch value in pre-ANSI C. |
443 | | */ |
444 | 0 | if (op1->value.intval != 16 && op1->value.intval != 24 && |
445 | 0 | op1->value.intval != 32 |
446 | 0 | ) |
447 | 0 | return_error(gs_error_rangecheck); |
448 | 0 | colors = 0; |
449 | 0 | colors_size = -op1->value.intval; |
450 | 0 | } else { |
451 | 0 | check_type(*op1, t_string); /* palette */ |
452 | 0 | if (r_size(op1) > 3 * 256) |
453 | 0 | return_error(gs_error_rangecheck); |
454 | 0 | colors = op1->value.bytes; |
455 | 0 | colors_size = r_size(op1); |
456 | 0 | } |
457 | 0 | if ((code = read_matrix(imemory, op - 4, &imat)) < 0) |
458 | 0 | return code; |
459 | | /* Everything OK, create device */ |
460 | 0 | code = gs_makewordimagedevice(&new_dev, &imat, |
461 | 0 | (int)op[-3].value.intval, |
462 | 0 | (int)op[-2].value.intval, |
463 | 0 | colors, colors_size, |
464 | 0 | op->value.boolval, true, imemory); |
465 | 0 | if (code == 0) { |
466 | 0 | new_dev->memory = imemory; |
467 | |
|
468 | 0 | psdev = gs_alloc_struct(imemory, psi_device_ref, &st_psi_device_ref, "zcurrentdevice"); |
469 | 0 | if (!psdev) { |
470 | 0 | rc_decrement(new_dev, "zmakewordimagedevice"); |
471 | 0 | return_error(gs_error_VMerror); |
472 | 0 | } |
473 | 0 | psdev->device = new_dev; |
474 | 0 | rc_increment(new_dev); |
475 | 0 | make_tav(op - 4, t_device, imemory_space(iimemory) | a_all, pdevice, psdev); |
476 | 0 | pop(4); |
477 | 0 | } |
478 | 0 | return code; |
479 | 0 | } |
480 | | |
481 | | /* - nulldevice - */ |
482 | | /* Note that nulldevice clears the current pagedevice. */ |
483 | | static int |
484 | | znulldevice(i_ctx_t *i_ctx_p) |
485 | 257k | { |
486 | 257k | int code = gs_nulldevice(igs); |
487 | 257k | clear_pagedevice(istate); |
488 | 257k | return code; |
489 | 257k | } |
490 | | |
491 | | extern void print_resource_usage(const gs_main_instance *, gs_dual_memory_t *, |
492 | | const char *); |
493 | | |
494 | | /* <num_copies> <flush_bool> .outputpage - */ |
495 | | static int |
496 | | zoutputpage(i_ctx_t *i_ctx_p) |
497 | 133k | { |
498 | 133k | os_ptr op = osp; |
499 | 133k | int code; |
500 | | |
501 | 133k | check_op(2); |
502 | 133k | check_type(op[-1], t_integer); |
503 | 133k | check_type(*op, t_boolean); |
504 | 133k | if (gs_debug[':']) { |
505 | 0 | gs_main_instance *minst = get_minst_from_memory((gs_memory_t *)i_ctx_p->memory.current->non_gc_memory); |
506 | |
|
507 | 0 | print_resource_usage(minst, &(i_ctx_p->memory), "Outputpage start"); |
508 | 0 | } |
509 | 133k | code = gs_output_page(igs, (int)op[-1].value.intval, |
510 | 133k | op->value.boolval); |
511 | 133k | if (code < 0) |
512 | 13.3k | return code; |
513 | 119k | pop(2); |
514 | 119k | if (gs_debug[':']) { |
515 | 0 | gs_main_instance *minst = get_minst_from_memory((gs_memory_t *)i_ctx_p->memory.current->non_gc_memory); |
516 | |
|
517 | 0 | print_resource_usage(minst, &(i_ctx_p->memory), "Outputpage end"); |
518 | 0 | } |
519 | 119k | return 0; |
520 | 133k | } |
521 | | |
522 | | /* <device> <policy_dict|null> <require_all> <mark> <name> <value> ... */ |
523 | | /* .putdeviceparams */ |
524 | | /* (on success) <device> <eraseflag> */ |
525 | | /* (on failure) <device> <policy_dict|null> <require_all> <mark> */ |
526 | | /* <name1> <error1> ... */ |
527 | | /* For a key that simply was not recognized, if require_all is true, */ |
528 | | /* the result will be an /undefined error; if require_all is false, */ |
529 | | /* the key will be ignored. */ |
530 | | /* Note that .putdeviceparams clears the current pagedevice. */ |
531 | | static int |
532 | | zputdeviceparams(i_ctx_t *i_ctx_p) |
533 | 1.60M | { |
534 | 1.60M | uint count = ref_stack_counttomark(&o_stack); |
535 | 1.60M | ref *prequire_all; |
536 | 1.60M | ref *ppolicy; |
537 | 1.60M | ref *pdev; |
538 | 1.60M | gx_device *dev; |
539 | 1.60M | stack_param_list list; |
540 | 1.60M | int code; |
541 | 1.60M | int old_width, old_height; |
542 | 1.60M | int i, dest; |
543 | | |
544 | 1.60M | if (count == 0) |
545 | 0 | return_error(gs_error_unmatchedmark); |
546 | 1.60M | prequire_all = ref_stack_index(&o_stack, count); |
547 | 1.60M | if (prequire_all == NULL) |
548 | 0 | return_error(gs_error_stackunderflow); |
549 | 1.60M | ppolicy = ref_stack_index(&o_stack, count + 1); |
550 | 1.60M | if (ppolicy == NULL) |
551 | 0 | return_error(gs_error_stackunderflow); |
552 | 1.60M | pdev = ref_stack_index(&o_stack, count + 2); |
553 | 1.60M | if (pdev == NULL) |
554 | 0 | return_error(gs_error_stackunderflow); |
555 | 1.60M | check_type_only(*prequire_all, t_boolean); |
556 | 1.60M | check_write_type_only(*pdev, t_device); |
557 | 1.60M | dev = pdev->value.pdevice->device; |
558 | 1.60M | if (dev == NULL) |
559 | | /* This can happen if we invalidated devices on the stack by calling nulldevice after they were pushed */ |
560 | 0 | return_error(gs_error_undefined); |
561 | 1.60M | code = stack_param_list_read(&list, &o_stack, 0, ppolicy, |
562 | 1.60M | prequire_all->value.boolval, iimemory); |
563 | 1.60M | if (code < 0) |
564 | 0 | return code; |
565 | 1.60M | old_width = dev->width; |
566 | 1.60M | old_height = dev->height; |
567 | 1.60M | code = gs_putdeviceparams(dev, (gs_param_list *) & list); |
568 | | /* Check for names that were undefined or caused errors. */ |
569 | 29.3M | for (dest = count - 2, i = 0; i < count >> 1; i++) { |
570 | 27.7M | ref *o; |
571 | 27.7M | if (list.results[i] < 0) { |
572 | 6.10k | o = ref_stack_index(&o_stack, dest); |
573 | 6.10k | if (o == NULL) |
574 | 0 | continue; |
575 | 6.10k | *o = *ref_stack_index(&o_stack, count - (i << 1) - 2); |
576 | 6.10k | o = ref_stack_index(&o_stack, dest - 1); |
577 | 6.10k | if (o == NULL) |
578 | 0 | continue; |
579 | 6.10k | gs_errorname(i_ctx_p, list.results[i], o); |
580 | 6.10k | dest -= 2; |
581 | 6.10k | } |
582 | 27.7M | } |
583 | 1.60M | iparam_list_release(&list); |
584 | 1.60M | if (code < 0) { /* There were errors reported. */ |
585 | 781 | ref_stack_pop(&o_stack, dest + 1); |
586 | 781 | return (code == gs_error_Fatal) ? code : 0; /* cannot continue from Fatal */ |
587 | 781 | } |
588 | 1.60M | if (code > 0 || (code == 0 && (dev->width != old_width || dev->height != old_height))) { |
589 | | /* |
590 | | * The device was open and is now closed, or its dimensions have |
591 | | * changed. If it was the current device, call setdevice to |
592 | | * reinstall it and erase the page. |
593 | | */ |
594 | | /****** DOESN'T FIND ALL THE GSTATES THAT REFERENCE THE DEVICE. ******/ |
595 | 367k | if (gs_currentdevice(igs) == dev) { |
596 | 204k | bool was_open = dev->is_open; |
597 | | |
598 | 204k | code = gs_setdevice_no_erase(igs, dev); |
599 | | /* If the device wasn't closed, setdevice won't erase the page. */ |
600 | 204k | if (was_open && code >= 0) |
601 | 187k | code = 1; |
602 | 204k | } |
603 | 367k | } |
604 | 1.60M | if (code < 0) |
605 | 0 | return code; |
606 | 1.60M | ref_stack_pop(&o_stack, count + 1); |
607 | 1.60M | make_bool(osp, code); |
608 | 1.60M | clear_pagedevice(istate); |
609 | 1.60M | return 0; |
610 | 1.60M | } |
611 | | |
612 | | int |
613 | | zsetdevice_no_safer(i_ctx_t *i_ctx_p, gx_device *new_dev) |
614 | 951k | { |
615 | 951k | int code; |
616 | | |
617 | 951k | if (new_dev == NULL) |
618 | 0 | return gs_note_error(gs_error_undefined); |
619 | | |
620 | 951k | code = gs_setdevice_no_erase(igs, new_dev); |
621 | 951k | if (code < 0) |
622 | 0 | return code; |
623 | | |
624 | 951k | clear_pagedevice(istate); |
625 | 951k | return code; |
626 | 951k | } |
627 | | |
628 | | /* <device> .setdevice <eraseflag> */ |
629 | | /* Note that .setdevice clears the current pagedevice. */ |
630 | | int |
631 | | zsetdevice(i_ctx_t *i_ctx_p) |
632 | 951k | { |
633 | 951k | gx_device *odev = NULL, *dev = gs_currentdevice(igs); |
634 | 951k | gx_device *ndev = NULL; |
635 | 951k | os_ptr op = osp; |
636 | 951k | int code = dev_proc(dev, dev_spec_op)(dev, |
637 | 951k | gxdso_current_output_device, (void *)&odev, 0); |
638 | | |
639 | 951k | if (code < 0) |
640 | 0 | return code; |
641 | 951k | check_op(1); |
642 | 951k | check_write_type(*op, t_device); |
643 | | |
644 | 951k | if (op->value.pdevice == 0) |
645 | 0 | return gs_note_error(gs_error_undefined); |
646 | | |
647 | | /* slightly icky special case: the new device may not have had |
648 | | * it's procs initialised, at this point - but we need to check |
649 | | * whether we're being asked to change the device here |
650 | | */ |
651 | 951k | if (dev_proc((op->value.pdevice->device), dev_spec_op) == NULL) |
652 | 0 | ndev = op->value.pdevice->device; |
653 | 951k | else |
654 | 951k | code = dev_proc((op->value.pdevice->device), dev_spec_op)(op->value.pdevice->device, |
655 | 951k | gxdso_current_output_device, (void *)&ndev, 0); |
656 | | |
657 | 951k | if (code < 0) |
658 | 0 | return code; |
659 | | |
660 | 951k | if (odev->LockSafetyParams) { /* do additional checking if locked */ |
661 | 0 | if(ndev != odev) /* don't allow a different device */ |
662 | 0 | return_error(gs_error_invalidaccess); |
663 | 0 | } |
664 | 951k | code = zsetdevice_no_safer(i_ctx_p, op->value.pdevice->device); |
665 | 951k | make_bool(op, code != 0); /* erase page if 1 */ |
666 | 951k | return code; |
667 | 951k | } |
668 | | |
669 | | /* Custom PostScript operator '.special_op' is used to implement |
670 | | * 'dev_spec_op' access from PostScript. Initially this is intended |
671 | | * to be used to recover individual device parameters from certain |
672 | | * devices (pdfwrite, ps2write etc). In the future we may choose to |
673 | | * increase the devices which can support this, and make more types |
674 | | * of 'spec_op' available from the PostScript world. |
675 | | */ |
676 | | |
677 | | /* We use this structure in a table below which allows us to add new |
678 | | * 'spec_op's with minimum fuss. |
679 | | */ |
680 | | typedef struct spec_op_s spec_op_t; |
681 | | struct spec_op_s { |
682 | | char *name; /* C string representing the name of the spec_op */ |
683 | | int spec_op; /* Integer used to switch on the name */ |
684 | | }; |
685 | | |
686 | | /* To add more spec_ops, put a key name (used to identify the specific |
687 | | * spec_op required) in this table, the integer is just used in the switch |
688 | | * in the main code to execute the required spec_op code. |
689 | | */ |
690 | | spec_op_t spec_op_defs[] = { |
691 | | {(char *)"GetDeviceParam", 0}, |
692 | | {(char *)"EventInfo", 1}, |
693 | | {(char *)"SupportsDevn", 2}, |
694 | | }; |
695 | | |
696 | | /* <any> <any> .... /spec_op name .special_op <any> <any> ..... |
697 | | * The special_op operator takes at a minimum the name of the spec_op to execute |
698 | | * and as many additional parameters as are required for the spec_op. It may |
699 | | * return as many additional parameters as required. |
700 | | */ |
701 | | int |
702 | | zspec_op(i_ctx_t *i_ctx_p) |
703 | 6.39M | { |
704 | 6.39M | os_ptr op = osp; |
705 | 6.39M | gx_device *dev = gs_currentdevice(igs); |
706 | 6.39M | int i, nprocs = sizeof(spec_op_defs) / sizeof(spec_op_t), code, proc = -1; |
707 | 6.39M | ref opname, nref, namestr; |
708 | 6.39M | char *data; |
709 | | |
710 | | /* At the very minimum we need a name object telling us which sepc_op to perform */ |
711 | 6.39M | check_op(1); |
712 | 6.39M | if (!r_has_type(op, t_name)) |
713 | 5 | return_error(gs_error_typecheck); |
714 | | |
715 | 6.39M | ref_assign(&opname, op); |
716 | | |
717 | | /* Find the relevant spec_op name */ |
718 | 6.39M | for (i=0;i<nprocs;i++) { |
719 | 6.39M | code = names_ref(imemory->gs_lib_ctx->gs_name_table, (const byte *)spec_op_defs[i].name, strlen(spec_op_defs[i].name), &nref, 0); |
720 | 6.39M | if (code < 0) |
721 | 0 | return code; |
722 | 6.39M | if (name_eq(&opname, &nref)) { |
723 | 6.39M | proc = i; |
724 | 6.39M | break; |
725 | 6.39M | } |
726 | 6.39M | } |
727 | | |
728 | 6.39M | if (proc < 0) |
729 | 0 | return_error(gs_error_undefined); |
730 | | |
731 | 6.39M | ref_stack_pop(&o_stack, 1); /* We don't need the name of the spec_op any more */ |
732 | 6.39M | op = osp; |
733 | | |
734 | 6.39M | switch(proc) { |
735 | 6.39M | case 0: |
736 | 6.39M | { |
737 | 6.39M | stack_param_list list; |
738 | 6.39M | dev_param_req_t request; |
739 | 6.39M | ref rkeys; |
740 | | /* Get a single device parameter, we should be supplied with |
741 | | * the name of the paramter, as a name object. |
742 | | */ |
743 | 6.39M | check_op(1); |
744 | 6.39M | if (!r_has_type(op, t_name)) |
745 | 0 | return_error(gs_error_typecheck); |
746 | | |
747 | 6.39M | ref_assign(&opname, op); |
748 | 6.39M | name_string_ref(imemory, &opname, &namestr); |
749 | | |
750 | 6.39M | data = (char *)gs_alloc_bytes(imemory, r_size(&namestr) + 1, "temporary special_op string"); |
751 | 6.39M | if (data == 0) |
752 | 0 | return_error(gs_error_VMerror); |
753 | 6.39M | memset(data, 0x00, r_size(&namestr) + 1); |
754 | 6.39M | memcpy(data, namestr.value.bytes, r_size(&namestr)); |
755 | | |
756 | | /* Discard the parameter name now, we're done with it */ |
757 | 6.39M | pop (1); |
758 | | /* Make a null object so that the stack param list won't check for requests */ |
759 | 6.39M | make_null(&rkeys); |
760 | 6.39M | stack_param_list_write(&list, &o_stack, &rkeys, iimemory); |
761 | | /* Stuff the data into a structure for passing to the spec_op */ |
762 | 6.39M | request.Param = data; |
763 | 6.39M | request.list = &list; |
764 | | |
765 | 6.39M | code = dev_proc(dev, dev_spec_op)(dev, gxdso_get_dev_param, &request, sizeof(dev_param_req_t)); |
766 | | |
767 | 6.39M | gs_free_object(imemory, data, "temporary special_op string"); |
768 | | |
769 | 6.39M | if (code < 0) { |
770 | 4.03M | if (code == gs_error_undefined) { |
771 | 4.03M | op = osp; |
772 | 4.03M | push(1); |
773 | 4.03M | make_bool(op, 0); |
774 | 4.03M | } else |
775 | 0 | return_error(code); |
776 | 4.03M | } else { |
777 | 2.35M | op = osp; |
778 | 2.35M | push(1); |
779 | 2.35M | make_bool(op, 1); |
780 | 2.35M | } |
781 | 6.39M | } |
782 | 6.39M | break; |
783 | 6.39M | case 1: |
784 | 0 | { |
785 | 0 | stack_param_list list; |
786 | 0 | dev_param_req_t request; |
787 | 0 | ref rkeys; |
788 | | /* EventInfo we should be supplied with a name object which we |
789 | | * pass as the event info to the dev_spec_op |
790 | | */ |
791 | 0 | check_op(1); |
792 | 0 | if (!r_has_type(op, t_name)) |
793 | 0 | return_error(gs_error_typecheck); |
794 | | |
795 | 0 | ref_assign(&opname, op); |
796 | 0 | name_string_ref(imemory, &opname, &namestr); |
797 | |
|
798 | 0 | data = (char *)gs_alloc_bytes(imemory, r_size(&namestr) + 1, "temporary special_op string"); |
799 | 0 | if (data == 0) |
800 | 0 | return_error(gs_error_VMerror); |
801 | 0 | memset(data, 0x00, r_size(&namestr) + 1); |
802 | 0 | memcpy(data, namestr.value.bytes, r_size(&namestr)); |
803 | | |
804 | | /* Discard the parameter name now, we're done with it */ |
805 | 0 | pop (1); |
806 | | /* Make a null object so that the stack param list won't check for requests */ |
807 | 0 | make_null(&rkeys); |
808 | 0 | stack_param_list_write(&list, &o_stack, &rkeys, iimemory); |
809 | | /* Stuff the data into a structure for passing to the spec_op */ |
810 | 0 | request.Param = data; |
811 | 0 | request.list = &list; |
812 | |
|
813 | 0 | code = dev_proc(dev, dev_spec_op)(dev, gxdso_event_info, &request, sizeof(dev_param_req_t)); |
814 | |
|
815 | 0 | gs_free_object(imemory, data, "temporary special_op string"); |
816 | |
|
817 | 0 | if (code < 0) { |
818 | 0 | if (code == gs_error_undefined) { |
819 | 0 | op = osp; |
820 | 0 | push(1); |
821 | 0 | make_bool(op, 0); |
822 | 0 | } else |
823 | 0 | return_error(code); |
824 | 0 | } |
825 | 0 | } |
826 | 0 | break; |
827 | 0 | case 2: |
828 | 0 | { |
829 | | /* SupportsDevn. Return the boolean from the device */ |
830 | |
|
831 | 0 | code = dev_proc(dev, dev_spec_op)(dev, gxdso_supports_devn, NULL, 0); |
832 | 0 | if (code < 0 && code != gs_error_undefined) |
833 | 0 | return_error(code); /* any other error leaves the stack unchanged */ |
834 | | |
835 | 0 | op = osp; |
836 | 0 | push(1); |
837 | 0 | make_bool(op, code > 0 ? 1 : 0); /* return true/false */ |
838 | 0 | } |
839 | 0 | break; |
840 | 0 | default: |
841 | | /* Belt and braces; it shold not be possible to get here, as the table |
842 | | * containing the names should mirror the entries in this switch. If we |
843 | | * found a name there should be a matching case here. |
844 | | */ |
845 | 0 | return_error(gs_error_undefined); |
846 | 0 | break; |
847 | 6.39M | } |
848 | 6.39M | return 0; |
849 | 6.39M | } |
850 | | |
851 | | /* ------ Initialization procedure ------ */ |
852 | | |
853 | | const op_def zdevice_op_defs[] = |
854 | | { |
855 | | {"2.copydevice2", zcopydevice2}, |
856 | | {"0currentdevice", zcurrentdevice}, |
857 | | {"0.currentoutputdevice", zcurrentoutputdevice}, |
858 | | {"1.devicename", zdevicename}, |
859 | | {"0.doneshowpage", zdoneshowpage}, |
860 | | {"0flushpage", zflushpage}, |
861 | | {"7.getbitsrect", zgetbitsrect}, |
862 | | {"1.getdevice", zgetdevice}, |
863 | | {"0.getdefaultdevice", zgetdefaultdevice}, |
864 | | {"2.getdeviceparams", zgetdeviceparams}, |
865 | | {"2.gethardwareparams", zgethardwareparams}, |
866 | | {"5makewordimagedevice", zmakewordimagedevice}, |
867 | | {"0nulldevice", znulldevice}, |
868 | | {"2.outputpage", zoutputpage}, |
869 | | {"3.putdeviceparams", zputdeviceparams}, |
870 | | {"1.setdevice", zsetdevice}, |
871 | | op_def_end(0) |
872 | | }; |
873 | | |
874 | | /* We need to split the table because of the 16-element limit. */ |
875 | | const op_def zdevice_ext_op_defs[] = |
876 | | { |
877 | | {"0.special_op", zspec_op}, |
878 | | op_def_end(0) |
879 | | }; |