chiark / gitweb /
Import upstream version 5.3.
[mup] / mup / mupdisp / genfile.c
1
2 /* Copyright (c) 1995, 1998, 1999, 2000, 2001 by Arkkra Enterprises */
3 /* All rights reserved */
4
5 /* functions to generate the bitmap output file from the Mup input.
6  * Includes functions for running Mup to produce PostScript, and to run
7  * Ghostscript on the output of Mup */
8
9
10 #ifdef __WATCOMC__
11 #include <process.h>
12 #include <errno.h>
13 #endif
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include "mupdisp.h"
17
18 #ifdef unix
19 #include <sys/wait.h>
20 #endif
21
22 /* bitmap for message to tell user to wait while we do our thing... */
23 #include "waitmsg.bm"
24
25 char Small_adjust[200]; /* PostScript instructions to add to get small
26                          * version of output (small enough for a whole page
27                          * to fit on the screen) */
28 char Large_adjust[200]; /* PostScript instructions to add for the large,
29                          * scroll-able version of the output */
30 double Reduction_factor;/* how to adjust for small version */
31 char Papersize[32];     /* -sPAPERSIZE argument to Ghostscript */
32
33 static void do_genfile P((int fd, int do_full));
34 static void copyfile P((int srcfile, long start, long end, int destfile));
35 static void genfile P((char *outfile, int do_full));
36 \f
37
38 /* generate one of the bitmap files if not already generated,
39  * either full or partial page based on value of fullpgmode.
40  * Return file descriptor of appropriate bitmap */
41
42 int
43 gen1file(fullpgmode)
44
45 int fullpgmode;         /* if YES, generate full-page version */
46
47 {
48         if (fullpgmode == YES) {
49                 /* if bitmap file not already created, make it now */
50                 if (Fullbitmaps <= 0) {
51                         Fullbitmaps = create_tmpfile(Fullfile);
52
53                         /* generate Postscript to scale and translate
54                          * output appropriately */
55                         sprintf(Small_adjust, "%f %f translate\n%f %f scale\n",
56                                         BITS_PER_LINE
57                                         * ((1.0 - Reduction_factor) / 2.0),
58                                         LINES_PER_PAGE - (LINES_PER_PAGE
59                                         * Reduction_factor
60                                         * Conf_info_p->adjust) - 4,
61                                         Reduction_factor,
62                                         Reduction_factor * Conf_info_p->adjust);
63
64                         /* tell user to wait */
65                         ( *(Conf_info_p->bitmap) ) (Waitmsg_bitmap,
66                                         Waitmsg_width, Waitmsg_height);
67                         /* generate the bitmap */
68                         genfile(Fullfile, YES);
69                 }
70                 close (Fullbitmaps);
71 #ifdef O_BINARY
72                 Fullbitmaps = open(Fullfile, O_RDONLY | O_BINARY, 0);
73 #else
74                 Fullbitmaps = open(Fullfile, O_RDONLY, 0);
75 #endif
76                 return(Fullbitmaps);
77         }
78         else {
79                 /* if bitmap file not already created, make it now */
80                 if (Partbitmaps <= 0) {
81                         Partbitmaps = create_tmpfile(Partfile);
82
83                         sprintf(Large_adjust, "%f %f translate\n%f %f scale\n",
84                                         0.0, (1.0 - Conf_info_p->adjust)
85                                         * LINES_PER_PAGE,
86                                         1.0, Conf_info_p->adjust);
87
88                         /* tell user to wait */
89                         ( *(Conf_info_p->bitmap) ) (Waitmsg_bitmap,
90                                         Waitmsg_width, Waitmsg_height);
91                         /* generate the bitmap */
92                         genfile(Partfile, NO);
93                 }
94                 close (Partbitmaps);
95 #ifdef O_BINARY
96                 Partbitmaps = open(Partfile, O_RDONLY | O_BINARY, 0);
97 #else
98                 Partbitmaps = open(Partfile, O_RDONLY, 0);
99 #endif
100                 return(Partbitmaps);
101         }
102 }
103 \f
104
105 /* generate a file containing bitmap representations of pages */
106
107 static void
108 genfile(outfile, do_full)
109
110 char *outfile;          /* put bitmaps in this file */
111 int do_full;            /* YES if to do full version of page */
112
113 {
114         char outfileopt[L_tmpnam+14];   /* space for -sOutputFile=outfile */
115         int ret;                /* return value from Ghostscript */
116         struct stat statinfo;
117 #ifdef unix
118         int pip[2];     /* for pipe to gs */
119         int child;      /* Ghostscript's process ID */
120         
121
122         /* create a pipe to Ghostscript */
123         if (pipe(pip) != 0) {
124                 Exit_errmsg = "can't set up pipe\n";
125                 (*Conf_info_p->cleanup) (1);
126         }
127
128         /* execute Ghostscript */
129         switch (child = fork()) {
130         case 0:
131                 /* connect its input to the pipe. Discard stdout and stderr */
132                 close(0);
133                 dup(pip[0]);
134                 close(1);
135                 dup(Nulldev);
136                 close(2);
137                 dup(Nulldev);
138                 sprintf(outfileopt, "-sOutputFile=%s", outfile);
139                 execlp("gs", "gs", "-sDEVICE=bit", Papersize, "-dQUIET",
140                                          outfileopt, "-", (char *) 0);
141                 /*FALLTHRU*/
142         case -1:
143                 Exit_errmsg = "can't exec Ghostscript";
144                 (*Conf_info_p->cleanup) (1);
145                 break;
146         default:
147                 close(pip[0]);
148
149                 /* generate the file */
150                 do_genfile(pip[1], do_full);
151                 close(pip[1]);
152
153                 /* wait for Ghostscript to complete */
154                 while ( wait(&ret) != child )
155                         ;
156                 if (ret != 0 || stat(Gs_errfile, &statinfo) == 0) {
157                         Exit_errmsg = "Ghostscript failed\n";
158                         (*Conf_info_p->cleanup) (1);
159                 }
160         }
161 #else
162 #ifdef __WATCOMC__
163         /* DOS can't do pipes, so use a temp file instead. */
164         int tmpf;
165         char pstmpfile[L_tmpnam];
166         static char message[128]; /* buffer for error message */
167
168
169         /* make a temp file for the modified PostScript */
170         tmpf = create_tmpfile(pstmpfile);
171         do_genfile(tmpf, do_full);
172         close(tmpf);
173
174         /* execute Ghostscript on the temp file */
175         sprintf(outfileopt, "-sOutputFile=%s", outfile);
176         ret = spawnlp(P_WAIT, "gs", "gs", "-sDEVICE=bit", Papersize,
177                 "-dQUIET", "-dNOPAUSE", outfileopt, pstmpfile, (char *) 0);
178
179         if (ret != 0) {
180                 /* try executing gs386 instead */
181                 /**** probably should check for a specific return code ****/
182                 ret = spawnlp(P_WAIT, "gs386", "gs386", "-sDEVICE=bit",
183                                 Papersize, "-dQUIET", "-dNOPAUSE",
184                                 outfileopt, pstmpfile, (char *) 0);
185         }
186
187         /* remove the temp file */
188         unlink(pstmpfile);
189         if (ret != 0 || stat(Gs_errfile, &statinfo) == 0) {
190                 unlink(outfile);
191                 if (ret == -1) {
192                         sprintf(message, "Ghostscript error %d (gs386 may be missing from your PATH?)\n", ret);
193                 }
194                 else {
195                         sprintf(message, "Ghostscript error %d\n", ret);
196                 }
197                 Exit_errmsg = message;
198                 (*Conf_info_p->cleanup) (1);
199         }
200 #else
201         Exit_errmsg = "unsupported platform\n";
202         (*Conf_info_p->cleanup) (1);
203 #endif
204 #endif
205 }
206 \f
207
208 /* determine proper PAPERSIZE, and set parameter appropirately for that
209  * page size */
210
211 /* table to translate width and height to PAPERSIZE name */
212 struct PaperSizeInfo {
213         int x;          /* width, must be <= MAX_BITS_PER_LINE */
214         int y;          /* height, must be <= MAX_LINES_PER_PAGE */
215         char *sizename; /* name of paper size */
216         double reduction;       /* how much to multiply by in small mode */
217 } Size_table[] = {
218         { 612, 792, "letter", 0.48 },   /* default has to be first */
219         { 540, 720, "note", 0.525 },
220         { 612, 1008, "legal", 0.375 },
221         { 595, 842, "a4", 0.46 },
222         { 421, 595, "a5", 0.65 },
223         { 297, 421, "a6", 0.8 },
224         { 612, 936, "flsa", 0.41 },
225         { 396, 612, "halfletter", 0.63 },
226         { 0, 0, (char *) 0 }
227 };
228
229 /* How many points away from a standard paper size we allow to
230  * account for roundoffs, since user was specifying in inches or cm
231  * rather than points, so they may be off somewhat */
232 #define FUZZ    24
233
234 void
235 get_paper_size(x, y)
236
237 int x, y;
238
239 {
240         int i;
241
242         /* if we've already been called once, we're already done */
243         if (Papersize[0] == '-') {
244                 return;
245         }
246
247         /* go through table till we find a standard size that matches */
248         for (i = 0; Size_table[i].x != 0; i++) {
249                 if ( (x > Size_table[i].x - FUZZ)
250                                 && (x < Size_table[i].x + FUZZ)
251                                 && (y > Size_table[i].y - FUZZ)
252                                 && (y < Size_table[i].y + FUZZ) ) {
253
254                         /* close enough to a standard size,
255                          * so we'll go with it */
256                         break;
257                 }
258         }
259
260         if (Size_table[i].x == 0) {
261                 /* if not found in table, use the default (letter) */
262                 i = 0;
263         }
264
265         /* set appropriate parameters */
266         Bits_per_line = Size_table[i].x;
267         Lines_per_page = Size_table[i].y;
268         Bytes_per_line = (Bits_per_line >> 3) + ((Bits_per_line & 0x7) ? 1 : 0);
269         Reduction_factor = Size_table[i].reduction;
270         sprintf(Papersize, "-sPAPERSIZE=%s", Size_table[i].sizename);
271 }
272 \f
273
274 /* generate bitmap from PostScript by calling Ghostscript */
275
276 static void
277 do_genfile(fd, do_full)
278
279 int fd;         /* file descriptor to write to */
280 int do_full;    /* if YES, do full page mode */
281
282 {
283         struct Pginfo *pg_p;    /* for info about each page */
284         char errhandler[200];   /* PostScript error handler redefinition */
285
286
287         /* arrange to quit Ghostscript on errors (shouldn't get errors
288          * from Mup output, but could run out of memory, causing VMerror) */
289         sprintf(errhandler, "/handleerror { $error begin (%s) (w) file dup errorname 100 string cvs writestring (\\n) writestring end quit } def\n", Gs_errfile);
290         write(fd, errhandler, strlen(errhandler));
291
292         /* copy the prolog */
293         copyfile(Psfile, Beginprolog, Endprolog, fd);
294         
295         /* for each page add proper scaling, etc information */
296         for (pg_p = Pagehead; pg_p != (struct Pginfo *) 0;
297                                                         pg_p = pg_p->next) {
298                 write(fd, "save\n", 5);
299                 if (do_full == YES) {
300                         char tmpbuff[100];
301
302                         write(fd, "0.2 setgray\n", 12);
303                         sprintf(tmpbuff, "0 0 moveto 0 %d lineto %d %d lineto %d 0 lineto closepath fill\n",
304                                         Lines_per_page,
305                                         Bits_per_line,
306                                         Lines_per_page,
307                                         Bits_per_line);
308                         write(fd, tmpbuff, strlen(tmpbuff));
309
310                         write(fd, Small_adjust, strlen(Small_adjust));
311                         write(fd, "1 setgray\n", 10);
312                         write(fd, tmpbuff, strlen(tmpbuff));
313                         write(fd, "0 setgray\n", 10);
314                 }
315                 else {
316                         write(fd, Large_adjust, strlen(Large_adjust));
317                 }
318
319                 /* copy a page worth of PostScript */
320                 copyfile(Psfile, pg_p->begin, pg_p->end, fd);
321                 write(fd, "restore\n", 8);
322         }
323
324         /* finish up the Ghostscript input */
325         write(fd, "quit\n", 5);
326 }
327 \f
328
329 /* copy a portion of one file descriptor to another file description */
330
331 static void
332 copyfile(srcfile, start, end, destfile)
333
334 int srcfile;    /* read from this file */
335 long start;     /* start at this offset in srcfile */
336 long end;       /* go this far in srcfile */
337 int destfile;   /* write to current position in this file */
338
339 {
340         char buff[BUFSIZ];      /* buffer for copying */
341         int size;               /* how much to copy at once */
342         long remaining;         /* how much still to be copied */
343
344  
345         /* go to specified spot in source file */
346         lseek(srcfile, start, SEEK_SET);
347
348         /* copy a block at a time, last piece will be wahatever is left */
349         for (remaining = end - start; remaining > 0L; remaining -= BUFSIZ) {
350                 size = (remaining >= BUFSIZ ? BUFSIZ : remaining);
351                 
352                 if (read(srcfile, buff, size) == size) {
353                         if (write(destfile, buff, size) != size) {
354                                 Exit_errmsg = "File write failed (probably out of disk space)\n";
355                                 ( *(Conf_info_p)->cleanup) (1);
356                         }
357                 }
358                 else {
359                         Exit_errmsg = "Read failed\n";
360                         ( *(Conf_info_p)->cleanup) (1);
361                 }
362         }
363 }
364 \f
365
366 /* execute Mup, putting output in Psfile */
367
368 void
369 run_mup(argv)
370
371 char **argv;    /* arguments to pass to Mup */
372
373 {
374         int ret;
375         int child;
376
377
378 #ifdef unix
379         switch(child = fork()) {
380         case 0:
381                 /* arrange to put output in Psfile */
382                 close(1);
383                 dup(Psfile);
384
385                 /* execute Mup */
386                 execvp("mup", argv);
387                 fprintf(stderr, "failed to execute Mup\n");
388                 exit(1);
389         case -1:
390                 fprintf(stderr, "failed to fork Mup\n");
391                 generalcleanup(1);
392                 /*NOTREACHED*/
393                 break;
394         default:
395                 /* wait for Mup to complete */
396                 while (wait(&ret) != child)
397                         ;
398                 if (ret != 0) {
399                         unlink(Mupfile);
400                         generalcleanup(ret);
401                 }
402                 break;
403         }
404 #else
405 #ifdef __WATCOMC__
406         /* arrange to put output in Psfile */
407         close(1);
408         dup(Psfile);
409
410         /* execute Mup */
411         if ((ret = spawnvp(P_WAIT, "mup", argv)) != 0) {
412                 if (errno != 0) {
413                         perror("Mup failed");
414                 }
415                 unlink(Mupfile);
416                 exit(ret);
417         } 
418 #else
419         fprintf(stderr, "unsupported platform\n");
420 #endif
421 #endif
422 }