1 /* Copyright (C) 1993, 1995, 1997, 1998, 1999 Aladdin Enterprises. All rights reserved.
2
3 This software is provided AS-IS with no warranty, either express or
4 implied.
5
6 This software is distributed under license and may not be copied,
7 modified or distributed except as expressly authorized under the terms
8 of the license contained in the file LICENSE in this distribution.
9
10 For more information about licensing, please refer to
11 http://www.ghostscript.com/licensing/. For information on
12 commercial licensing, go to http://www.artifex.com/licensing/ or
13 contact Artifex Software, Inc., 101 Lucas Valley Road #110,
14 San Rafael, CA 94903, U.S.A., +1(415)492-9861.
15 */
16
17 /* $Id: zdevice2.c,v 1.10 2005/10/04 06:30:02 ray Exp $ */
18 /* Level 2 device operators */
19 #include "math_.h"
20 #include "memory_.h"
21 #include "ghost.h"
22 #include "oper.h"
23 #include "dstack.h" /* for dict_find_name */
24 #include "estack.h"
25 #include "idict.h"
26 #include "idparam.h"
27 #include "igstate.h"
28 #include "iname.h"
29 #include "iutil.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 private int z2copy_gstate(i_ctx_t *);
39 private 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
z2copy(i_ctx_t * i_ctx_p)46 z2copy(i_ctx_t *i_ctx_p)
47 {
48 os_ptr op = osp;
49 int code = zcopy(i_ctx_p);
50
51 if (code >= 0)
52 return code;
53 if (!r_has_type(op, t_astruct))
54 return code;
55 return z2copy_gstate(i_ctx_p);
56 }
57
58 /* - .currentshowpagecount <count> true */
59 /* - .currentshowpagecount false */
60 private int
zcurrentshowpagecount(i_ctx_t * i_ctx_p)61 zcurrentshowpagecount(i_ctx_t *i_ctx_p)
62 {
63 os_ptr op = osp;
64 gx_device *dev = gs_currentdevice(igs);
65
66 if ((*dev_proc(dev, get_page_device))(dev) == 0) {
67 push(1);
68 make_false(op);
69 } else {
70 push(2);
71 make_int(op - 1, dev->ShowpageCount);
72 make_true(op);
73 }
74 return 0;
75 }
76
77 /* - .currentpagedevice <dict> <bool> */
78 private int
zcurrentpagedevice(i_ctx_t * i_ctx_p)79 zcurrentpagedevice(i_ctx_t *i_ctx_p)
80 {
81 os_ptr op = osp;
82 gx_device *dev = gs_currentdevice(igs);
83
84 push(2);
85 if ((*dev_proc(dev, get_page_device))(dev) != 0) {
86 op[-1] = istate->pagedevice;
87 make_true(op);
88 } else {
89 make_null(op - 1);
90 make_false(op);
91 }
92 return 0;
93 }
94
95 /* <local_dict|null> .setpagedevice - */
96 private int
zsetpagedevice(i_ctx_t * i_ctx_p)97 zsetpagedevice(i_ctx_t *i_ctx_p)
98 {
99 os_ptr op = osp;
100 int code;
101
102 /******
103 if ( igs->in_cachedevice )
104 return_error(e_undefined);
105 ******/
106 if (r_has_type(op, t_dictionary)) {
107 check_dict_read(*op);
108 #if 0 /****************/
109 /*
110 * In order to avoid invalidaccess errors on setpagedevice,
111 * the dictionary must be allocated in local VM.
112 */
113 if (!(r_is_local(op)))
114 return_error(e_invalidaccess);
115 #endif /****************/
116 /* Make the dictionary read-only. */
117 code = zreadonly(i_ctx_p);
118 if (code < 0)
119 return code;
120 } else {
121 check_type(*op, t_null);
122 }
123 istate->pagedevice = *op;
124 pop(1);
125 return 0;
126 }
127
128 /* Default Install/BeginPage/EndPage procedures */
129 /* that just call the procedure in the device. */
130
131 /* - .callinstall - */
132 private int
zcallinstall(i_ctx_t * i_ctx_p)133 zcallinstall(i_ctx_t *i_ctx_p)
134 {
135 gx_device *dev = gs_currentdevice(igs);
136
137 if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
138 int code = (*dev->page_procs.install) (dev, igs);
139
140 if (code < 0)
141 return code;
142 }
143 return 0;
144 }
145
146 /* <showpage_count> .callbeginpage - */
147 private int
zcallbeginpage(i_ctx_t * i_ctx_p)148 zcallbeginpage(i_ctx_t *i_ctx_p)
149 {
150 os_ptr op = osp;
151 gx_device *dev = gs_currentdevice(igs);
152
153 check_type(*op, t_integer);
154 if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
155 int code = (*dev->page_procs.begin_page)(dev, igs);
156
157 if (code < 0)
158 return code;
159 }
160 pop(1);
161 return 0;
162 }
163
164 /* <showpage_count> <reason_int> .callendpage <flush_bool> */
165 private int
zcallendpage(i_ctx_t * i_ctx_p)166 zcallendpage(i_ctx_t *i_ctx_p)
167 {
168 os_ptr op = osp;
169 gx_device *dev = gs_currentdevice(igs);
170 int code;
171
172 check_type(op[-1], t_integer);
173 check_type(*op, t_integer);
174 if ((dev = (*dev_proc(dev, get_page_device))(dev)) != 0) {
175 code = (*dev->page_procs.end_page)(dev, (int)op->value.intval, igs);
176 if (code < 0)
177 return code;
178 if (code > 1)
179 return_error(e_rangecheck);
180 } else {
181 code = (op->value.intval == 2 ? 0 : 1);
182 }
183 make_bool(op - 1, code);
184 pop(1);
185 return 0;
186 }
187
188 /* ------ Wrappers for operators that save the graphics state. ------ */
189
190 /* When saving the state with the current device a page device, */
191 /* we need to make sure that the page device dictionary exists */
192 /* so that grestore can use it to reset the device parameters. */
193 /* This may have significant performance consequences, but we don't see */
194 /* any way around it. */
195
196 /* Check whether we need to call out to create the page device dictionary. */
197 private bool
save_page_device(gs_state * pgs)198 save_page_device(gs_state *pgs)
199 {
200 return
201 (r_has_type(&gs_int_gstate(pgs)->pagedevice, t_null) &&
202 (*dev_proc(gs_currentdevice(pgs), get_page_device))(gs_currentdevice(pgs)) != 0);
203 }
204
205 /* - gsave - */
206 private int
z2gsave(i_ctx_t * i_ctx_p)207 z2gsave(i_ctx_t *i_ctx_p)
208 {
209 if (!save_page_device(igs))
210 return gs_gsave(igs);
211 return push_callout(i_ctx_p, "%gsavepagedevice");
212 }
213
214 /* - save - */
215 private int
z2save(i_ctx_t * i_ctx_p)216 z2save(i_ctx_t *i_ctx_p)
217 {
218 if (!save_page_device(igs))
219 return zsave(i_ctx_p);
220 return push_callout(i_ctx_p, "%savepagedevice");
221 }
222
223 /* - gstate <gstate> */
224 private int
z2gstate(i_ctx_t * i_ctx_p)225 z2gstate(i_ctx_t *i_ctx_p)
226 {
227 if (!save_page_device(igs))
228 return zgstate(i_ctx_p);
229 return push_callout(i_ctx_p, "%gstatepagedevice");
230 }
231
232 /* <gstate1> <gstate2> copy <gstate2> */
233 private int
z2copy_gstate(i_ctx_t * i_ctx_p)234 z2copy_gstate(i_ctx_t *i_ctx_p)
235 {
236 if (!save_page_device(igs))
237 return zcopy_gstate(i_ctx_p);
238 return push_callout(i_ctx_p, "%copygstatepagedevice");
239 }
240
241 /* <gstate> currentgstate <gstate> */
242 private int
z2currentgstate(i_ctx_t * i_ctx_p)243 z2currentgstate(i_ctx_t *i_ctx_p)
244 {
245 if (!save_page_device(igs))
246 return zcurrentgstate(i_ctx_p);
247 return push_callout(i_ctx_p, "%currentgstatepagedevice");
248 }
249
250 /* ------ Wrappers for operators that reset the graphics state. ------ */
251
252 /* Check whether we need to call out to restore the page device. */
253 private bool
restore_page_device(const gs_state * pgs_old,const gs_state * pgs_new)254 restore_page_device(const gs_state * pgs_old, const gs_state * pgs_new)
255 {
256 gx_device *dev_old = gs_currentdevice(pgs_old);
257 gx_device *dev_new;
258 gx_device *dev_t1;
259 gx_device *dev_t2;
260 bool samepagedevice = obj_eq(dev_old->memory, &gs_int_gstate(pgs_old)->pagedevice,
261 &gs_int_gstate(pgs_new)->pagedevice);
262
263 if ((dev_t1 = (*dev_proc(dev_old, get_page_device)) (dev_old)) == 0)
264 return false;
265 /* If we are going to putdeviceparams in a callout, we need to */
266 /* unlock temporarily. The device will be re-locked as needed */
267 /* by putdeviceparams from the pgs_old->pagedevice dict state. */
268 if (!samepagedevice)
269 dev_old->LockSafetyParams = false;
270 dev_new = gs_currentdevice(pgs_new);
271 if (dev_old != dev_new) {
272 if ((dev_t2 = (*dev_proc(dev_new, get_page_device)) (dev_new)) == 0)
273 return false;
274 if (dev_t1 != dev_t2)
275 return true;
276 }
277 /*
278 * The current implementation of setpagedevice just sets new
279 * parameters in the same device object, so we have to check
280 * whether the page device dictionaries are the same.
281 */
282 return !samepagedevice;
283 }
284
285 /* - grestore - */
286 private int
z2grestore(i_ctx_t * i_ctx_p)287 z2grestore(i_ctx_t *i_ctx_p)
288 {
289 if (!restore_page_device(igs, gs_state_saved(igs)))
290 return gs_grestore(igs);
291 return push_callout(i_ctx_p, "%grestorepagedevice");
292 }
293
294 /* - grestoreall - */
295 private int
z2grestoreall(i_ctx_t * i_ctx_p)296 z2grestoreall(i_ctx_t *i_ctx_p)
297 {
298 for (;;) {
299 if (!restore_page_device(igs, gs_state_saved(igs))) {
300 bool done = !gs_state_saved(gs_state_saved(igs));
301
302 gs_grestore(igs);
303 if (done)
304 break;
305 } else
306 return push_callout(i_ctx_p, "%grestoreallpagedevice");
307 }
308 return 0;
309 }
310
311 /* <save> restore - */
312 private int
z2restore(i_ctx_t * i_ctx_p)313 z2restore(i_ctx_t *i_ctx_p)
314 {
315 while (gs_state_saved(gs_state_saved(igs))) {
316 if (restore_page_device(igs, gs_state_saved(igs)))
317 return push_callout(i_ctx_p, "%restore1pagedevice");
318 gs_grestore(igs);
319 }
320 if (restore_page_device(igs, gs_state_saved(igs)))
321 return push_callout(i_ctx_p, "%restorepagedevice");
322 return zrestore(i_ctx_p);
323 }
324
325 /* <gstate> setgstate - */
326 private int
z2setgstate(i_ctx_t * i_ctx_p)327 z2setgstate(i_ctx_t *i_ctx_p)
328 {
329 os_ptr op = osp;
330
331 check_stype(*op, st_igstate_obj);
332 if (!restore_page_device(igs, igstate_ptr(op)))
333 return zsetgstate(i_ctx_p);
334 return push_callout(i_ctx_p, "%setgstatepagedevice");
335 }
336
337 /* ------ Initialization procedure ------ */
338
339 const op_def zdevice2_l2_op_defs[] =
340 {
341 op_def_begin_level2(),
342 {"0.currentshowpagecount", zcurrentshowpagecount},
343 {"0.currentpagedevice", zcurrentpagedevice},
344 {"1.setpagedevice", zsetpagedevice},
345 /* Note that the following replace prior definitions */
346 /* in the indicated files: */
347 {"1copy", z2copy}, /* zdps1.c */
348 {"0gsave", z2gsave}, /* zgstate.c */
349 {"0save", z2save}, /* zvmem.c */
350 {"0gstate", z2gstate}, /* zdps1.c */
351 {"1currentgstate", z2currentgstate}, /* zdps1.c */
352 {"0grestore", z2grestore}, /* zgstate.c */
353 {"0grestoreall", z2grestoreall}, /* zgstate.c */
354 {"1restore", z2restore}, /* zvmem.c */
355 {"1setgstate", z2setgstate}, /* zdps1.c */
356 /* Default Install/BeginPage/EndPage procedures */
357 /* that just call the procedure in the device. */
358 {"0.callinstall", zcallinstall},
359 {"1.callbeginpage", zcallbeginpage},
360 {"2.callendpage", zcallendpage},
361 op_def_end(0)
362 };
363
364 /* ------ Internal routines ------ */
365
366 /* Call out to a PostScript procedure. */
367 private int
push_callout(i_ctx_t * i_ctx_p,const char * callout_name)368 push_callout(i_ctx_t *i_ctx_p, const char *callout_name)
369 {
370 int code;
371
372 check_estack(1);
373 code = name_enter_string(imemory, callout_name, esp + 1);
374 if (code < 0)
375 return code;
376 ++esp;
377 r_set_attrs(esp, a_executable);
378 return o_push_estack;
379 }
380