| 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 | } |