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