2 |
- |
1 |
/* Copyright (C) 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: gdevpsu.c,v 1.19 2005/03/02 18:08:38 raph Exp $ */
|
|
|
18 |
/* PostScript-writing utilities */
|
|
|
19 |
#include "math_.h"
|
|
|
20 |
#include "time_.h"
|
|
|
21 |
#include "stat_.h"
|
|
|
22 |
#include "unistd_.h"
|
|
|
23 |
#include "gx.h"
|
|
|
24 |
#include "gscdefs.h"
|
|
|
25 |
#include "gxdevice.h"
|
|
|
26 |
#include "gdevpsu.h"
|
|
|
27 |
#include "spprint.h"
|
|
|
28 |
#include "stream.h"
|
|
|
29 |
#include "gserrors.h"
|
|
|
30 |
|
|
|
31 |
/* ---------------- Low level ---------------- */
|
|
|
32 |
|
|
|
33 |
/* Write a 0-terminated array of strings as lines. */
|
|
|
34 |
int
|
|
|
35 |
psw_print_lines(FILE *f, const char *const lines[])
|
|
|
36 |
{
|
|
|
37 |
int i;
|
|
|
38 |
for (i = 0; lines[i] != 0; ++i) {
|
|
|
39 |
if (fprintf(f, "%s\n", lines[i]) < 0)
|
|
|
40 |
return_error(gs_error_ioerror);
|
|
|
41 |
}
|
|
|
42 |
return 0;
|
|
|
43 |
}
|
|
|
44 |
|
|
|
45 |
/* Write the ProcSet name. */
|
|
|
46 |
private void
|
|
|
47 |
psw_put_procset_name(stream *s, const gx_device *dev,
|
|
|
48 |
const gx_device_pswrite_common_t *pdpc)
|
|
|
49 |
{
|
|
|
50 |
pprints1(s, "GS_%s", dev->dname);
|
|
|
51 |
pprintd3(s, "_%d_%d_%d",
|
|
|
52 |
(int)pdpc->LanguageLevel,
|
|
|
53 |
(int)(pdpc->LanguageLevel * 10 + 0.5) % 10,
|
|
|
54 |
pdpc->ProcSet_version);
|
|
|
55 |
}
|
|
|
56 |
private void
|
|
|
57 |
psw_print_procset_name(FILE *f, const gx_device *dev,
|
|
|
58 |
const gx_device_pswrite_common_t *pdpc)
|
|
|
59 |
{
|
|
|
60 |
byte buf[100]; /* arbitrary */
|
|
|
61 |
stream s;
|
|
|
62 |
|
|
|
63 |
s_init(&s, dev->memory);
|
|
|
64 |
swrite_file(&s, f, buf, sizeof(buf));
|
|
|
65 |
psw_put_procset_name(&s, dev, pdpc);
|
|
|
66 |
sflush(&s);
|
|
|
67 |
}
|
|
|
68 |
|
|
|
69 |
/* Write a bounding box. */
|
|
|
70 |
private void
|
|
|
71 |
psw_print_bbox(FILE *f, const gs_rect *pbbox)
|
|
|
72 |
{
|
|
|
73 |
fprintf(f, "%%%%BoundingBox: %d %d %d %d\n",
|
|
|
74 |
(int)floor(pbbox->p.x), (int)floor(pbbox->p.y),
|
|
|
75 |
(int)ceil(pbbox->q.x), (int)ceil(pbbox->q.y));
|
|
|
76 |
fprintf(f, "%%%%HiResBoundingBox: %f %f %f %f\n",
|
|
|
77 |
pbbox->p.x, pbbox->p.y, pbbox->q.x, pbbox->q.y);
|
|
|
78 |
}
|
|
|
79 |
|
|
|
80 |
/* ---------------- File level ---------------- */
|
|
|
81 |
|
|
|
82 |
private const char *const psw_ps_header[] = {
|
|
|
83 |
"%!PS-Adobe-3.0",
|
|
|
84 |
"%%Pages: (atend)",
|
|
|
85 |
|
|
|
86 |
};
|
|
|
87 |
|
|
|
88 |
private const char *const psw_eps_header[] = {
|
|
|
89 |
"%!PS-Adobe-3.0 EPSF-3.0",
|
|
|
90 |
|
|
|
91 |
};
|
|
|
92 |
|
|
|
93 |
private const char *const psw_begin_prolog[] = {
|
|
|
94 |
"%%EndComments",
|
|
|
95 |
"%%BeginProlog",
|
|
|
96 |
"% This copyright applies to everything between here and the %%EndProlog:",
|
|
|
97 |
/* copyright */
|
|
|
98 |
/* begin ProcSet */
|
|
|
99 |
|
|
|
100 |
};
|
|
|
101 |
|
|
|
102 |
/*
|
|
|
103 |
* To achieve page independence, every page must in the general case
|
|
|
104 |
* set page parameters. To preserve duplexing the page cannot set page
|
|
|
105 |
* parameters. The following code checks the current page size and sets
|
|
|
106 |
* it only if it is necessary.
|
|
|
107 |
*/
|
|
|
108 |
private const char *const psw_ps_procset[] = {
|
|
|
109 |
/* <w> <h> <sizename> setpagesize - */
|
|
|
110 |
"/PageSize 2 array def"
|
|
|
111 |
"/setpagesize" /* x y /a4 -> - */
|
|
|
112 |
"{ PageSize aload pop " /* x y /a4 x0 y0 */
|
|
|
113 |
"3 index eq exch", /* x y /a4 bool x0 */
|
|
|
114 |
"4 index eq and" /* x y /a4 bool */
|
|
|
115 |
"{ pop pop pop"
|
|
|
116 |
"}"
|
|
|
117 |
"{ PageSize dup 1", /* x y /a4 [ ] [ ] 1 */
|
|
|
118 |
"5 -1 roll put 0 " /* x /a4 [ y] 0 */
|
|
|
119 |
"4 -1 roll put " /* /a4 */
|
|
|
120 |
"dup null eq {false} {dup where} ifelse"
|
|
|
121 |
"{ exch get exec" /* - */
|
|
|
122 |
"}",
|
|
|
123 |
"{ pop" /* - */
|
|
|
124 |
"/setpagedevice where",
|
|
|
125 |
"{ pop 1 dict dup /PageSize PageSize put setpagedevice"
|
|
|
126 |
"}",
|
|
|
127 |
"{ /setpage where"
|
|
|
128 |
"{ pop PageSize aload pop pageparams 3 {exch pop} repeat",
|
|
|
129 |
"setpage"
|
|
|
130 |
"}"
|
|
|
131 |
"if"
|
|
|
132 |
"}"
|
|
|
133 |
"ifelse"
|
|
|
134 |
"}"
|
|
|
135 |
"ifelse"
|
|
|
136 |
"}"
|
|
|
137 |
"ifelse"
|
|
|
138 |
"} bind def",
|
|
|
139 |
|
|
|
140 |
};
|
|
|
141 |
|
|
|
142 |
private const char *const psw_end_prolog[] = {
|
|
|
143 |
"end readonly def",
|
|
|
144 |
"%%EndResource", /* ProcSet */
|
|
|
145 |
"/pagesave null def", /* establish binding */
|
|
|
146 |
"%%EndProlog",
|
|
|
147 |
|
|
|
148 |
};
|
|
|
149 |
|
|
|
150 |
/* Return true when the file is seekable.
|
|
|
151 |
* On Windows NT ftell() returns some non-EOF value when used on pipes.
|
|
|
152 |
*/
|
|
|
153 |
private bool
|
|
|
154 |
is_seekable(FILE *f)
|
|
|
155 |
{
|
|
|
156 |
struct stat buf;
|
|
|
157 |
|
|
|
158 |
if(fstat(fileno(f), &buf))
|
|
|
159 |
return 0;
|
|
|
160 |
return S_ISREG(buf.st_mode);
|
|
|
161 |
}
|
|
|
162 |
|
|
|
163 |
/*
|
|
|
164 |
* Write the file header, up through the BeginProlog. This must write to a
|
|
|
165 |
* file, not a stream, because it may be called during finalization.
|
|
|
166 |
*/
|
|
|
167 |
int
|
|
|
168 |
psw_begin_file_header(FILE *f, const gx_device *dev, const gs_rect *pbbox,
|
|
|
169 |
gx_device_pswrite_common_t *pdpc, bool ascii)
|
|
|
170 |
{
|
|
|
171 |
psw_print_lines(f, (pdpc->ProduceEPS ? psw_eps_header : psw_ps_header));
|
|
|
172 |
if (pbbox) {
|
|
|
173 |
psw_print_bbox(f, pbbox);
|
|
|
174 |
pdpc->bbox_position = 0;
|
|
|
175 |
} else if (!is_seekable(f)) { /* File is not seekable. */
|
|
|
176 |
pdpc->bbox_position = -1;
|
|
|
177 |
fputs("%%BoundingBox: (atend)\n", f);
|
|
|
178 |
fputs("%%HiResBoundingBox: (atend)\n", f);
|
|
|
179 |
} else { /* File is seekable, leave room to rewrite bbox. */
|
|
|
180 |
pdpc->bbox_position = ftell(f);
|
|
|
181 |
fputs("%...............................................................\n", f);
|
|
|
182 |
fputs("%...............................................................\n", f);
|
|
|
183 |
}
|
|
|
184 |
fprintf(f, "%%%%Creator: %s %ld (%s)\n", gs_product, (long)gs_revision,
|
|
|
185 |
dev->dname);
|
|
|
186 |
{
|
|
|
187 |
time_t t;
|
|
|
188 |
struct tm tms;
|
|
|
189 |
|
|
|
190 |
time(&t);
|
|
|
191 |
tms = *localtime(&t);
|
|
|
192 |
fprintf(f, "%%%%CreationDate: %d/%02d/%02d %02d:%02d:%02d\n",
|
|
|
193 |
tms.tm_year + 1900, tms.tm_mon + 1, tms.tm_mday,
|
|
|
194 |
tms.tm_hour, tms.tm_min, tms.tm_sec);
|
|
|
195 |
}
|
|
|
196 |
if (ascii)
|
|
|
197 |
fputs("%%DocumentData: Clean7Bit\n", f);
|
|
|
198 |
if (pdpc->LanguageLevel >= 2.0)
|
|
|
199 |
fprintf(f, "%%%%LanguageLevel: %d\n", (int)pdpc->LanguageLevel);
|
|
|
200 |
else if (pdpc->LanguageLevel == 1.5)
|
|
|
201 |
fputs("%%Extensions: CMYK\n", f);
|
|
|
202 |
psw_print_lines(f, psw_begin_prolog);
|
|
|
203 |
fprintf(f, "%% %s\n", gs_copyright);
|
|
|
204 |
fputs("%%BeginResource: procset ", f);
|
|
|
205 |
fflush(f);
|
|
|
206 |
psw_print_procset_name(f, dev, pdpc);
|
|
|
207 |
fputs("\n/", f);
|
|
|
208 |
fflush(f);
|
|
|
209 |
psw_print_procset_name(f, dev, pdpc);
|
|
|
210 |
fputs(" 80 dict dup begin\n", f);
|
|
|
211 |
psw_print_lines(f, psw_ps_procset);
|
|
|
212 |
fflush(f);
|
|
|
213 |
if (ferror(f))
|
|
|
214 |
return_error(gs_error_ioerror);
|
|
|
215 |
return 0;
|
|
|
216 |
}
|
|
|
217 |
|
|
|
218 |
/*
|
|
|
219 |
* End the file header.
|
|
|
220 |
*/
|
|
|
221 |
int
|
|
|
222 |
psw_end_file_header(FILE *f)
|
|
|
223 |
{
|
|
|
224 |
return psw_print_lines(f, psw_end_prolog);
|
|
|
225 |
}
|
|
|
226 |
|
|
|
227 |
/*
|
|
|
228 |
* End the file.
|
|
|
229 |
*/
|
|
|
230 |
int
|
|
|
231 |
psw_end_file(FILE *f, const gx_device *dev,
|
|
|
232 |
const gx_device_pswrite_common_t *pdpc, const gs_rect *pbbox,
|
|
|
233 |
int /* should be long */ page_count)
|
|
|
234 |
{
|
|
|
235 |
if (f == NULL)
|
|
|
236 |
return 0; /* clients should be more careful */
|
|
|
237 |
fprintf(f, "%%%%Trailer\n%%%%Pages: %ld\n", (long)page_count);
|
|
|
238 |
if (ferror(f))
|
|
|
239 |
return_error(gs_error_ioerror);
|
|
|
240 |
if (dev->PageCount > 0 && pdpc->bbox_position != 0) {
|
|
|
241 |
if (pdpc->bbox_position >= 0) {
|
|
|
242 |
long save_pos = ftell(f);
|
|
|
243 |
|
|
|
244 |
fseek(f, pdpc->bbox_position, SEEK_SET);
|
|
|
245 |
psw_print_bbox(f, pbbox);
|
|
|
246 |
fputc('%', f);
|
|
|
247 |
if (ferror(f))
|
|
|
248 |
return_error(gs_error_ioerror);
|
|
|
249 |
fseek(f, save_pos, SEEK_SET);
|
|
|
250 |
} else
|
|
|
251 |
psw_print_bbox(f, pbbox);
|
|
|
252 |
}
|
|
|
253 |
if (!pdpc->ProduceEPS)
|
|
|
254 |
fputs("%%EOF\n", f);
|
|
|
255 |
if (ferror(f))
|
|
|
256 |
return_error(gs_error_ioerror);
|
|
|
257 |
return 0;
|
|
|
258 |
}
|
|
|
259 |
|
|
|
260 |
/* ---------------- Page level ---------------- */
|
|
|
261 |
|
|
|
262 |
/*
|
|
|
263 |
* Write the page header.
|
|
|
264 |
*/
|
|
|
265 |
int
|
|
|
266 |
psw_write_page_header(stream *s, const gx_device *dev,
|
|
|
267 |
const gx_device_pswrite_common_t *pdpc,
|
|
|
268 |
bool do_scale, long page_ord, int dictsize)
|
|
|
269 |
{
|
|
|
270 |
long page = dev->PageCount + 1;
|
|
|
271 |
|
|
|
272 |
pprintld2(s, "%%%%Page: %ld %ld\n%%%%BeginPageSetup\n", page, page_ord);
|
|
|
273 |
/*
|
|
|
274 |
* Adobe's documentation says that page setup must be placed outside the
|
|
|
275 |
* save/restore that encapsulates the page contents, and that the
|
|
|
276 |
* showpage must be placed after the restore. This means that to
|
|
|
277 |
* achieve page independence, *every* page's setup code must include a
|
|
|
278 |
* setpagedevice that sets *every* page device parameter that is changed
|
|
|
279 |
* on *any* page. Currently, the only such parameter relevant to this
|
|
|
280 |
* driver is page size, but there might be more in the future.
|
|
|
281 |
*/
|
|
|
282 |
psw_put_procset_name(s, dev, pdpc);
|
|
|
283 |
stream_puts(s, " begin\n");
|
|
|
284 |
if (!pdpc->ProduceEPS) {
|
|
|
285 |
int width = (int)(dev->width * 72.0 / dev->HWResolution[0] + 0.5);
|
|
|
286 |
int height = (int)(dev->height * 72.0 / dev->HWResolution[1] + 0.5);
|
|
|
287 |
typedef struct ps_ {
|
|
|
288 |
const char *size_name;
|
|
|
289 |
int width, height;
|
|
|
290 |
} page_size;
|
|
|
291 |
static const page_size sizes[] = {
|
|
|
292 |
{"/11x17", 792, 1224},
|
|
|
293 |
{"/a3", 842, 1190},
|
|
|
294 |
{"/a4", 595, 842},
|
|
|
295 |
{"/b5", 501, 709},
|
|
|
296 |
{"/ledger", 1224, 792},
|
|
|
297 |
{"/legal", 612, 1008},
|
|
|
298 |
{"/letter", 612, 792},
|
|
|
299 |
{"null", 0, 0}
|
|
|
300 |
};
|
|
|
301 |
const page_size *p = sizes;
|
|
|
302 |
|
|
|
303 |
while (p->size_name[0] == '/' &&
|
|
|
304 |
(p->width != width || p->height != height))
|
|
|
305 |
++p;
|
|
|
306 |
pprintd2(s, "%d %d ", width, height);
|
|
|
307 |
pprints1(s, "%s setpagesize\n", p->size_name);
|
|
|
308 |
}
|
|
|
309 |
pprintd1(s, "/pagesave save store %d dict begin\n", dictsize);
|
|
|
310 |
if (do_scale)
|
|
|
311 |
pprintg2(s, "%g %g scale\n",
|
|
|
312 |
72.0 / dev->HWResolution[0], 72.0 / dev->HWResolution[1]);
|
|
|
313 |
stream_puts(s, "%%EndPageSetup\ngsave mark\n");
|
|
|
314 |
if (s->end_status == ERRC)
|
|
|
315 |
return_error(gs_error_ioerror);
|
|
|
316 |
return 0;
|
|
|
317 |
}
|
|
|
318 |
|
|
|
319 |
/*
|
|
|
320 |
* Write the page trailer. We do this directly to the file, rather than to
|
|
|
321 |
* the stream, because we may have to do it during finalization.
|
|
|
322 |
*/
|
|
|
323 |
int
|
|
|
324 |
psw_write_page_trailer(FILE *f, int num_copies, int flush)
|
|
|
325 |
{
|
|
|
326 |
fprintf(f, "cleartomark end end pagesave restore\n");
|
|
|
327 |
if (num_copies != 1)
|
|
|
328 |
fprintf(f, "userdict /#copies %d put\n", num_copies);
|
|
|
329 |
fprintf(f, " %s\n%%%%PageTrailer\n", (flush ? "showpage" : "copypage"));
|
|
|
330 |
fflush(f);
|
|
|
331 |
if (ferror(f))
|
|
|
332 |
return_error(gs_error_ioerror);
|
|
|
333 |
return 0;
|
|
|
334 |
}
|