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