2 |
- |
1 |
/* Copyright (C) 1989, 2000 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: gsdevice.c,v 1.25 2005/10/12 17:59:55 leonardo Exp $ */
|
|
|
18 |
/* Device operators for Ghostscript library */
|
|
|
19 |
#include "ctype_.h"
|
|
|
20 |
#include "memory_.h" /* for memchr, memcpy */
|
|
|
21 |
#include "string_.h"
|
|
|
22 |
#include "gx.h"
|
|
|
23 |
#include "gp.h"
|
|
|
24 |
#include "gscdefs.h" /* for gs_lib_device_list */
|
|
|
25 |
#include "gserrors.h"
|
|
|
26 |
#include "gsfname.h"
|
|
|
27 |
#include "gsstruct.h"
|
|
|
28 |
#include "gspath.h" /* gs_initclip prototype */
|
|
|
29 |
#include "gspaint.h" /* gs_erasepage prototype */
|
|
|
30 |
#include "gsmatrix.h" /* for gscoord.h */
|
|
|
31 |
#include "gscoord.h" /* for gs_initmatrix */
|
|
|
32 |
#include "gzstate.h"
|
|
|
33 |
#include "gxcmap.h"
|
|
|
34 |
#include "gxdevice.h"
|
|
|
35 |
#include "gxdevmem.h"
|
|
|
36 |
#include "gxiodev.h"
|
|
|
37 |
#include "gxcspace.h"
|
|
|
38 |
|
|
|
39 |
/* Include the extern for the device list. */
|
|
|
40 |
extern_gs_lib_device_list();
|
|
|
41 |
|
|
|
42 |
/*
|
|
|
43 |
* Finalization for devices: do any special finalization first, then
|
|
|
44 |
* close the device if it is open, and finally free the structure
|
|
|
45 |
* descriptor if it is dynamic.
|
|
|
46 |
*/
|
|
|
47 |
void
|
|
|
48 |
gx_device_finalize(void *vptr)
|
|
|
49 |
{
|
|
|
50 |
gx_device * const dev = (gx_device *)vptr;
|
|
|
51 |
|
|
|
52 |
if (dev->finalize)
|
|
|
53 |
dev->finalize(dev);
|
|
|
54 |
discard(gs_closedevice(dev));
|
|
|
55 |
if (dev->stype_is_dynamic)
|
|
|
56 |
gs_free_const_object(dev->memory->non_gc_memory, dev->stype,
|
|
|
57 |
"gx_device_finalize");
|
|
|
58 |
}
|
|
|
59 |
|
|
|
60 |
/* "Free" a device locally allocated on the stack, by finalizing it. */
|
|
|
61 |
void
|
|
|
62 |
gx_device_free_local(gx_device *dev)
|
|
|
63 |
{
|
|
|
64 |
gx_device_finalize(dev);
|
|
|
65 |
}
|
|
|
66 |
|
|
|
67 |
/* GC procedures */
|
|
|
68 |
private
|
|
|
69 |
ENUM_PTRS_WITH(device_forward_enum_ptrs, gx_device_forward *fdev) return 0;
|
|
|
70 |
case 0: ENUM_RETURN(gx_device_enum_ptr(fdev->target));
|
|
|
71 |
ENUM_PTRS_END
|
|
|
72 |
private RELOC_PTRS_WITH(device_forward_reloc_ptrs, gx_device_forward *fdev)
|
|
|
73 |
{
|
|
|
74 |
fdev->target = gx_device_reloc_ptr(fdev->target, gcst);
|
|
|
75 |
}
|
|
|
76 |
RELOC_PTRS_END
|
|
|
77 |
|
|
|
78 |
/*
|
|
|
79 |
* Structure descriptors. These must follow the procedures, because
|
|
|
80 |
* we can't conveniently forward-declare the procedures.
|
|
|
81 |
* (See gxdevice.h for details.)
|
|
|
82 |
*/
|
|
|
83 |
public_st_device();
|
|
|
84 |
public_st_device_forward();
|
|
|
85 |
public_st_device_null();
|
|
|
86 |
|
|
|
87 |
/* GC utilities */
|
|
|
88 |
/* Enumerate or relocate a device pointer for a client. */
|
|
|
89 |
gx_device *
|
|
|
90 |
gx_device_enum_ptr(gx_device * dev)
|
|
|
91 |
{
|
|
|
92 |
if (dev == 0 || dev->memory == 0)
|
|
|
93 |
return 0;
|
|
|
94 |
return dev;
|
|
|
95 |
}
|
|
|
96 |
gx_device *
|
|
|
97 |
gx_device_reloc_ptr(gx_device * dev, gc_state_t * gcst)
|
|
|
98 |
{
|
|
|
99 |
if (dev == 0 || dev->memory == 0)
|
|
|
100 |
return dev;
|
|
|
101 |
return RELOC_OBJ(dev); /* gcst implicit */
|
|
|
102 |
}
|
|
|
103 |
|
|
|
104 |
/* Set up the device procedures in the device structure. */
|
|
|
105 |
/* Also copy old fields to new ones. */
|
|
|
106 |
void
|
|
|
107 |
gx_device_set_procs(gx_device * dev)
|
|
|
108 |
{
|
|
|
109 |
if (dev->static_procs != 0) { /* 0 if already populated */
|
|
|
110 |
dev->procs = *dev->static_procs;
|
|
|
111 |
dev->static_procs = 0;
|
|
|
112 |
}
|
|
|
113 |
}
|
|
|
114 |
|
|
|
115 |
/* Flush buffered output to the device */
|
|
|
116 |
int
|
|
|
117 |
gs_flushpage(gs_state * pgs)
|
|
|
118 |
{
|
|
|
119 |
gx_device *dev = gs_currentdevice(pgs);
|
|
|
120 |
|
|
|
121 |
return (*dev_proc(dev, sync_output)) (dev);
|
|
|
122 |
}
|
|
|
123 |
|
|
|
124 |
/* Make the device output the accumulated page description */
|
|
|
125 |
int
|
|
|
126 |
gs_copypage(gs_state * pgs)
|
|
|
127 |
{
|
|
|
128 |
return gs_output_page(pgs, 1, 0);
|
|
|
129 |
}
|
|
|
130 |
int
|
|
|
131 |
gs_output_page(gs_state * pgs, int num_copies, int flush)
|
|
|
132 |
{
|
|
|
133 |
gx_device *dev = gs_currentdevice(pgs);
|
|
|
134 |
|
|
|
135 |
if (dev->IgnoreNumCopies)
|
|
|
136 |
num_copies = 1;
|
|
|
137 |
return (*dev_proc(dev, output_page)) (dev, num_copies, flush);
|
|
|
138 |
}
|
|
|
139 |
|
|
|
140 |
/*
|
|
|
141 |
* Do generic work for output_page. All output_page procedures must call
|
|
|
142 |
* this as the last thing they do, unless an error has occurred earlier.
|
|
|
143 |
*/
|
|
|
144 |
int
|
|
|
145 |
gx_finish_output_page(gx_device *dev, int num_copies, int flush)
|
|
|
146 |
{
|
|
|
147 |
dev->PageCount += num_copies;
|
|
|
148 |
return 0;
|
|
|
149 |
}
|
|
|
150 |
|
|
|
151 |
/* Copy scan lines from an image device */
|
|
|
152 |
int
|
|
|
153 |
gs_copyscanlines(gx_device * dev, int start_y, byte * data, uint size,
|
|
|
154 |
int *plines_copied, uint * pbytes_copied)
|
|
|
155 |
{
|
|
|
156 |
uint line_size = gx_device_raster(dev, 0);
|
|
|
157 |
uint count = size / line_size;
|
|
|
158 |
uint i;
|
|
|
159 |
byte *dest = data;
|
|
|
160 |
|
|
|
161 |
for (i = 0; i < count; i++, dest += line_size) {
|
|
|
162 |
int code = (*dev_proc(dev, get_bits)) (dev, start_y + i, dest, NULL);
|
|
|
163 |
|
|
|
164 |
if (code < 0) {
|
|
|
165 |
/* Might just be an overrun. */
|
|
|
166 |
if (start_y + i == dev->height)
|
|
|
167 |
break;
|
|
|
168 |
return_error(code);
|
|
|
169 |
}
|
|
|
170 |
}
|
|
|
171 |
if (plines_copied != NULL)
|
|
|
172 |
*plines_copied = i;
|
|
|
173 |
if (pbytes_copied != NULL)
|
|
|
174 |
*pbytes_copied = i * line_size;
|
|
|
175 |
return 0;
|
|
|
176 |
}
|
|
|
177 |
|
|
|
178 |
/* Get the current device from the graphics state. */
|
|
|
179 |
gx_device *
|
|
|
180 |
gs_currentdevice(const gs_state * pgs)
|
|
|
181 |
{
|
|
|
182 |
return pgs->device;
|
|
|
183 |
}
|
|
|
184 |
|
|
|
185 |
/* Get the name of a device. */
|
|
|
186 |
const char *
|
|
|
187 |
gs_devicename(const gx_device * dev)
|
|
|
188 |
{
|
|
|
189 |
return dev->dname;
|
|
|
190 |
}
|
|
|
191 |
|
|
|
192 |
/* Get the initial matrix of a device. */
|
|
|
193 |
void
|
|
|
194 |
gs_deviceinitialmatrix(gx_device * dev, gs_matrix * pmat)
|
|
|
195 |
{
|
|
|
196 |
fill_dev_proc(dev, get_initial_matrix, gx_default_get_initial_matrix);
|
|
|
197 |
(*dev_proc(dev, get_initial_matrix)) (dev, pmat);
|
|
|
198 |
}
|
|
|
199 |
|
|
|
200 |
/* Get the N'th device from the known device list */
|
|
|
201 |
const gx_device *
|
|
|
202 |
gs_getdevice(int index)
|
|
|
203 |
{
|
|
|
204 |
const gx_device *const *list;
|
|
|
205 |
int count = gs_lib_device_list(&list, NULL);
|
|
|
206 |
|
|
|
207 |
if (index < 0 || index >= count)
|
|
|
208 |
return 0; /* index out of range */
|
|
|
209 |
return list[index];
|
|
|
210 |
}
|
|
|
211 |
|
|
|
212 |
/* Fill in the GC structure descriptor for a device. */
|
|
|
213 |
private void
|
|
|
214 |
gx_device_make_struct_type(gs_memory_struct_type_t *st,
|
|
|
215 |
const gx_device *dev)
|
|
|
216 |
{
|
|
|
217 |
const gx_device_procs *procs = dev->static_procs;
|
|
|
218 |
|
|
|
219 |
/*
|
|
|
220 |
* Try to figure out whether this is a forwarding device. For printer
|
|
|
221 |
* devices, we rely on the prototype referencing the correct structure
|
|
|
222 |
* descriptor; for other devices, we look for a likely forwarding
|
|
|
223 |
* procedure in the vector. The algorithm isn't foolproof, but it's the
|
|
|
224 |
* best we can come up with.
|
|
|
225 |
*/
|
|
|
226 |
if (procs == 0)
|
|
|
227 |
procs = &dev->procs;
|
|
|
228 |
if (dev->stype)
|
|
|
229 |
*st = *dev->stype;
|
|
|
230 |
else if (procs->get_xfont_procs == gx_forward_get_xfont_procs)
|
|
|
231 |
*st = st_device_forward;
|
|
|
232 |
else
|
|
|
233 |
*st = st_device;
|
|
|
234 |
st->ssize = dev->params_size;
|
|
|
235 |
}
|
|
|
236 |
|
|
|
237 |
/* Clone an existing device. */
|
|
|
238 |
int
|
|
|
239 |
gs_copydevice2(gx_device ** pnew_dev, const gx_device * dev, bool keep_open,
|
|
|
240 |
gs_memory_t * mem)
|
|
|
241 |
{
|
|
|
242 |
gx_device *new_dev;
|
|
|
243 |
const gs_memory_struct_type_t *std = dev->stype;
|
|
|
244 |
const gs_memory_struct_type_t *new_std;
|
|
|
245 |
gs_memory_struct_type_t *a_std = 0;
|
|
|
246 |
int code;
|
|
|
247 |
|
|
|
248 |
if (dev->stype_is_dynamic) {
|
|
|
249 |
/*
|
|
|
250 |
* We allocated the stype for this device previously.
|
|
|
251 |
* Just allocate a new stype and copy the old one into it.
|
|
|
252 |
*/
|
|
|
253 |
a_std = (gs_memory_struct_type_t *)
|
|
|
254 |
gs_alloc_bytes_immovable(mem->non_gc_memory, sizeof(*std),
|
|
|
255 |
"gs_copydevice(stype)");
|
|
|
256 |
if (!a_std)
|
|
|
257 |
return_error(gs_error_VMerror);
|
|
|
258 |
*a_std = *std;
|
|
|
259 |
new_std = a_std;
|
|
|
260 |
} else if (std != 0 && std->ssize == dev->params_size) {
|
|
|
261 |
/* Use the static stype. */
|
|
|
262 |
new_std = std;
|
|
|
263 |
} else {
|
|
|
264 |
/* We need to figure out or adjust the stype. */
|
|
|
265 |
a_std = (gs_memory_struct_type_t *)
|
|
|
266 |
gs_alloc_bytes_immovable(mem->non_gc_memory, sizeof(*std),
|
|
|
267 |
"gs_copydevice(stype)");
|
|
|
268 |
if (!a_std)
|
|
|
269 |
return_error(gs_error_VMerror);
|
|
|
270 |
gx_device_make_struct_type(a_std, dev);
|
|
|
271 |
new_std = a_std;
|
|
|
272 |
}
|
|
|
273 |
/*
|
|
|
274 |
* Because command list devices have complicated internal pointer
|
|
|
275 |
* structures, we allocate all device instances as immovable.
|
|
|
276 |
*/
|
|
|
277 |
new_dev = gs_alloc_struct_immovable(mem, gx_device, new_std,
|
|
|
278 |
"gs_copydevice(device)");
|
|
|
279 |
if (new_dev == 0)
|
|
|
280 |
return_error(gs_error_VMerror);
|
|
|
281 |
gx_device_init(new_dev, dev, mem, false);
|
|
|
282 |
gx_device_set_procs(new_dev);
|
|
|
283 |
new_dev->stype = new_std;
|
|
|
284 |
new_dev->stype_is_dynamic = new_std != std;
|
|
|
285 |
/*
|
|
|
286 |
* keep_open is very dangerous. On the other hand, so is copydevice in
|
|
|
287 |
* general, since it just copies the bits without any regard to pointers
|
|
|
288 |
* (including self-pointers) that they may contain. We handle this by
|
|
|
289 |
* making the default finish_copydevice forbid copying of anything other
|
|
|
290 |
* than the device prototype.
|
|
|
291 |
*/
|
|
|
292 |
new_dev->is_open = dev->is_open && keep_open;
|
|
|
293 |
fill_dev_proc(new_dev, finish_copydevice, gx_default_finish_copydevice);
|
|
|
294 |
code = dev_proc(new_dev, finish_copydevice)(new_dev, dev);
|
|
|
295 |
if (code < 0) {
|
|
|
296 |
gs_free_object(mem, new_dev, "gs_copydevice(device)");
|
|
|
297 |
if (a_std)
|
|
|
298 |
gs_free_object(dev->memory->non_gc_memory, a_std, "gs_copydevice(stype)");
|
|
|
299 |
return code;
|
|
|
300 |
}
|
|
|
301 |
*pnew_dev = new_dev;
|
|
|
302 |
return 0;
|
|
|
303 |
}
|
|
|
304 |
int
|
|
|
305 |
gs_copydevice(gx_device ** pnew_dev, const gx_device * dev, gs_memory_t * mem)
|
|
|
306 |
{
|
|
|
307 |
return gs_copydevice2(pnew_dev, dev, false, mem);
|
|
|
308 |
}
|
|
|
309 |
|
|
|
310 |
/* Open a device if not open already. Return 0 if the device was open, */
|
|
|
311 |
/* 1 if it was closed. */
|
|
|
312 |
int
|
|
|
313 |
gs_opendevice(gx_device *dev)
|
|
|
314 |
{
|
|
|
315 |
if (dev->is_open)
|
|
|
316 |
return 0;
|
|
|
317 |
check_device_separable(dev);
|
|
|
318 |
gx_device_fill_in_procs(dev);
|
|
|
319 |
{
|
|
|
320 |
int code = (*dev_proc(dev, open_device))(dev);
|
|
|
321 |
|
|
|
322 |
if (code < 0)
|
|
|
323 |
return_error(code);
|
|
|
324 |
dev->is_open = true;
|
|
|
325 |
return 1;
|
|
|
326 |
}
|
|
|
327 |
}
|
|
|
328 |
|
|
|
329 |
/* Set device parameters, updating a graphics state or imager state. */
|
|
|
330 |
int
|
|
|
331 |
gs_imager_putdeviceparams(gs_imager_state *pis, gx_device *dev,
|
|
|
332 |
gs_param_list *plist)
|
|
|
333 |
{
|
|
|
334 |
int code = gs_putdeviceparams(dev, plist);
|
|
|
335 |
|
|
|
336 |
if (code >= 0)
|
|
|
337 |
gx_set_cmap_procs(pis, dev);
|
|
|
338 |
return code;
|
|
|
339 |
}
|
|
|
340 |
private void
|
|
|
341 |
gs_state_update_device(gs_state *pgs)
|
|
|
342 |
{
|
|
|
343 |
gx_set_cmap_procs((gs_imager_state *)pgs, pgs->device);
|
|
|
344 |
gx_unset_dev_color(pgs);
|
|
|
345 |
}
|
|
|
346 |
int
|
|
|
347 |
gs_state_putdeviceparams(gs_state *pgs, gs_param_list *plist)
|
|
|
348 |
{
|
|
|
349 |
int code = gs_putdeviceparams(pgs->device, plist);
|
|
|
350 |
|
|
|
351 |
if (code >= 0)
|
|
|
352 |
gs_state_update_device(pgs);
|
|
|
353 |
return code;
|
|
|
354 |
}
|
|
|
355 |
|
|
|
356 |
/* Set the device in the graphics state */
|
|
|
357 |
int
|
|
|
358 |
gs_setdevice(gs_state * pgs, gx_device * dev)
|
|
|
359 |
{
|
|
|
360 |
int code = gs_setdevice_no_erase(pgs, dev);
|
|
|
361 |
|
|
|
362 |
if (code == 1)
|
|
|
363 |
code = gs_erasepage(pgs);
|
|
|
364 |
return code;
|
|
|
365 |
}
|
|
|
366 |
int
|
|
|
367 |
gs_setdevice_no_erase(gs_state * pgs, gx_device * dev)
|
|
|
368 |
{
|
|
|
369 |
int open_code = 0, code;
|
|
|
370 |
|
|
|
371 |
/* Initialize the device */
|
|
|
372 |
if (!dev->is_open) {
|
|
|
373 |
gx_device_fill_in_procs(dev);
|
|
|
374 |
if (gs_device_is_memory(dev)) {
|
|
|
375 |
/* Set the target to the current device. */
|
|
|
376 |
gx_device *odev = gs_currentdevice_inline(pgs);
|
|
|
377 |
|
|
|
378 |
while (odev != 0 && gs_device_is_memory(odev))
|
|
|
379 |
odev = ((gx_device_memory *)odev)->target;
|
|
|
380 |
gx_device_set_target(((gx_device_forward *)dev), odev);
|
|
|
381 |
}
|
|
|
382 |
code = open_code = gs_opendevice(dev);
|
|
|
383 |
if (code < 0)
|
|
|
384 |
return code;
|
|
|
385 |
}
|
|
|
386 |
gs_setdevice_no_init(pgs, dev);
|
|
|
387 |
pgs->ctm_default_set = false;
|
|
|
388 |
if ((code = gs_initmatrix(pgs)) < 0 ||
|
|
|
389 |
(code = gs_initclip(pgs)) < 0
|
|
|
390 |
)
|
|
|
391 |
return code;
|
|
|
392 |
/* If we were in a charpath or a setcachedevice, */
|
|
|
393 |
/* we aren't any longer. */
|
|
|
394 |
pgs->in_cachedevice = 0;
|
|
|
395 |
pgs->in_charpath = (gs_char_path_mode) 0;
|
|
|
396 |
return open_code;
|
|
|
397 |
}
|
|
|
398 |
int
|
|
|
399 |
gs_setdevice_no_init(gs_state * pgs, gx_device * dev)
|
|
|
400 |
{
|
|
|
401 |
/*
|
|
|
402 |
* Just set the device, possibly changing color space but no other
|
|
|
403 |
* device parameters.
|
|
|
404 |
*
|
|
|
405 |
* Make sure we don't close the device if dev == pgs->device
|
|
|
406 |
* This could be done by allowing the rc_assign to close the
|
|
|
407 |
* old 'dev' if the rc goes to 0 (via the device structure's
|
|
|
408 |
* finalization procedure), but then the 'code' from the dev
|
|
|
409 |
* closedevice would not be propagated up. We want to allow
|
|
|
410 |
* the code to be handled, particularly for the pdfwrite
|
|
|
411 |
* device.
|
|
|
412 |
*/
|
|
|
413 |
if (pgs->device != NULL && pgs->device->rc.ref_count == 1 &&
|
|
|
414 |
pgs->device != dev) {
|
|
|
415 |
int code = gs_closedevice(pgs->device);
|
|
|
416 |
|
|
|
417 |
if (code < 0)
|
|
|
418 |
return code;
|
|
|
419 |
}
|
|
|
420 |
rc_assign(pgs->device, dev, "gs_setdevice_no_init");
|
|
|
421 |
gs_state_update_device(pgs);
|
|
|
422 |
return pgs->overprint ? gs_do_set_overprint(pgs) : 0;
|
|
|
423 |
}
|
|
|
424 |
|
|
|
425 |
/* Initialize a just-allocated device. */
|
|
|
426 |
void
|
|
|
427 |
gx_device_init(gx_device * dev, const gx_device * proto, gs_memory_t * mem,
|
|
|
428 |
bool internal)
|
|
|
429 |
{
|
|
|
430 |
memcpy(dev, proto, proto->params_size);
|
|
|
431 |
dev->memory = mem;
|
|
|
432 |
dev->retained = !internal;
|
|
|
433 |
rc_init(dev, mem, (internal ? 0 : 1));
|
|
|
434 |
}
|
|
|
435 |
|
|
|
436 |
/* Make a null device. */
|
|
|
437 |
void
|
|
|
438 |
gs_make_null_device(gx_device_null *dev_null, gx_device *dev,
|
|
|
439 |
gs_memory_t * mem)
|
|
|
440 |
{
|
|
|
441 |
gx_device_init((gx_device *)dev_null, (const gx_device *)&gs_null_device,
|
|
|
442 |
mem, true);
|
|
|
443 |
gx_device_set_target((gx_device_forward *)dev_null, dev);
|
|
|
444 |
if (dev) {
|
|
|
445 |
/* The gx_device_copy_color_params() call below should
|
|
|
446 |
probably copy over these new-style color mapping procs, as
|
|
|
447 |
well as the old-style (map_rgb_color and friends). However,
|
|
|
448 |
the change was made here instead, to minimize the potential
|
|
|
449 |
impact of the patch.
|
|
|
450 |
*/
|
|
|
451 |
gx_device *dn = (gx_device *)dev_null;
|
|
|
452 |
set_dev_proc(dn, get_color_mapping_procs, gx_forward_get_color_mapping_procs);
|
|
|
453 |
set_dev_proc(dn, get_color_comp_index, gx_forward_get_color_comp_index);
|
|
|
454 |
set_dev_proc(dn, encode_color, gx_forward_encode_color);
|
|
|
455 |
set_dev_proc(dn, decode_color, gx_forward_decode_color);
|
|
|
456 |
gx_device_copy_color_params(dn, dev);
|
|
|
457 |
}
|
|
|
458 |
}
|
|
|
459 |
|
|
|
460 |
/* Is a null device ? */
|
|
|
461 |
bool gs_is_null_device(gx_device *dev)
|
|
|
462 |
{
|
|
|
463 |
/* Assuming null_fill_path isn't used elswhere. */
|
|
|
464 |
return dev->procs.fill_path == gs_null_device.procs.fill_path;
|
|
|
465 |
}
|
|
|
466 |
|
|
|
467 |
/* Mark a device as retained or not retained. */
|
|
|
468 |
void
|
|
|
469 |
gx_device_retain(gx_device *dev, bool retained)
|
|
|
470 |
{
|
|
|
471 |
int delta = (int)retained - (int)dev->retained;
|
|
|
472 |
|
|
|
473 |
if (delta) {
|
|
|
474 |
dev->retained = retained; /* do first in case dev is freed */
|
|
|
475 |
rc_adjust_only(dev, delta, "gx_device_retain");
|
|
|
476 |
}
|
|
|
477 |
}
|
|
|
478 |
|
|
|
479 |
/* Select a null device. */
|
|
|
480 |
int
|
|
|
481 |
gs_nulldevice(gs_state * pgs)
|
|
|
482 |
{
|
|
|
483 |
if (pgs->device == 0 || !gx_device_is_null(pgs->device)) {
|
|
|
484 |
gx_device *ndev;
|
|
|
485 |
int code = gs_copydevice(&ndev, (const gx_device *)&gs_null_device,
|
|
|
486 |
pgs->memory);
|
|
|
487 |
|
|
|
488 |
if (code < 0)
|
|
|
489 |
return code;
|
|
|
490 |
/*
|
|
|
491 |
* Internal devices have a reference count of 0, not 1,
|
|
|
492 |
* aside from references from graphics states.
|
|
|
493 |
*/
|
|
|
494 |
rc_init(ndev, pgs->memory, 0);
|
|
|
495 |
return gs_setdevice_no_erase(pgs, ndev);
|
|
|
496 |
}
|
|
|
497 |
return 0;
|
|
|
498 |
}
|
|
|
499 |
|
|
|
500 |
/* Close a device. The client is responsible for ensuring that */
|
|
|
501 |
/* this device is not current in any graphics state. */
|
|
|
502 |
int
|
|
|
503 |
gs_closedevice(gx_device * dev)
|
|
|
504 |
{
|
|
|
505 |
int code = 0;
|
|
|
506 |
|
|
|
507 |
if (dev->is_open) {
|
|
|
508 |
code = (*dev_proc(dev, close_device))(dev);
|
|
|
509 |
dev->is_open = false;
|
|
|
510 |
if (code < 0)
|
|
|
511 |
return_error(code);
|
|
|
512 |
}
|
|
|
513 |
return code;
|
|
|
514 |
}
|
|
|
515 |
|
|
|
516 |
/*
|
|
|
517 |
* Just set the device without any reinitializing.
|
|
|
518 |
* (For internal use only.)
|
|
|
519 |
*/
|
|
|
520 |
void
|
|
|
521 |
gx_set_device_only(gs_state * pgs, gx_device * dev)
|
|
|
522 |
{
|
|
|
523 |
rc_assign(pgs->device, dev, "gx_set_device_only");
|
|
|
524 |
}
|
|
|
525 |
|
|
|
526 |
/* Compute the size of one scan line for a device, */
|
|
|
527 |
/* with or without padding to a word boundary. */
|
|
|
528 |
uint
|
|
|
529 |
gx_device_raster(const gx_device * dev, bool pad)
|
|
|
530 |
{
|
|
|
531 |
ulong bits = (ulong) dev->width * dev->color_info.depth;
|
|
|
532 |
|
|
|
533 |
return (pad ? bitmap_raster(bits) : (uint) ((bits + 7) >> 3));
|
|
|
534 |
}
|
|
|
535 |
|
|
|
536 |
/* Adjust the resolution for devices that only have a fixed set of */
|
|
|
537 |
/* geometries, so that the apparent size in inches remains constant. */
|
|
|
538 |
/* If fit=1, the resolution is adjusted so that the entire image fits; */
|
|
|
539 |
/* if fit=0, one dimension fits, but the other one is clipped. */
|
|
|
540 |
int
|
|
|
541 |
gx_device_adjust_resolution(gx_device * dev,
|
|
|
542 |
int actual_width, int actual_height, int fit)
|
|
|
543 |
{
|
|
|
544 |
double width_ratio = (double)actual_width / dev->width;
|
|
|
545 |
double height_ratio = (double)actual_height / dev->height;
|
|
|
546 |
double ratio =
|
|
|
547 |
(fit ? min(width_ratio, height_ratio) :
|
|
|
548 |
max(width_ratio, height_ratio));
|
|
|
549 |
|
|
|
550 |
dev->HWResolution[0] *= ratio;
|
|
|
551 |
dev->HWResolution[1] *= ratio;
|
|
|
552 |
gx_device_set_width_height(dev, actual_width, actual_height);
|
|
|
553 |
return 0;
|
|
|
554 |
}
|
|
|
555 |
|
|
|
556 |
/* Set the HWMargins to values defined in inches. */
|
|
|
557 |
/* If move_origin is true, also reset the Margins. */
|
|
|
558 |
/* Note that this assumes a printer-type device (Y axis inverted). */
|
|
|
559 |
void
|
|
|
560 |
gx_device_set_margins(gx_device * dev, const float *margins /*[4] */ ,
|
|
|
561 |
bool move_origin)
|
|
|
562 |
{
|
|
|
563 |
int i;
|
|
|
564 |
|
|
|
565 |
for (i = 0; i < 4; ++i)
|
|
|
566 |
dev->HWMargins[i] = margins[i] * 72.0;
|
|
|
567 |
if (move_origin) {
|
|
|
568 |
dev->Margins[0] = -margins[0] * dev->MarginsHWResolution[0];
|
|
|
569 |
dev->Margins[1] = -margins[3] * dev->MarginsHWResolution[1];
|
|
|
570 |
}
|
|
|
571 |
}
|
|
|
572 |
|
|
|
573 |
|
|
|
574 |
/* Handle 90 and 270 degree rotation of the Tray
|
|
|
575 |
* Device must support TrayOrientation in its InitialMatrix and get/put params
|
|
|
576 |
*/
|
|
|
577 |
private void
|
|
|
578 |
gx_device_TrayOrientationRotate(gx_device *dev)
|
|
|
579 |
{
|
|
|
580 |
if ( dev->TrayOrientation == 90 || dev->TrayOrientation == 270) {
|
|
|
581 |
/* page sizes don't rotate, height and width do rotate
|
|
|
582 |
* HWResolution, HWSize, and MediaSize parameters interact,
|
|
|
583 |
* and must be set before TrayOrientation
|
|
|
584 |
*/
|
|
|
585 |
int tmp = dev->height;
|
|
|
586 |
dev->height = dev->width;
|
|
|
587 |
dev->width = tmp;
|
|
|
588 |
}
|
|
|
589 |
}
|
|
|
590 |
|
|
|
591 |
/* Set the width and height, updating MediaSize to remain consistent. */
|
|
|
592 |
void
|
|
|
593 |
gx_device_set_width_height(gx_device * dev, int width, int height)
|
|
|
594 |
{
|
|
|
595 |
dev->width = width;
|
|
|
596 |
dev->height = height;
|
|
|
597 |
dev->MediaSize[0] = width * 72.0 / dev->HWResolution[0];
|
|
|
598 |
dev->MediaSize[1] = height * 72.0 / dev->HWResolution[1];
|
|
|
599 |
gx_device_TrayOrientationRotate(dev);
|
|
|
600 |
}
|
|
|
601 |
|
|
|
602 |
/* Set the resolution, updating width and height to remain consistent. */
|
|
|
603 |
void
|
|
|
604 |
gx_device_set_resolution(gx_device * dev, floatp x_dpi, floatp y_dpi)
|
|
|
605 |
{
|
|
|
606 |
dev->HWResolution[0] = x_dpi;
|
|
|
607 |
dev->HWResolution[1] = y_dpi;
|
|
|
608 |
dev->width = (int)(dev->MediaSize[0] * x_dpi / 72.0 + 0.5);
|
|
|
609 |
dev->height = (int)(dev->MediaSize[1] * y_dpi / 72.0 + 0.5);
|
|
|
610 |
gx_device_TrayOrientationRotate(dev);
|
|
|
611 |
}
|
|
|
612 |
|
|
|
613 |
/* Set the MediaSize, updating width and height to remain consistent. */
|
|
|
614 |
void
|
|
|
615 |
gx_device_set_media_size(gx_device * dev, floatp media_width, floatp media_height)
|
|
|
616 |
{
|
|
|
617 |
dev->MediaSize[0] = media_width;
|
|
|
618 |
dev->MediaSize[1] = media_height;
|
|
|
619 |
dev->width = (int)(media_width * dev->HWResolution[0] / 72.0 + 0.499);
|
|
|
620 |
dev->height = (int)(media_height * dev->HWResolution[1] / 72.0 + 0.499);
|
|
|
621 |
gx_device_TrayOrientationRotate(dev);
|
|
|
622 |
}
|
|
|
623 |
|
|
|
624 |
/*
|
|
|
625 |
* Copy the color mapping procedures from the target if they are
|
|
|
626 |
* standard ones (saving a level of procedure call at mapping time).
|
|
|
627 |
*/
|
|
|
628 |
void
|
|
|
629 |
gx_device_copy_color_procs(gx_device *dev, const gx_device *target)
|
|
|
630 |
{
|
|
|
631 |
dev_proc_map_cmyk_color((*from_cmyk)) =
|
|
|
632 |
dev_proc(dev, map_cmyk_color);
|
|
|
633 |
dev_proc_map_rgb_color((*from_rgb)) =
|
|
|
634 |
dev_proc(dev, map_rgb_color);
|
|
|
635 |
dev_proc_map_color_rgb((*to_rgb)) =
|
|
|
636 |
dev_proc(dev, map_color_rgb);
|
|
|
637 |
|
|
|
638 |
/* The logic in this function seems a bit stale; it sets the
|
|
|
639 |
old-style color procs, but not the new ones
|
|
|
640 |
(get_color_mapping_procs, get_color_comp_index, encode_color,
|
|
|
641 |
and decode_color). It should probably copy those as well.
|
|
|
642 |
*/
|
|
|
643 |
if (from_cmyk == gx_forward_map_cmyk_color ||
|
|
|
644 |
from_cmyk == cmyk_1bit_map_cmyk_color ||
|
|
|
645 |
from_cmyk == cmyk_8bit_map_cmyk_color) {
|
|
|
646 |
from_cmyk = dev_proc(target, map_cmyk_color);
|
|
|
647 |
set_dev_proc(dev, map_cmyk_color,
|
|
|
648 |
(from_cmyk == cmyk_1bit_map_cmyk_color ||
|
|
|
649 |
from_cmyk == cmyk_8bit_map_cmyk_color ?
|
|
|
650 |
from_cmyk : gx_forward_map_cmyk_color));
|
|
|
651 |
}
|
|
|
652 |
if (from_rgb == gx_forward_map_rgb_color ||
|
|
|
653 |
from_rgb == gx_default_rgb_map_rgb_color) {
|
|
|
654 |
from_rgb = dev_proc(target, map_rgb_color);
|
|
|
655 |
set_dev_proc(dev, map_rgb_color,
|
|
|
656 |
(from_rgb == gx_default_rgb_map_rgb_color ?
|
|
|
657 |
from_rgb : gx_forward_map_rgb_color));
|
|
|
658 |
}
|
|
|
659 |
if (to_rgb == gx_forward_map_color_rgb ||
|
|
|
660 |
to_rgb == cmyk_1bit_map_color_rgb ||
|
|
|
661 |
to_rgb == cmyk_8bit_map_color_rgb) {
|
|
|
662 |
to_rgb = dev_proc(target, map_color_rgb);
|
|
|
663 |
set_dev_proc(dev, map_color_rgb,
|
|
|
664 |
(to_rgb == cmyk_1bit_map_color_rgb ||
|
|
|
665 |
to_rgb == cmyk_8bit_map_color_rgb ?
|
|
|
666 |
to_rgb : gx_forward_map_color_rgb));
|
|
|
667 |
}
|
|
|
668 |
}
|
|
|
669 |
|
|
|
670 |
#define COPY_PARAM(p) dev->p = target->p
|
|
|
671 |
|
|
|
672 |
/*
|
|
|
673 |
* Copy the color-related device parameters back from the target:
|
|
|
674 |
* color_info and color mapping procedures.
|
|
|
675 |
*/
|
|
|
676 |
void
|
|
|
677 |
gx_device_copy_color_params(gx_device *dev, const gx_device *target)
|
|
|
678 |
{
|
|
|
679 |
COPY_PARAM(color_info);
|
|
|
680 |
COPY_PARAM(cached_colors);
|
|
|
681 |
gx_device_copy_color_procs(dev, target);
|
|
|
682 |
}
|
|
|
683 |
|
|
|
684 |
/*
|
|
|
685 |
* Copy device parameters back from a target. This copies all standard
|
|
|
686 |
* parameters related to page size and resolution, plus color_info
|
|
|
687 |
* and (if appropriate) color mapping procedures.
|
|
|
688 |
*/
|
|
|
689 |
void
|
|
|
690 |
gx_device_copy_params(gx_device *dev, const gx_device *target)
|
|
|
691 |
{
|
|
|
692 |
#define COPY_ARRAY_PARAM(p) memcpy(dev->p, target->p, sizeof(dev->p))
|
|
|
693 |
COPY_PARAM(width);
|
|
|
694 |
COPY_PARAM(height);
|
|
|
695 |
COPY_ARRAY_PARAM(MediaSize);
|
|
|
696 |
COPY_ARRAY_PARAM(ImagingBBox);
|
|
|
697 |
COPY_PARAM(ImagingBBox_set);
|
|
|
698 |
COPY_ARRAY_PARAM(HWResolution);
|
|
|
699 |
COPY_ARRAY_PARAM(MarginsHWResolution);
|
|
|
700 |
COPY_ARRAY_PARAM(Margins);
|
|
|
701 |
COPY_ARRAY_PARAM(HWMargins);
|
|
|
702 |
COPY_PARAM(PageCount);
|
|
|
703 |
#undef COPY_ARRAY_PARAM
|
|
|
704 |
gx_device_copy_color_params(dev, target);
|
|
|
705 |
}
|
|
|
706 |
|
|
|
707 |
#undef COPY_PARAM
|
|
|
708 |
|
|
|
709 |
/*
|
|
|
710 |
* Parse the output file name detecting and validating any %nnd format
|
|
|
711 |
* for inserting the page count. If a format is present, store a pointer
|
|
|
712 |
* to its last character in *pfmt, otherwise store 0 there.
|
|
|
713 |
* Note that we assume devices have already been scanned, and any % must
|
|
|
714 |
* precede a valid format character.
|
|
|
715 |
*
|
|
|
716 |
* If there was a format, then return the max_width
|
|
|
717 |
*/
|
|
|
718 |
private int
|
|
|
719 |
gx_parse_output_format(gs_parsed_file_name_t *pfn, const char **pfmt)
|
|
|
720 |
{
|
|
|
721 |
bool have_format = false, field = 0;
|
|
|
722 |
int width[2], int_width = sizeof(int) * 3, w = 0;
|
|
|
723 |
uint i;
|
|
|
724 |
|
|
|
725 |
/* Scan the file name for a format string, and validate it if present. */
|
|
|
726 |
width[0] = width[1] = 0;
|
|
|
727 |
for (i = 0; i < pfn->len; ++i)
|
|
|
728 |
if (pfn->fname[i] == '%') {
|
|
|
729 |
if (i + 1 < pfn->len && pfn->fname[i + 1] == '%')
|
|
|
730 |
continue;
|
|
|
731 |
if (have_format) /* more than one % */
|
|
|
732 |
return_error(gs_error_undefinedfilename);
|
|
|
733 |
have_format = true;
|
|
|
734 |
sw:
|
|
|
735 |
if (++i == pfn->len)
|
|
|
736 |
return_error(gs_error_undefinedfilename);
|
|
|
737 |
switch (pfn->fname[i]) {
|
|
|
738 |
case 'l':
|
|
|
739 |
int_width = sizeof(long) * 3;
|
|
|
740 |
case ' ': case '#': case '+': case '-':
|
|
|
741 |
goto sw;
|
|
|
742 |
case '.':
|
|
|
743 |
if (field)
|
|
|
744 |
return_error(gs_error_undefinedfilename);
|
|
|
745 |
field = 1;
|
|
|
746 |
continue;
|
|
|
747 |
case '0': case '1': case '2': case '3': case '4':
|
|
|
748 |
case '5': case '6': case '7': case '8': case '9':
|
|
|
749 |
width[field] = width[field] * 10 + pfn->fname[i] - '0';
|
|
|
750 |
goto sw;
|
|
|
751 |
case 'd': case 'i': case 'u': case 'o': case 'x': case 'X':
|
|
|
752 |
*pfmt = &pfn->fname[i];
|
|
|
753 |
continue;
|
|
|
754 |
default:
|
|
|
755 |
return_error(gs_error_undefinedfilename);
|
|
|
756 |
}
|
|
|
757 |
}
|
|
|
758 |
if (have_format) {
|
|
|
759 |
/* Calculate a conservative maximum width. */
|
|
|
760 |
w = max(width[0], width[1]);
|
|
|
761 |
w = max(w, int_width) + 5;
|
|
|
762 |
}
|
|
|
763 |
return w;
|
|
|
764 |
}
|
|
|
765 |
|
|
|
766 |
/*
|
|
|
767 |
* Parse the output file name for a device, recognizing "-" and "|command",
|
|
|
768 |
* and also detecting and validating any %nnd format for inserting the
|
|
|
769 |
* page count. If a format is present, store a pointer to its last
|
|
|
770 |
* character in *pfmt, otherwise store 0 there. Note that an empty name
|
|
|
771 |
* is currently allowed.
|
|
|
772 |
*/
|
|
|
773 |
int
|
|
|
774 |
gx_parse_output_file_name(gs_parsed_file_name_t *pfn, const char **pfmt,
|
|
|
775 |
const char *fname, uint fnlen)
|
|
|
776 |
{
|
|
|
777 |
int code;
|
|
|
778 |
|
|
|
779 |
*pfmt = 0;
|
|
|
780 |
pfn->memory = 0;
|
|
|
781 |
pfn->iodev = NULL;
|
|
|
782 |
pfn->fname = NULL; /* irrelevant since length = 0 */
|
|
|
783 |
pfn->len = 0;
|
|
|
784 |
if (fnlen == 0) /* allow null name */
|
|
|
785 |
return 0;
|
|
|
786 |
/*
|
|
|
787 |
* If the file name begins with a %, it might be either an IODevice
|
|
|
788 |
* or a %nnd format. Check (carefully) for this case.
|
|
|
789 |
*/
|
|
|
790 |
code = gs_parse_file_name(pfn, fname, fnlen);
|
|
|
791 |
if (code < 0) {
|
|
|
792 |
if (fname[0] == '%') {
|
|
|
793 |
/* not a recognized iodev -- may be a leading format descriptor */
|
|
|
794 |
pfn->len = fnlen;
|
|
|
795 |
pfn->fname = fname;
|
|
|
796 |
code = gx_parse_output_format(pfn, pfmt);
|
|
|
797 |
}
|
|
|
798 |
if (code < 0)
|
|
|
799 |
return code;
|
|
|
800 |
}
|
|
|
801 |
if (!pfn->iodev) {
|
|
|
802 |
if ( (pfn->len == 1) && (pfn->fname[0] == '-') ) {
|
|
|
803 |
pfn->iodev = gs_findiodevice((const byte *)"%stdout", 7);
|
|
|
804 |
pfn->fname = NULL;
|
|
|
805 |
} else if (pfn->fname[0] == '|') {
|
|
|
806 |
pfn->iodev = gs_findiodevice((const byte *)"%pipe", 5);
|
|
|
807 |
pfn->fname++, pfn->len--;
|
|
|
808 |
} else
|
|
|
809 |
pfn->iodev = iodev_default;
|
|
|
810 |
if (!pfn->iodev)
|
|
|
811 |
return_error(gs_error_undefinedfilename);
|
|
|
812 |
}
|
|
|
813 |
if (!pfn->fname)
|
|
|
814 |
return 0;
|
|
|
815 |
code = gx_parse_output_format(pfn, pfmt);
|
|
|
816 |
if (code < 0)
|
|
|
817 |
return code;
|
|
|
818 |
if (strlen(pfn->iodev->dname) + pfn->len + code >= gp_file_name_sizeof)
|
|
|
819 |
return_error(gs_error_undefinedfilename);
|
|
|
820 |
return 0;
|
|
|
821 |
}
|
|
|
822 |
|
|
|
823 |
/* Open the output file for a device. */
|
|
|
824 |
int
|
|
|
825 |
gx_device_open_output_file(const gx_device * dev, char *fname,
|
|
|
826 |
bool binary, bool positionable, FILE ** pfile)
|
|
|
827 |
{
|
|
|
828 |
gs_parsed_file_name_t parsed;
|
|
|
829 |
const char *fmt;
|
|
|
830 |
char pfname[gp_file_name_sizeof];
|
|
|
831 |
int code = gx_parse_output_file_name(&parsed, &fmt, fname, strlen(fname));
|
|
|
832 |
|
|
|
833 |
if (code < 0)
|
|
|
834 |
return code;
|
|
|
835 |
if (parsed.iodev && !strcmp(parsed.iodev->dname, "%stdout%")) {
|
|
|
836 |
if (parsed.fname)
|
|
|
837 |
return_error(gs_error_undefinedfilename);
|
|
|
838 |
*pfile = dev->memory->gs_lib_ctx->fstdout;
|
|
|
839 |
/* Force stdout to binary. */
|
|
|
840 |
return gp_setmode_binary(*pfile, true);
|
|
|
841 |
}
|
|
|
842 |
if (fmt) {
|
|
|
843 |
long count1 = dev->PageCount + 1;
|
|
|
844 |
|
|
|
845 |
while (*fmt != 'l' && *fmt != '%')
|
|
|
846 |
--fmt;
|
|
|
847 |
if (*fmt == 'l')
|
|
|
848 |
sprintf(pfname, parsed.fname, count1);
|
|
|
849 |
else
|
|
|
850 |
sprintf(pfname, parsed.fname, (int)count1);
|
|
|
851 |
parsed.fname = pfname;
|
|
|
852 |
parsed.len = strlen(parsed.fname);
|
|
|
853 |
}
|
|
|
854 |
if (positionable || (parsed.iodev && parsed.iodev != iodev_default)) {
|
|
|
855 |
char fmode[4];
|
|
|
856 |
|
|
|
857 |
if (!parsed.fname)
|
|
|
858 |
return_error(gs_error_undefinedfilename);
|
|
|
859 |
strcpy(fmode, gp_fmode_wb);
|
|
|
860 |
if (positionable)
|
|
|
861 |
strcat(fmode, "+");
|
|
|
862 |
code = parsed.iodev->procs.fopen(parsed.iodev, parsed.fname, fmode,
|
|
|
863 |
pfile, NULL, 0);
|
|
|
864 |
if (code)
|
|
|
865 |
eprintf1("**** Could not open the file %s .\n", parsed.fname);
|
|
|
866 |
return code;
|
|
|
867 |
}
|
|
|
868 |
*pfile = gp_open_printer((fmt ? pfname : fname), binary);
|
|
|
869 |
if (*pfile)
|
|
|
870 |
return 0;
|
|
|
871 |
eprintf1("**** Could not open the file %s .\n", (fmt ? pfname : fname));
|
|
|
872 |
return_error(gs_error_invalidfileaccess);
|
|
|
873 |
}
|
|
|
874 |
|
|
|
875 |
/* Close the output file for a device. */
|
|
|
876 |
int
|
|
|
877 |
gx_device_close_output_file(const gx_device * dev, const char *fname,
|
|
|
878 |
FILE *file)
|
|
|
879 |
{
|
|
|
880 |
gs_parsed_file_name_t parsed;
|
|
|
881 |
const char *fmt;
|
|
|
882 |
int code = gx_parse_output_file_name(&parsed, &fmt, fname, strlen(fname));
|
|
|
883 |
|
|
|
884 |
if (code < 0)
|
|
|
885 |
return code;
|
|
|
886 |
if (parsed.iodev) {
|
|
|
887 |
if (!strcmp(parsed.iodev->dname, "%stdout%"))
|
|
|
888 |
return 0;
|
|
|
889 |
/* NOTE: fname is unsubstituted if the name has any %nnd formats. */
|
|
|
890 |
if (parsed.iodev != iodev_default)
|
|
|
891 |
return parsed.iodev->procs.fclose(parsed.iodev, file);
|
|
|
892 |
}
|
|
|
893 |
gp_close_printer(file, (parsed.fname ? parsed.fname : fname));
|
|
|
894 |
return 0;
|
|
|
895 |
}
|