xref: /plan9/sys/src/cmd/gs/src/gdevpsu.c (revision 593dc095aefb2a85c828727bbfa9da139a49bdf4)
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
psw_print_lines(FILE * f,const char * const lines[])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
psw_put_procset_name(stream * s,const gx_device * dev,const gx_device_pswrite_common_t * pdpc)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
psw_print_procset_name(FILE * f,const gx_device * dev,const gx_device_pswrite_common_t * pdpc)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
psw_print_bbox(FILE * f,const gs_rect * pbbox)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     0
86 };
87 
88 private const char *const psw_eps_header[] = {
89     "%!PS-Adobe-3.0 EPSF-3.0",
90     0
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     0
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     0
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     0
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
is_seekable(FILE * f)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
psw_begin_file_header(FILE * f,const gx_device * dev,const gs_rect * pbbox,gx_device_pswrite_common_t * pdpc,bool ascii)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
psw_end_file_header(FILE * f)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
psw_end_file(FILE * f,const gx_device * dev,const gx_device_pswrite_common_t * pdpc,const gs_rect * pbbox,int page_count)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
psw_write_page_header(stream * s,const gx_device * dev,const gx_device_pswrite_common_t * pdpc,bool do_scale,long page_ord,int dictsize)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
psw_write_page_trailer(FILE * f,int num_copies,int flush)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 }
335