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