/src/ghostpdl/psi/zdevice2.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 device operators */ |
18 | | #include "math_.h" |
19 | | #include "memory_.h" |
20 | | #include "ghost.h" |
21 | | #include "oper.h" |
22 | | #include "dstack.h" /* for dict_find_name */ |
23 | | #include "estack.h" |
24 | | #include "idict.h" |
25 | | #include "idparam.h" |
26 | | #include "igstate.h" |
27 | | #include "iname.h" |
28 | | #include "iutil.h" |
29 | | #include "isave.h" |
30 | | #include "store.h" |
31 | | #include "gxdevice.h" |
32 | | #include "gsstate.h" |
33 | | |
34 | | /* Exported for zfunc4.c */ |
35 | | int z2copy(i_ctx_t *); |
36 | | |
37 | | /* Forward references */ |
38 | | static int z2copy_gstate(i_ctx_t *); |
39 | | static int push_callout(i_ctx_t *, const char *); |
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 | | /* We export this for recognition in FunctionType 4 functions. */ |
45 | | int |
46 | | z2copy(i_ctx_t *i_ctx_p) |
47 | 255M | { |
48 | 255M | os_ptr op = osp; |
49 | 255M | int code = zcopy(i_ctx_p); |
50 | | |
51 | 255M | if (code >= 0) |
52 | 255M | return code; |
53 | 76 | if (!r_has_type(op, t_astruct)) |
54 | 76 | return code; |
55 | 0 | return z2copy_gstate(i_ctx_p); |
56 | 76 | } |
57 | | |
58 | | /* - .currentshowpagecount <count> true */ |
59 | | /* - .currentshowpagecount false */ |
60 | | static int |
61 | | zcurrentshowpagecount(i_ctx_t *i_ctx_p) |
62 | 997k | { |
63 | 997k | os_ptr op = osp; |
64 | 997k | gx_device *dev1, *dev = gs_currentdevice(igs); |
65 | | |
66 | 997k | if ((*dev_proc(dev, get_page_device))(dev) == 0) { |
67 | 55 | push(1); |
68 | 55 | make_false(op); |
69 | 996k | } else { |
70 | 996k | dev1 = (*dev_proc(dev, get_page_device))(dev); |
71 | 996k | push(2); |
72 | 996k | make_int(op - 1, dev1->ShowpageCount); |
73 | 996k | make_true(op); |
74 | 996k | } |
75 | 997k | return 0; |
76 | 997k | } |
77 | | |
78 | | /* - .currentpagedevice <dict> <bool> */ |
79 | | static int |
80 | | zcurrentpagedevice(i_ctx_t *i_ctx_p) |
81 | 2.98M | { |
82 | 2.98M | os_ptr op = osp; |
83 | 2.98M | gx_device *dev = gs_currentdevice(igs); |
84 | | |
85 | 2.98M | push(2); |
86 | 2.98M | if ((*dev_proc(dev, get_page_device))(dev) != 0) { |
87 | 2.98M | op[-1] = istate->pagedevice; |
88 | 2.98M | make_true(op); |
89 | 2.98M | } else { |
90 | 129 | make_null(op - 1); |
91 | 129 | make_false(op); |
92 | 129 | } |
93 | 2.98M | return 0; |
94 | 2.98M | } |
95 | | |
96 | | /* <local_dict|null> .setpagedevice - */ |
97 | | static int |
98 | | zsetpagedevice(i_ctx_t *i_ctx_p) |
99 | 1.49M | { |
100 | 1.49M | os_ptr op = osp; |
101 | 1.49M | int code; |
102 | | |
103 | | /****** |
104 | | if ( igs->in_cachedevice ) |
105 | | return_error(gs_error_undefined); |
106 | | ******/ |
107 | 1.49M | if (r_has_type(op, t_dictionary)) { |
108 | 1.49M | check_dict_read(*op); |
109 | | #if 0 /****************/ |
110 | | /* |
111 | | * In order to avoid invalidaccess errors on setpagedevice, |
112 | | * the dictionary must be allocated in local VM. |
113 | | */ |
114 | | if (!(r_is_local(op))) |
115 | | return_error(gs_error_invalidaccess); |
116 | | #endif /****************/ |
117 | | /* Make the dictionary read-only. */ |
118 | 1.49M | code = zreadonly(i_ctx_p); |
119 | 1.49M | if (code < 0) |
120 | 0 | return code; |
121 | 1.49M | } else { |
122 | 43 | check_type(*op, t_null); |
123 | 43 | } |
124 | 1.49M | istate->pagedevice = *op; |
125 | 1.49M | pop(1); |
126 | 1.49M | return 0; |
127 | 1.49M | } |
128 | | |
129 | | /* Default Install/BeginPage/EndPage procedures */ |
130 | | /* that just call the procedure in the device. */ |
131 | | |
132 | | /* - .callinstall - */ |
133 | | static int |
134 | | zcallinstall(i_ctx_t *i_ctx_p) |
135 | 375k | { |
136 | 375k | gx_device *dev = gs_currentdevice(igs); |
137 | | |
138 | 375k | if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) { |
139 | 375k | int code = (*dev->page_procs.install) (dev, igs); |
140 | | |
141 | 375k | if (code < 0) |
142 | 0 | return code; |
143 | 375k | } |
144 | 375k | return 0; |
145 | 375k | } |
146 | | |
147 | | /* <showpage_count> .callbeginpage - */ |
148 | | static int |
149 | | zcallbeginpage(i_ctx_t *i_ctx_p) |
150 | 448k | { |
151 | 448k | os_ptr op = osp; |
152 | 448k | gx_device *dev = gs_currentdevice(igs); |
153 | | |
154 | 448k | check_type(*op, t_integer); |
155 | 448k | if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) { |
156 | 448k | int code = (*dev->page_procs.begin_page)(dev, igs); |
157 | | |
158 | 448k | if (code < 0) |
159 | 0 | return code; |
160 | 448k | } |
161 | 448k | pop(1); |
162 | 448k | return 0; |
163 | 448k | } |
164 | | |
165 | | /* <showpage_count> <reason_int> .callendpage <flush_bool> */ |
166 | | static int |
167 | | zcallendpage(i_ctx_t *i_ctx_p) |
168 | 546k | { |
169 | 546k | os_ptr op = osp; |
170 | 546k | gx_device *dev = gs_currentdevice(igs); |
171 | 546k | int code; |
172 | | |
173 | 546k | check_type(op[-1], t_integer); |
174 | 546k | check_type(*op, t_integer); |
175 | 546k | if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) { |
176 | 546k | code = (*dev->page_procs.end_page)(dev, (int)op->value.intval, igs); |
177 | 546k | if (code < 0) |
178 | 0 | return code; |
179 | 546k | if (code > 1) |
180 | 0 | return_error(gs_error_rangecheck); |
181 | 546k | } else { |
182 | 0 | code = (op->value.intval == 2 ? 0 : 1); |
183 | 0 | } |
184 | 546k | make_bool(op - 1, code); |
185 | 546k | pop(1); |
186 | 546k | return 0; |
187 | 546k | } |
188 | | |
189 | | /* ------ Wrappers for operators that save the graphics state. ------ */ |
190 | | |
191 | | /* When saving the state with the current device a page device, */ |
192 | | /* we need to make sure that the page device dictionary exists */ |
193 | | /* so that grestore can use it to reset the device parameters. */ |
194 | | /* This may have significant performance consequences, but we don't see */ |
195 | | /* any way around it. */ |
196 | | |
197 | | /* Check whether we need to call out to create the page device dictionary. */ |
198 | | static bool |
199 | | save_page_device(gs_gstate *pgs) |
200 | 587k | { |
201 | 587k | return |
202 | 587k | (r_has_type(&gs_int_gstate(pgs)->pagedevice, t_null) && |
203 | 587k | (*dev_proc(gs_currentdevice(pgs), get_page_device))(gs_currentdevice(pgs)) != 0); |
204 | 587k | } |
205 | | |
206 | | /* - gsave - */ |
207 | | static int |
208 | | z2gsave(i_ctx_t *i_ctx_p) |
209 | 298k | { |
210 | 298k | if (!save_page_device(igs)) |
211 | 209k | return gs_gsave(igs); |
212 | 89.2k | return push_callout(i_ctx_p, "%gsavepagedevice"); |
213 | 298k | } |
214 | | |
215 | | /* - save - */ |
216 | | static int |
217 | | z2save(i_ctx_t *i_ctx_p) |
218 | 288k | { |
219 | 288k | if (!save_page_device(igs)) |
220 | 199k | return zsave(i_ctx_p); |
221 | 89.2k | return push_callout(i_ctx_p, "%savepagedevice"); |
222 | 288k | } |
223 | | |
224 | | /* - gstate <gstate> */ |
225 | | static int |
226 | | z2gstate(i_ctx_t *i_ctx_p) |
227 | 316 | { |
228 | 316 | if (!save_page_device(igs)) |
229 | 316 | return zgstate(i_ctx_p); |
230 | 0 | return push_callout(i_ctx_p, "%gstatepagedevice"); |
231 | 316 | } |
232 | | |
233 | | /* <gstate1> <gstate2> copy <gstate2> */ |
234 | | static int |
235 | | z2copy_gstate(i_ctx_t *i_ctx_p) |
236 | 0 | { |
237 | 0 | if (!save_page_device(igs)) |
238 | 0 | return zcopy_gstate(i_ctx_p); |
239 | 0 | return push_callout(i_ctx_p, "%copygstatepagedevice"); |
240 | 0 | } |
241 | | |
242 | | /* <gstate> currentgstate <gstate> */ |
243 | | static int |
244 | | z2currentgstate(i_ctx_t *i_ctx_p) |
245 | 11 | { |
246 | 11 | if (!save_page_device(igs)) |
247 | 11 | return zcurrentgstate(i_ctx_p); |
248 | 0 | return push_callout(i_ctx_p, "%currentgstatepagedevice"); |
249 | 11 | } |
250 | | |
251 | | /* ------ Wrappers for operators that reset the graphics state. ------ */ |
252 | | |
253 | | /* Check whether we need to call out to restore the page device. */ |
254 | | static int |
255 | | restore_page_device(i_ctx_t *i_ctx_p, const gs_gstate * pgs_old, const gs_gstate * pgs_new) |
256 | 188k | { |
257 | 188k | gx_device *dev_old = gs_currentdevice(pgs_old); |
258 | 188k | gx_device *dev_new; |
259 | 188k | gx_device *dev_t1; |
260 | 188k | gx_device *dev_t2; |
261 | 188k | bool samepagedevice = obj_eq(dev_old->memory, &gs_int_gstate(pgs_old)->pagedevice, |
262 | 188k | &gs_int_gstate(pgs_new)->pagedevice); |
263 | 188k | bool LockSafetyParams = dev_old->LockSafetyParams; |
264 | | |
265 | 188k | if ((dev_t1 = (*dev_proc(dev_old, get_page_device)) (dev_old)) == 0) |
266 | 28.3k | return 0; |
267 | | /* If we are going to putdeviceparams in a callout, we need to */ |
268 | | /* unlock temporarily. The device will be re-locked as needed */ |
269 | | /* by putdeviceparams from the pgs_old->pagedevice dict state. */ |
270 | 159k | if (!samepagedevice) |
271 | 28.3k | dev_old->LockSafetyParams = false; |
272 | 159k | dev_new = gs_currentdevice(pgs_new); |
273 | 159k | if (dev_old != dev_new) { |
274 | 0 | if ((dev_t2 = (*dev_proc(dev_new, get_page_device)) (dev_new)) == 0) |
275 | 0 | samepagedevice = true; |
276 | 0 | else if (dev_t1 != dev_t2) |
277 | 0 | samepagedevice = false; |
278 | 0 | } |
279 | | |
280 | 159k | if (LockSafetyParams) { |
281 | 0 | const int required_ops = 512; |
282 | 0 | const int required_es = 32; |
283 | | |
284 | | /* The %grestorepagedevice must complete: the biggest danger |
285 | | is operand stack overflow. As we use get/putdeviceparams |
286 | | that means pushing all the device params onto the stack, |
287 | | pdfwrite having by far the largest number of parameters |
288 | | at (currently) 212 key/value pairs - thus needing (currently) |
289 | | 424 entries on the op stack. Allowing for working stack |
290 | | space, and safety margin..... |
291 | | */ |
292 | 0 | if (required_ops + ref_stack_count(&o_stack) >= ref_stack_max_count(&o_stack)) { |
293 | 0 | gs_currentdevice(pgs_old)->LockSafetyParams = LockSafetyParams; |
294 | 0 | return_error(gs_error_stackoverflow); |
295 | 0 | } |
296 | | /* We also want enough exec stack space - 32 is an overestimate of |
297 | | what we need to complete the Postscript call out. |
298 | | */ |
299 | 0 | if (required_es + ref_stack_count(&e_stack) >= ref_stack_max_count(&e_stack)) { |
300 | 0 | gs_currentdevice(pgs_old)->LockSafetyParams = LockSafetyParams; |
301 | 0 | return_error(gs_error_execstackoverflow); |
302 | 0 | } |
303 | 0 | } |
304 | | /* |
305 | | * The current implementation of setpagedevice just sets new |
306 | | * parameters in the same device object, so we have to check |
307 | | * whether the page device dictionaries are the same. |
308 | | */ |
309 | 159k | return samepagedevice ? 0 : 1; |
310 | 159k | } |
311 | | |
312 | | /* - grestore - */ |
313 | | static int |
314 | | z2grestore(i_ctx_t *i_ctx_p) |
315 | 115k | { |
316 | 115k | int code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs)); |
317 | 115k | if (code < 0) return code; |
318 | | |
319 | 115k | if (code == 0) |
320 | 114k | return gs_grestore(igs); |
321 | 1.00k | return push_callout(i_ctx_p, "%grestorepagedevice"); |
322 | 115k | } |
323 | | |
324 | | /* - grestoreall - */ |
325 | | static int |
326 | | z2grestoreall(i_ctx_t *i_ctx_p) |
327 | 121 | { |
328 | 121 | for (;;) { |
329 | 121 | int code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs)); |
330 | 121 | if (code < 0) return code; |
331 | 121 | if (code == 0) { |
332 | 65 | bool done = !gs_gstate_saved(gs_gstate_saved(igs)); |
333 | | |
334 | 65 | gs_grestore(igs); |
335 | 65 | if (done) |
336 | 65 | break; |
337 | 65 | } else |
338 | 56 | return push_callout(i_ctx_p, "%grestoreallpagedevice"); |
339 | 121 | } |
340 | 65 | return 0; |
341 | 121 | } |
342 | | /* This is the Level 2+ variant of restore - which adds restoring |
343 | | of the page device to the Level 1 variant in zvmem.c. |
344 | | Previous this restored the device state before calling zrestore.c |
345 | | which validated operands etc, meaning a restore could error out |
346 | | partially complete. |
347 | | The operand checking, and actual VM restore are now in two functions |
348 | | so they can called separately thus, here, we can do as much |
349 | | checking as possible, before embarking on actual changes |
350 | | */ |
351 | | /* <save> restore - */ |
352 | | static int |
353 | | z2restore(i_ctx_t *i_ctx_p) |
354 | 72.1k | { |
355 | 72.1k | alloc_save_t *asave; |
356 | 72.1k | bool saveLockSafety = gs_currentdevice_inline(igs)->LockSafetyParams; |
357 | 72.1k | int code = restore_check_save(i_ctx_p, &asave); |
358 | | |
359 | 72.1k | if (code < 0) return code; |
360 | | |
361 | 72.7k | while (gs_gstate_saved(gs_gstate_saved(igs))) { |
362 | 673 | code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs)); |
363 | 673 | if (code < 0) return code; |
364 | 673 | if (code > 0) |
365 | 0 | return push_callout(i_ctx_p, "%restore1pagedevice"); |
366 | 673 | gs_grestore(igs); |
367 | 673 | } |
368 | 72.0k | code = restore_page_device(i_ctx_p, igs, gs_gstate_saved(igs)); |
369 | 72.0k | if (code < 0) return code; |
370 | 72.0k | if (code > 0) |
371 | 27.2k | return push_callout(i_ctx_p, "%restorepagedevice"); |
372 | | |
373 | 44.8k | code = dorestore(i_ctx_p, asave); |
374 | | |
375 | 44.8k | if (code < 0) { |
376 | | /* An error here is basically fatal, but.... |
377 | | restore_page_device() has to set LockSafetyParams false so it can |
378 | | configure the restored device correctly - in normal operation, that |
379 | | gets reset by that configuration. If we hit an error, though, that |
380 | | may not happen - at least ensure we keep the setting through the |
381 | | error. |
382 | | */ |
383 | 0 | gs_currentdevice_inline(igs)->LockSafetyParams = saveLockSafety; |
384 | 0 | } |
385 | 44.8k | return code; |
386 | 72.0k | } |
387 | | |
388 | | /* <gstate> setgstate - */ |
389 | | static int |
390 | | z2setgstate(i_ctx_t *i_ctx_p) |
391 | 15 | { |
392 | 15 | os_ptr op = osp; |
393 | 15 | int code; |
394 | | |
395 | 15 | check_stype(*op, st_igstate_obj); |
396 | 4 | code = restore_page_device(i_ctx_p, igs, igstate_ptr(op)); |
397 | 4 | if (code < 0) return code; |
398 | 4 | if (code == 0) |
399 | 4 | return zsetgstate(i_ctx_p); |
400 | 0 | return push_callout(i_ctx_p, "%setgstatepagedevice"); |
401 | 4 | } |
402 | | |
403 | | /* ------ Initialization procedure ------ */ |
404 | | |
405 | | const op_def zdevice2_l2_op_defs[] = |
406 | | { |
407 | | op_def_begin_level2(), |
408 | | {"0.currentshowpagecount", zcurrentshowpagecount}, |
409 | | {"0.currentpagedevice", zcurrentpagedevice}, |
410 | | {"1.setpagedevice", zsetpagedevice}, |
411 | | /* Note that the following replace prior definitions */ |
412 | | /* in the indicated files: */ |
413 | | {"1copy", z2copy}, /* zdps1.c */ |
414 | | {"0gsave", z2gsave}, /* zgstate.c */ |
415 | | {"0save", z2save}, /* zvmem.c */ |
416 | | {"0gstate", z2gstate}, /* zdps1.c */ |
417 | | {"1currentgstate", z2currentgstate}, /* zdps1.c */ |
418 | | {"0grestore", z2grestore}, /* zgstate.c */ |
419 | | {"0grestoreall", z2grestoreall}, /* zgstate.c */ |
420 | | {"1restore", z2restore}, /* zvmem.c */ |
421 | | {"1setgstate", z2setgstate}, /* zdps1.c */ |
422 | | /* Default Install/BeginPage/EndPage procedures */ |
423 | | /* that just call the procedure in the device. */ |
424 | | {"0.callinstall", zcallinstall}, |
425 | | {"1.callbeginpage", zcallbeginpage}, |
426 | | {"2.callendpage", zcallendpage}, |
427 | | op_def_end(0) |
428 | | }; |
429 | | |
430 | | /* ------ Internal routines ------ */ |
431 | | |
432 | | /* Call out to a PostScript procedure. */ |
433 | | static int |
434 | | push_callout(i_ctx_t *i_ctx_p, const char *callout_name) |
435 | 206k | { |
436 | 206k | int code; |
437 | | |
438 | 206k | check_estack(1); |
439 | 206k | code = name_enter_string(imemory, callout_name, esp + 1); |
440 | 206k | if (code < 0) |
441 | 0 | return code; |
442 | 206k | ++esp; |
443 | 206k | r_set_attrs(esp, a_executable); |
444 | 206k | return o_push_estack; |
445 | 206k | } |