Commit | Line | Data |
---|---|---|
69695f33 MW |
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 | } |