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