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