1 /* 2 * 3 * postbgi - BGI (Basic Graphical Instructions) to PostScript translator. 4 * 5 * A simple program that translates BGI files into PostScript. Probably only 6 * useful in Computer Centers that support STARE or PRISM plotters. Most of the 7 * code was borrowed from the corresponding program that was written for printers 8 * that understand Impress. 9 * 10 * Extending the original program to handle PRISM jobs was not trivial. Graphics 11 * packages that support PRISM occasionally use BGI commands that I ignored in the 12 * STARE implementation. Subroutines, color requests, patterns (for filling), and 13 * filled trapeziods were the most important omissions. All are now implemented, 14 * and at present only repeats, filled slices, and raster rectangles are missing. 15 * 16 * Pattern filling results were not always predictable or even good, unless the 17 * halftone screen definitions were changed and scaling was adjusted so one pixel 18 * in user space mapped into an integral number of device space pixels. Doing that 19 * makes the resulting PostScript output device dependent, but was often necessary. 20 * I've added two booleans to the PostScript prologue (fixscreen and scaletodevice) 21 * that control what's done. By default both are false (check postbgi.ps) but can 22 * be set to true on the command line using the -P option or by hand by changing 23 * the definitions in the prologue. A command line that would set fixscreen and 24 * scaletodevice true would look like, 25 * 26 * postbgi -P"/fixscreen true" -P"/scaletodevice true" file >file.ps 27 * 28 * Several other approaches are available if you want to have your spooler handle 29 * STARE and PRISM jobs differently. A boolean called prism is defined in the 30 * prologue (postbgi.ps) and if it's set to true PostScript procedure setup will 31 * set fixscreen and scaletodevice to true before anything important is done. That 32 * means the following command line, 33 * 34 * postbgi -P"/prism true" file >file.ps 35 * 36 * accomplishes the same things as the last example. Two different prologue files, 37 * one for STARE jobs and the other for PRISM, could be used and the spooler could 38 * point postbgi to the appropriate one using the -L option. In that case the only 39 * important difference in the two prologues would be the definition of prism. The 40 * prologue used for PRISM jobs would have prism set to true, while the STARE 41 * prologue would have it set to false. 42 * 43 * Also included is code that ties lines to device space coordinates. What you get 44 * is a consistent line thickness, but placement of lines won't be exact. It's a 45 * trade-off that should be right for most jobs. Everything is implemented in the 46 * prologue (postbgi.ps) and nothing will be done if the linewidth is zero or if 47 * the boolean fixlinewidth (again in postbgi.ps) is false. Once again the -P 48 * option can be used to set fixlinewidth to whatever you choose. 49 * 50 * BGI supports color mixing but PostScript doesn't. BGI files that expect to mix 51 * colors won't print properly. PostScript's fill operator overlays whatever has 52 * already been put down. Implementing color mixing would have been a terribly 53 * difficult job - not worth the effort! 54 * 55 * The PostScript prologue is copied from *prologue before any of the input files 56 * are translated. The program expects that the following PostScript procedures 57 * are defined in that file: 58 * 59 * setup 60 * 61 * mark ... setup - 62 * 63 * Handles special initialization stuff that depends on how the program 64 * was called. Expects to find a mark followed by key/value pairs on the 65 * stack. The def operator is applied to each pair up to the mark, then 66 * the default state is set up. 67 * 68 * pagesetup 69 * 70 * page pagesetup - 71 * 72 * Does whatever is needed to set things up for the next page. Expects 73 * to find the current page number on the stack. 74 * 75 * v 76 * 77 * dx1 dy1 ... dxn dyn x y v - 78 * 79 * Draws the vector described by the numbers on the stack. The top two 80 * numbers are the coordinates of the starting point. The rest of the 81 * numbers are relative displacements from the preceeding point. 82 * 83 * pp 84 * 85 * x1 y1 ... xn yn string pp - 86 * 87 * Prints string, which is always a single character, at the points 88 * represented by the rest of the numbers on the stack. 89 * 90 * R 91 * 92 * n deltax deltay x y R - 93 * 94 * Creates a rectangular path with its lower left corner at (x, y) and 95 * sides of length deltax and deltay. The resulting path is stroked if 96 * n is 0 and filled otherwise. 97 * 98 * T 99 * 100 * dx3 dy3 dx2 dy2 dx1 dy1 x y T - 101 * 102 * Fills a trapezoid starting at (x, y) and having relative displacements 103 * given by the (dx, dy) pairs. 104 * 105 * t 106 * 107 * angle x y string t - 108 * 109 * Prints string starting at (x, y) using an orientation of angle degrees. 110 * The PostScript procedure can handle any angle, but BGI files will only 111 * request 0 or 90 degrees. Text printed at any other orientation will be 112 * vector generated. 113 * 114 * p 115 * 116 * x y p - 117 * 118 * Called to mark the point (x, y). It fills a small circle, that right 119 * now has a constant radius. This stuff could probably be much more 120 * efficient? 121 * 122 * l 123 * 124 * array l - 125 * 126 * Sets the line drawing mode according to the description given in 127 * array. The arrays that describe the different line styles are declared 128 * in STYLES (file posttek.h), although it would be better to have them 129 * defined in the prologue. 130 * 131 * c 132 * 133 * red green blue c - 134 * 135 * Sets the current PostScript RGB color using setrgbcolor. Also used for 136 * selecting appropriate patterns as colors. 137 * 138 * f 139 * 140 * bgisize f - 141 * 142 * Changes the size of the font that's used to print text. bgisize is a 143 * grid separation in a 5 by 7 array in which characters are assumed to 144 * be built. 145 * 146 * done 147 * 148 * done 149 * 150 * Makes sure the last page is printed. Only needed when we're printing 151 * more than one page on each sheet of paper. 152 * 153 * The default line width is zero, which forces lines to be one pixel wide. That 154 * works well for 'write to black' engines but won't be right for 'write to white' 155 * engines. The line width can be changed using the -w option, or you can change 156 * the initialization of linewidth in the prologue. Code in the prologue supports 157 * the generation of uniform width lines when linewidth is non-zero and boolean 158 * fixlinewidth is true. 159 * 160 * Many default values, like the magnification and orientation, are defined in 161 * the prologue, which is where they belong. If they're changed (by options), an 162 * appropriate definition is made after the prologue is added to the output file. 163 * The -P option passes arbitrary PostScript through to the output file. Among 164 * other things it can be used to set (or change) values that can't be accessed by 165 * other options. 166 * 167 */ 168 169 #include <stdio.h> 170 #include <sys/types.h> 171 #include <fcntl.h> 172 #include <signal.h> 173 #include <math.h> 174 #include <ctype.h> 175 #ifdef plan9 176 #define isascii(c) ((unsigned char)(c)<=0177) 177 #endif 178 179 #include "comments.h" /* PostScript file structuring comments */ 180 #include "gen.h" /* general purpose definitions */ 181 #include "path.h" /* for the prologue */ 182 #include "ext.h" /* external variable declarations */ 183 #include "postbgi.h" /* a few definitions just used here */ 184 185 char *optnames = "a:c:f:m:n:o:p:w:x:y:A:C:E:J:L:P:R:DI"; 186 187 char *prologue = POSTBGI; /* default PostScript prologue */ 188 char *formfile = FORMFILE; /* stuff for multiple pages per sheet */ 189 190 int formsperpage = 1; /* page images on each piece of paper */ 191 int copies = 1; /* and this many copies of each sheet */ 192 193 char *styles[] = STYLES; /* descriptions of line styles */ 194 195 int hpos = 0; /* current horizontal */ 196 int vpos = 0; /* and vertical position */ 197 198 int bgisize = BGISIZE; /* just the character grid spacing */ 199 int linespace; /* distance between lines of text */ 200 201 int bgimode; /* character or graph mode */ 202 203 int in_subr = FALSE; /* currently defining a subroutine */ 204 int in_global = FALSE; /* to save space with subroutine defs */ 205 int subr_id = 0; /* defining this subroutine */ 206 int shpos = 0; /* starting horizontal */ 207 int svpos = 0; /* and vertical positions - subroutines */ 208 Disp displacement[64]; /* dx and dy after a subroutine call */ 209 210 Fontmap fontmap[] = FONTMAP; /* for translating font names */ 211 char *fontname = "Courier"; /* use this PostScript font */ 212 213 int page = 0; /* page we're working on */ 214 int printed = 0; /* printed this many pages */ 215 216 FILE *fp_in = stdin; /* read from this file */ 217 FILE *fp_out = NULL; /* and write stuff here */ 218 FILE *fp_acct = NULL; /* for accounting data */ 219 220 /*****************************************************************************/ 221 222 main(agc, agv) 223 224 int agc; 225 char *agv[]; 226 227 { 228 229 /* 230 * 231 * A program that converts BGI (Basic Graphical Instructions) files generated by 232 * packages like GRAFPAC and DISSPLA into PostScript. It does an adequate job but 233 * is far from perfect. A few things still haven't been implemented (eg. repeats 234 * and raster rectangles), but what's here should be good enough for most of our 235 * STARE and PRISM jobs. Color mixing (in PRISM jobs) won't work on PostScript 236 * printers, and there's no chance I'll implement it! 237 * 238 */ 239 240 argc = agc; /* global so everyone can use them */ 241 argv = agv; 242 243 prog_name = argv[0]; /* just for error messages */ 244 245 init_signals(); /* set up interrupt handling */ 246 header(); /* PostScript header comments */ 247 options(); /* command line options */ 248 setup(); /* for PostScript */ 249 arguments(); /* followed by each input file */ 250 done(); /* print the last page etc. */ 251 account(); /* job accounting data */ 252 253 exit(x_stat); /* everything probably went OK */ 254 255 } /* End of main */ 256 257 /*****************************************************************************/ 258 259 init_signals() 260 261 { 262 263 /* 264 * 265 * Make sure we handle interrupts. 266 * 267 */ 268 269 if ( signal(SIGINT, interrupt) == SIG_IGN ) { 270 signal(SIGINT, SIG_IGN); 271 signal(SIGQUIT, SIG_IGN); 272 signal(SIGHUP, SIG_IGN); 273 } else { 274 signal(SIGHUP, interrupt); 275 signal(SIGQUIT, interrupt); 276 } /* End else */ 277 278 signal(SIGTERM, interrupt); 279 280 } /* End of init_signals */ 281 282 /*****************************************************************************/ 283 284 header() 285 286 { 287 288 int ch; /* return value from getopt() */ 289 int old_optind = optind; /* for restoring optind - should be 1 */ 290 291 /* 292 * 293 * Scans the option list looking for things, like the prologue file, that we need 294 * right away but could be changed from the default. Doing things this way is an 295 * attempt to conform to Adobe's latest file structuring conventions. In particular 296 * they now say there should be nothing executed in the prologue, and they have 297 * added two new comments that delimit global initialization calls. Once we know 298 * where things really are we write out the job header, follow it by the prologue, 299 * and then add the ENDPROLOG and BEGINSETUP comments. 300 * 301 */ 302 303 while ( (ch = getopt(argc, argv, optnames)) != EOF ) 304 if ( ch == 'L' ) 305 prologue = optarg; 306 else if ( ch == '?' ) 307 error(FATAL, ""); 308 309 optind = old_optind; /* get ready for option scanning */ 310 311 fprintf(stdout, "%s", CONFORMING); 312 fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION); 313 fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND); 314 fprintf(stdout, "%s %s\n", PAGES, ATEND); 315 fprintf(stdout, "%s", ENDCOMMENTS); 316 317 if ( cat(prologue) == FALSE ) 318 error(FATAL, "can't read %s", prologue); 319 320 fprintf(stdout, "%s", ENDPROLOG); 321 fprintf(stdout, "%s", BEGINSETUP); 322 fprintf(stdout, "mark\n"); 323 324 } /* End of header */ 325 326 /*****************************************************************************/ 327 328 options() 329 330 { 331 332 int ch; /* option name - from getopt() */ 333 334 /* 335 * 336 * Reads and processes the command line options. 337 * 338 */ 339 340 while ( (ch = getopt(argc, argv, optnames)) != EOF ) { 341 switch ( ch ) { 342 case 'a': /* aspect ratio */ 343 fprintf(stdout, "/aspectratio %s def\n", optarg); 344 break; 345 346 case 'c': /* copies */ 347 copies = atoi(optarg); 348 fprintf(stdout, "/#copies %s def\n", optarg); 349 break; 350 351 case 'f': /* new font */ 352 fontname = get_font(optarg); 353 fprintf(stdout, "/font /%s def\n", fontname); 354 break; 355 356 case 'm': /* magnification */ 357 fprintf(stdout, "/magnification %s def\n", optarg); 358 break; 359 360 case 'n': /* forms per page */ 361 formsperpage = atoi(optarg); 362 fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg); 363 fprintf(stdout, "/formsperpage %s def\n", optarg); 364 break; 365 366 case 'o': /* output page list */ 367 out_list(optarg); 368 break; 369 370 case 'p': /* landscape or portrait mode */ 371 if ( *optarg == 'l' ) 372 fprintf(stdout, "/landscape true def\n"); 373 else fprintf(stdout, "/landscape false def\n"); 374 break; 375 376 case 'w': /* line width */ 377 fprintf(stdout, "/linewidth %s def\n", optarg); 378 break; 379 380 case 'x': /* shift horizontally */ 381 fprintf(stdout, "/xoffset %s def\n", optarg); 382 break; 383 384 case 'y': /* and vertically on the page */ 385 fprintf(stdout, "/yoffset %s def\n", optarg); 386 break; 387 388 case 'A': /* force job accounting */ 389 case 'J': 390 if ( (fp_acct = fopen(optarg, "a")) == NULL ) 391 error(FATAL, "can't open accounting file %s", optarg); 392 break; 393 394 case 'C': /* copy file straight to output */ 395 if ( cat(optarg) == FALSE ) 396 error(FATAL, "can't read %s", optarg); 397 break; 398 399 case 'E': /* text font encoding */ 400 fontencoding = optarg; 401 break; 402 403 case 'L': /* Postscript prologue file */ 404 prologue = optarg; 405 break; 406 407 case 'P': /* PostScript pass through */ 408 fprintf(stdout, "%s\n", optarg); 409 break; 410 411 case 'R': /* special global or page level request */ 412 saverequest(optarg); 413 break; 414 415 case 'D': /* debug flag */ 416 debug = ON; 417 break; 418 419 case 'I': /* ignore FATAL errors */ 420 ignore = ON; 421 break; 422 423 case '?': /* don't know the option */ 424 error(FATAL, ""); 425 break; 426 427 default: /* don't know what to do for ch */ 428 error(FATAL, "missing case for option %c", ch); 429 break; 430 } /* End switch */ 431 } /* End while */ 432 433 argc -= optind; /* get ready for non-option args */ 434 argv += optind; 435 436 } /* End of options */ 437 438 /*****************************************************************************/ 439 440 char *get_font(name) 441 442 char *name; /* name the user asked for */ 443 444 { 445 446 int i; /* for looking through fontmap[] */ 447 448 /* 449 * 450 * Called from options() to map a user's font name into a legal PostScript name. 451 * If the lookup fails *name is returned to the caller. That should let you choose 452 * any PostScript font. 453 * 454 */ 455 456 for ( i = 0; fontmap[i].name != NULL; i++ ) 457 if ( strcmp(name, fontmap[i].name) == 0 ) 458 return(fontmap[i].val); 459 460 return(name); 461 462 } /* End of get_font */ 463 464 /*****************************************************************************/ 465 466 setup() 467 468 { 469 470 /* 471 * 472 * Handles things that must be done after the options are read but before the 473 * input files are processed. 474 * 475 */ 476 477 writerequest(0, stdout); /* global requests eg. manual feed */ 478 setencoding(fontencoding); 479 fprintf(stdout, "setup\n"); 480 481 if ( formsperpage > 1 ) { 482 if ( cat(formfile) == FALSE ) 483 error(FATAL, "can't read %s", formfile); 484 fprintf(stdout, "%d setupforms\n", formsperpage); 485 } /* End if */ 486 487 fprintf(stdout, "%s", ENDSETUP); 488 489 } /* End of setup */ 490 491 /*****************************************************************************/ 492 493 arguments() 494 495 { 496 497 /* 498 * 499 * Makes sure all the non-option command line options are processed. If we get 500 * here and there aren't any arguments left, or if '-' is one of the input files 501 * we'll process stdin. 502 * 503 */ 504 505 if ( argc < 1 ) 506 conv(); 507 else 508 while ( argc > 0 ) { 509 if ( strcmp(*argv, "-") == 0 ) 510 fp_in = stdin; 511 else if ( (fp_in = fopen(*argv, "r")) == NULL ) 512 error(FATAL, "can't open %s", *argv); 513 conv(); 514 if ( fp_in != stdin ) 515 fclose(fp_in); 516 argc--; 517 argv++; 518 } /* End while */ 519 520 } /* End of arguments */ 521 522 /*****************************************************************************/ 523 524 done() 525 526 { 527 528 /* 529 * 530 * Finished with the last input file, so mark the end of the pages, make sure the 531 * last page is printed, and restore the initial environment. 532 * 533 */ 534 535 fprintf(stdout, "%s", TRAILER); 536 fprintf(stdout, "done\n"); 537 fprintf(stdout, "%s %s\n", DOCUMENTFONTS, fontname); 538 fprintf(stdout, "%s %d\n", PAGES, printed); 539 540 } /* End of done */ 541 542 /*****************************************************************************/ 543 544 account() 545 546 { 547 548 /* 549 * 550 * Writes an accounting record to *fp_acct, provided it's not NULL. 551 * 552 */ 553 554 if ( fp_acct != NULL ) 555 fprintf(fp_acct, " print %d\n copies %d\n", printed, copies); 556 557 } /* End of account */ 558 559 /*****************************************************************************/ 560 561 conv() 562 563 { 564 565 int ch; /* next input character */ 566 567 /* 568 * 569 * Controls the conversion of BGI files into PostScript. Not everything has been 570 * implemented, but what's been done should be good enough for our purposes. 571 * 572 */ 573 574 redirect(-1); /* get ready for the first page */ 575 bgimode = 0; 576 formfeed(); 577 578 while ( (ch = get_char()) != EOF ) { 579 switch ( ch ) { 580 case BRCHAR: /* rotated character mode */ 581 bgimode = ch; 582 text(90); 583 break; 584 585 case BCHAR: /* graphical character mode */ 586 bgimode = ch; 587 text(0); 588 break; 589 590 case BGRAPH: /* graphical master mode */ 591 bgimode = ch; 592 break; 593 594 case BSUB: /* subroutine definition */ 595 subr_def(); 596 break; 597 598 case BRET: /* end of subroutine */ 599 subr_end(); 600 break; 601 602 case BCALL: /* subroutine call */ 603 subr_call(); 604 break; 605 606 case BEND: /* end display - page */ 607 formfeed(); 608 break; 609 610 case BERASE: /* erase - shouldn't be used */ 611 error(FATAL, "BGI erase opcode obsolete"); 612 break; 613 614 case BREP: /* repeat */ 615 error(FATAL, "Repeat not implemented"); 616 repeat(); 617 break; 618 619 case BSETX: /* new x coordinate */ 620 hgoto(get_int(0)); 621 break; 622 623 case BSETY: /* new y coordinate */ 624 vgoto(get_int(0)); 625 break; 626 627 case BSETXY: /* new x and y coordinates */ 628 hgoto(get_int(0)); 629 vgoto(get_int(0)); 630 break; 631 632 case BINTEN: /* mark the current point */ 633 fprintf(fp_out, "%d %d p\n", hpos, vpos); 634 break; 635 636 case BVISX: /* visible x */ 637 vector(X_COORD, VISIBLE); 638 break; 639 640 case BINVISX: /* invisible x */ 641 vector(X_COORD, INVISIBLE); 642 break; 643 644 case BVISY: /* visible y */ 645 vector(Y_COORD, VISIBLE); 646 break; 647 648 case BINVISY: /* invisible y */ 649 vector(Y_COORD, INVISIBLE); 650 break; 651 652 case BVEC: /* arbitrary vector */ 653 vector(LONGVECTOR, VISIBLE); 654 break; 655 656 case BSVEC: /* short vector */ 657 vector(SHORTVECTOR, VISIBLE); 658 break; 659 660 case BRECT: /* draw rectangle */ 661 rectangle(OUTLINE); 662 break; 663 664 case BPOINT1: /* point plot 1 */ 665 case BPOINT: /* point plot 2 */ 666 point_plot(ch, get_char()); 667 break; 668 669 case BLINE: /* line plot */ 670 line_plot(); 671 break; 672 673 case BLTY: /* line type */ 674 fprintf(fp_out, "%s l\n", styles[get_data()]); 675 break; 676 677 case BARC: /* circular arc */ 678 arc(OUTLINE); 679 break; 680 681 case BFARC: /* filled circle */ 682 arc(FILL); 683 break; 684 685 case BFRECT: /* filled rectangle */ 686 rectangle(FILL); 687 break; 688 689 case BRASRECT: /* raster rectangle */ 690 error(FATAL, "Raster Rectangle not implemented"); 691 break; 692 693 case BCOL: /* select color */ 694 set_color(get_data()); 695 break; 696 697 case BFTRAPH: /* filled trapezoid */ 698 trapezoid(); 699 break; 700 701 case BPAT: /* pattern for area filling */ 702 pattern(); 703 break; 704 705 case BCSZ: /* change BGI character 'size' */ 706 setsize(get_data()); 707 break; 708 709 case BNOISE: /* from bad file format */ 710 break; 711 712 default: /* don't recognize the code */ 713 error(FATAL, "bad BGI command %d (0%o)", ch, ch); 714 break; 715 } /* End switch */ 716 717 if ( debug == ON ) 718 fprintf(stderr, "\n"); 719 } /* End while */ 720 721 formfeed(); /* in case BEND was missing */ 722 723 } /* End of conv */ 724 725 /*****************************************************************************/ 726 727 hgoto(n) 728 729 int n; /* new horizontal position */ 730 731 { 732 733 /* 734 * 735 * Sets the current BGI horizontal position to n. 736 * 737 */ 738 739 hpos = n; 740 741 } /* End of hgoto */ 742 743 /*****************************************************************************/ 744 745 vgoto(n) 746 747 int n; /* move to this vertical position */ 748 749 { 750 751 /* 752 * 753 * Sets the absolute vertical position to n. 754 * 755 */ 756 757 vpos = n; 758 759 } /* End of vgoto */ 760 761 /*****************************************************************************/ 762 763 setsize(n) 764 765 int n; /* BGI size - just a grid separation */ 766 767 { 768 769 /* 770 * 771 * Called when we're supposed to change the BGI character size to n. The BGI 772 * size is the grid separation in a 5 by 7 array in which characters are assumed 773 * to be built. 774 * 775 */ 776 777 bgisize = n; 778 linespace = LINESPACE(bgisize); 779 780 fprintf(fp_out, "%d f\n", bgisize); 781 782 if ( debug == ON ) 783 fprintf(stderr, "BGI size = %d\n", n); 784 785 } /* End of setsize */ 786 787 /*****************************************************************************/ 788 789 repeat() 790 791 { 792 793 int count; /* repeat this many times */ 794 int ch; /* next input character */ 795 796 /* 797 * 798 * Haven't implemented repeats, although it wouldn't be difficult. Apparently it's 799 * not used by any graphics packages that generate BGI. 800 * 801 */ 802 803 count = get_int(); /* get the repeat count */ 804 805 while ( (ch = get_char()) != EOF && ch != BENDR ) ; 806 807 } /* End of repeat */ 808 809 /*****************************************************************************/ 810 811 text(angle) 812 813 int angle; /* either 0 or 90 degrees */ 814 815 { 816 817 int ch; /* next character from file *fp_in */ 818 819 /* 820 * 821 * Called from conv() after we've entered one of the graphical character modes. 822 * Characters are read from the input file and printed until the next mode change 823 * opcode is found (or until EOF). angle will be 90 for rotated character mode 824 * and 0 otherwise. 825 * 826 * 827 */ 828 829 fprintf(fp_out, "%d %d %d(", angle, hpos, vpos); 830 831 while ( (ch = get_char()) != EOF ) { 832 if ( ch == BGRAPH || ch == BCHAR || ch == BRCHAR ) { 833 ungetc(ch, fp_in); 834 position--; 835 break; 836 } /* End if */ 837 838 switch ( ch ) { 839 case '\012': 840 vgoto(vpos - linespace); 841 842 case '\015': 843 hgoto(0); 844 fprintf(fp_out, ")t\n%d %d %d(", angle, hpos, vpos); 845 break; 846 847 case '(': 848 case ')': 849 case '\\': 850 putc('\\', fp_out); 851 852 default: 853 if ( isascii(ch) && isprint(ch) ) 854 putc(ch, fp_out); 855 else fprintf(fp_out, "\\%.3o", ch & 0377); 856 break; 857 } /* End switch */ 858 } /* End while */ 859 860 fprintf(fp_out, ") t\n"); 861 862 } /* End of text */ 863 864 /*****************************************************************************/ 865 866 formfeed() 867 868 { 869 870 int ch; /* repeat count for this page */ 871 872 /* 873 * 874 * Does whatever is needed to print the last page and get ready for the next one. 875 * It's called, from conv(), after a BEND code is processed. I'm ignoring the 876 * copy count that's expected to follow each page. 877 * 878 */ 879 880 if ( bgimode == BGRAPH && (ch = get_char()) != EOF && ! (ch & MSB) ) { 881 ungetc(ch, fp_in); 882 position--; 883 } /* End if */ 884 885 if ( fp_out == stdout ) /* count the last page */ 886 printed++; 887 888 fprintf(fp_out, "cleartomark\n"); 889 fprintf(fp_out, "showpage\n"); 890 fprintf(fp_out, "saveobj restore\n"); 891 fprintf(fp_out, "%s %d %d\n", ENDPAGE, page, printed); 892 893 while ( (ch = get_char()) == 0 ) ; /* skip any NULL characters */ 894 ungetc(ch, fp_in); 895 position--; 896 897 if ( ungetc(getc(fp_in), fp_in) == EOF ) 898 redirect(-1); 899 else redirect(++page); 900 901 fprintf(fp_out, "%s %d %d\n", PAGE, page, printed+1); 902 fprintf(fp_out, "/saveobj save def\n"); 903 fprintf(fp_out, "mark\n"); 904 writerequest(printed+1, fp_out); 905 fprintf(fp_out, "%d pagesetup\n", printed+1); 906 907 setsize(bgisize); 908 hpos = vpos = 0; 909 910 } /* End of formfeed */ 911 912 /*****************************************************************************/ 913 914 subr_def() 915 916 { 917 918 /* 919 * 920 * Starts a subroutine definition. All subroutines are defined as PostScript 921 * procedures that begin with the character S and end with the subroutine's id 922 * (a number between 0 and 63 - I guess). The primary, and perhaps only use of 923 * subroutines is in special color plots produced by several graphics libraries, 924 * and even there it's not all that common. I've also chosen not to worry about 925 * nested subroutine definitions - that would certainly be overkill! 926 * 927 * All subroutines set up their own (translated) coordinate system, do their work 928 * in that system, and restore things when they exit. To make everything work 929 * properly we save the current point (in shpos and svpos), set our position to 930 * (0, 0), and restore things at the end of the subroutine definition. That means 931 * hpos and vpos measure the relative displacement after a subroutine returns, and 932 * we save those values in the displacement[] array. The displacements are used 933 * (in subr_call()) to properly adjust our position after each subroutine call, 934 * and all subroutines are called with the current x and y coordinates on top of 935 * the stack. 936 * 937 */ 938 939 if ( in_subr == TRUE ) /* a nested subroutine definition?? */ 940 error(FATAL, "can't handle nested subroutine definitions"); 941 942 if ( (subr_id = get_data()) == EOF ) 943 error(FATAL, "missing subroutine identifier"); 944 945 if ( in_global == FALSE ) { /* just used to reduce file size some */ 946 fprintf(fp_out, "cleartomark\n"); 947 fprintf(fp_out, "saveobj restore\n"); 948 fprintf(fp_out, "%s", BEGINGLOBAL); 949 in_global = TRUE; 950 } /* End if */ 951 952 fprintf(fp_out, "/S%d {\n", subr_id); 953 fprintf(fp_out, "gsave translate\n"); 954 955 shpos = hpos; /* save our current position */ 956 svpos = vpos; 957 958 hgoto(0); /* start at the origin */ 959 vgoto(0); 960 961 in_subr = TRUE; /* in a subroutine definition */ 962 963 } /* End of subr_def */ 964 965 /*****************************************************************************/ 966 967 subr_end() 968 969 { 970 971 int ch; /* for looking at next opcode */ 972 973 /* 974 * 975 * Handles stuff needed at the end of each subroutine. Want to remember the change 976 * in horizontal and vertical positions for each subroutine so we can adjust our 977 * position after each call - just in case. The current position was set to (0, 0) 978 * before we started the subroutine definition, so when we get here hpos and vpos 979 * are the relative displacements after the subroutine is called. They're saved in 980 * the displacement[] array and used to adjust the current position when we return 981 * from a subroutine. 982 * 983 */ 984 985 if ( in_subr == FALSE ) /* not in a subroutine definition?? */ 986 error(FATAL, "subroutine end without corresponding start"); 987 988 fprintf(fp_out, "grestore\n"); 989 fprintf(fp_out, "} def\n"); 990 991 if ( in_global == TRUE && (ch = get_char()) != BSUB ) { 992 fprintf(fp_out, "%s", ENDGLOBAL); 993 fprintf(fp_out, "/saveobj save def\n"); 994 fprintf(fp_out, "mark\n"); 995 in_global = FALSE; 996 } /* End if */ 997 998 ungetc(ch, fp_in); /* put back the next opcode */ 999 1000 displacement[subr_id].dx = hpos; 1001 displacement[subr_id].dy = vpos; 1002 1003 hgoto(shpos); /* back to where we started */ 1004 vgoto(svpos); 1005 1006 in_subr = FALSE; /* done with the definition */ 1007 1008 } /* End of subr_end */ 1009 1010 /*****************************************************************************/ 1011 1012 subr_call() 1013 1014 { 1015 1016 int ch; /* next byte from *fp_in */ 1017 int id; /* subroutine id if ch wasn't an opcode */ 1018 1019 /* 1020 * 1021 * Handles subroutine calls. Everything that follows the BCALL opcode (up to the 1022 * next opcode) is taken as a subroutine identifier - thus the loop that generates 1023 * the subroutine calls. 1024 * 1025 */ 1026 1027 while ( (ch = get_char()) != EOF && (ch & MSB) ) { 1028 id = ch & DMASK; 1029 fprintf(fp_out, "%d %d S%d\n", hpos, vpos, id); 1030 1031 hgoto(hpos + displacement[id].dx); /* adjust our position */ 1032 vgoto(vpos + displacement[id].dy); 1033 } /* End while */ 1034 1035 ungetc(ch, fp_in); 1036 1037 } /* End of subr_call */ 1038 1039 /*****************************************************************************/ 1040 1041 vector(var, mode) 1042 1043 int var; /* coordinate that varies next? */ 1044 int mode; /* VISIBLE or INVISIBLE vectors */ 1045 1046 { 1047 1048 int ch; /* next character from *fp_in */ 1049 int x, y; /* line drawn to this point */ 1050 int count = 0; /* number of points so far */ 1051 1052 /* 1053 * 1054 * Handles plotting of all types of BGI vectors. If it's a manhattan vector var 1055 * specifies which coordinate will be changed by the next number in the input 1056 * file. 1057 * 1058 */ 1059 1060 x = hpos; /* set up the first point */ 1061 y = vpos; 1062 1063 while ( (ch = get_char()) != EOF && ch & MSB ) { 1064 if ( var == X_COORD ) /* next length is change in x */ 1065 x += get_int(ch); 1066 else if ( var == Y_COORD ) /* it's the change in y */ 1067 y += get_int(ch); 1068 else if ( var == LONGVECTOR ) { /* long vector */ 1069 x += get_int(ch); 1070 y += get_int(0); 1071 } else { /* must be a short vector */ 1072 x += ((ch & MSBMAG) * ((ch & SGNB) ? -1 : 1)); 1073 y += (((ch = get_data()) & MSBMAG) * ((ch & SGNB) ? -1 : 1)); 1074 } /* End else */ 1075 1076 if ( mode == VISIBLE ) { /* draw the line segment */ 1077 fprintf(fp_out, "%d %d\n", hpos - x, vpos - y); 1078 count++; 1079 } /* End if */ 1080 1081 hgoto(x); /* adjust the current BGI position */ 1082 vgoto(y); 1083 1084 if ( var == X_COORD ) /* vertical length comes next */ 1085 var = Y_COORD; 1086 else if ( var == Y_COORD ) /* change horizontal next */ 1087 var = X_COORD; 1088 } /* End while */ 1089 1090 if ( count > 0 ) 1091 fprintf(fp_out, "%d %d v\n", hpos, vpos); 1092 1093 ungetc(ch, fp_in); /* it wasn't part of the vector */ 1094 position--; 1095 1096 } /* End of vector */ 1097 1098 /*****************************************************************************/ 1099 1100 rectangle(mode) 1101 1102 int mode; /* FILL or OUTLINE the rectangle */ 1103 1104 { 1105 1106 int deltax; /* displacement for horizontal side */ 1107 int deltay; /* same but for vertical sides */ 1108 1109 /* 1110 * 1111 * Draws a rectangle and either outlines or fills it, depending on the value of 1112 * mode. Would be clearer, and perhaps better, if {stroke} or {fill} were put on 1113 * the stack instead of 0 or 1. R could then define the path and just do an exec 1114 * to fill or stroke it. 1115 * 1116 */ 1117 1118 deltax = get_int(0); /* get the height and width */ 1119 deltay = get_int(0); 1120 1121 if ( mode == OUTLINE ) 1122 fprintf(fp_out, "0 %d %d %d %d R\n", deltax, deltay, hpos, vpos); 1123 else fprintf(fp_out, "1 %d %d %d %d R\n", deltax, deltay, hpos, vpos); 1124 1125 } /* End of rectangle */ 1126 1127 /*****************************************************************************/ 1128 1129 trapezoid() 1130 1131 { 1132 1133 int kind; /* which sides are parallel */ 1134 int d[6]; /* true displacements - depends on kind */ 1135 1136 /* 1137 * 1138 * Handles filled trapeziods. A data byte of 0101 following the opcode means the 1139 * horizontal sides are parallel, 0102 means the vertical sides are parallel. 1140 * Filling is handled by eofill so we don't need to get things in the right order. 1141 * 1142 */ 1143 1144 kind = get_data(); 1145 1146 d[0] = get_int(0); 1147 d[1] = 0; 1148 d[2] = get_int(0); 1149 d[3] = get_int(0); 1150 d[4] = get_int(0); 1151 d[5] = 0; 1152 1153 if ( kind == 2 ) { /* parallel sides are vertical */ 1154 d[1] = d[0]; 1155 d[0] = 0; 1156 d[5] = d[4]; 1157 d[4] = 0; 1158 } /* End if */ 1159 1160 fprintf(fp_out, "%d %d %d %d %d %d %d %d T\n", d[4], d[5], d[2], d[3], d[0], d[1], hpos, vpos); 1161 1162 } /* End of trapezoid */ 1163 1164 /*****************************************************************************/ 1165 1166 point_plot(mode, ch) 1167 1168 int mode; /* plotting mode BPOINT or BPOINT1 */ 1169 int ch; /* will be placed at the points */ 1170 1171 { 1172 1173 int c; /* next character from input file */ 1174 int x, y; /* ch gets put here next */ 1175 int deltax; /* x increment for BPOINT1 mode */ 1176 1177 /* 1178 * 1179 * The two point plot modes are used to place a character at selected points. The 1180 * difference in the two modes, namely BPOINT and BPOINT1, is the way we get the 1181 * coordinates of the next point. In BPOINT1 the two bytes immediately following 1182 * ch select a constant horizontal change, while both coordinates are given for 1183 * all points in BPOINT mode. 1184 * 1185 */ 1186 1187 if ( mode == BPOINT1 ) { /* first integer is change in x */ 1188 deltax = get_int(0); 1189 x = hpos - deltax; 1190 } /* End if */ 1191 1192 while ( (c = get_char()) != EOF && (c & MSB) ) { 1193 if ( mode == BPOINT1 ) { /* only read y coordinate */ 1194 y = get_int(c); 1195 x += deltax; 1196 } else { /* get new x and y from input file */ 1197 x = get_int(c); 1198 y = get_int(0); 1199 } /* End else */ 1200 1201 hgoto(x); /* adjust BGI position */ 1202 vgoto(y); 1203 1204 fprintf(fp_out, "%d %d\n", hpos, vpos); 1205 } /* End while */ 1206 1207 putc('(', fp_out); 1208 1209 switch ( ch ) { 1210 case '(': 1211 case ')': 1212 case '\\': 1213 putc('\\', fp_out); 1214 1215 default: 1216 putc(ch, fp_out); 1217 } /* End switch */ 1218 1219 fprintf(fp_out, ")pp\n"); 1220 1221 ungetc(c, fp_in); /* it wasn't part of the point plot */ 1222 position--; 1223 1224 } /* End of point_plot */ 1225 1226 /*****************************************************************************/ 1227 1228 line_plot() 1229 1230 { 1231 1232 int c; /* next input character from fp_in */ 1233 int deltax; /* change in x coordinate */ 1234 int x0, y0; /* starting point for next segment */ 1235 int x1, y1; /* endpoint of the line */ 1236 int count = 0; /* number of points so far */ 1237 1238 /* 1239 * 1240 * Essentially the same format as BPOINT1, except that in this case we connect 1241 * pairs of points by line segments. 1242 * 1243 */ 1244 1245 deltax = get_int(0); /* again the change in x is first */ 1246 1247 x1 = hpos; /* so it works first time through */ 1248 y1 = get_int(0); 1249 1250 while ( (c = get_char()) != EOF && (c & MSB) ) { 1251 x0 = x1; /* line starts here */ 1252 y0 = y1; 1253 1254 x1 += deltax; /* and ends at this point */ 1255 y1 = get_int(c); 1256 1257 fprintf(fp_out, "%d %d\n", -deltax, y0 - y1); 1258 count++; 1259 } /* End while */ 1260 1261 hgoto(x1); /* adjust current BGI position */ 1262 vgoto(y1); 1263 1264 if ( count > 0 ) 1265 fprintf(fp_out, "%d %d v\n", hpos, vpos); 1266 1267 ungetc(c, fp_in); /* wasn't part of the line */ 1268 position--; 1269 1270 } /* End of line_plot */ 1271 1272 /*****************************************************************************/ 1273 1274 arc(mode) 1275 1276 int mode; /* FILL or OUTLINE the path */ 1277 1278 { 1279 1280 int dx1, dy1; /* displacements for first point */ 1281 int dx2, dy2; /* same for the second point */ 1282 int radius; /* of the arc */ 1283 int angle1, angle2; /* starting and ending angles */ 1284 1285 /* 1286 * 1287 * Called whenever we need to draw an arc. I'm ignoring filled slices for now. 1288 * 1289 */ 1290 1291 dx1 = get_int(0); /* displacements relative to center */ 1292 dy1 = get_int(0); 1293 dx2 = get_int(0); 1294 dy2 = get_int(0); 1295 1296 radius = get_int(0); /* and the radius */ 1297 1298 if ( radius == 0 ) /* nothing to do */ 1299 return; 1300 1301 angle1 = (atan2((double) dy1, (double) dx1) * 360) / (2 * PI) + .5; 1302 angle2 = (atan2((double) dy2, (double) dx2) * 360) / (2 * PI) + .5; 1303 1304 fprintf(fp_out, "%d %d %d %d %d arcn stroke\n", hpos, vpos, radius, angle1, angle2); 1305 1306 } /* End of arc */ 1307 1308 /*****************************************************************************/ 1309 1310 pattern() 1311 1312 { 1313 1314 double red = 0; /* color components */ 1315 double green = 0; 1316 double blue = 0; 1317 int kind; /* corse or fine pattern */ 1318 int val; /* next color data byte */ 1319 int i; /* loop index */ 1320 1321 /* 1322 * 1323 * Handles patterns by setting the current color based of the values assigned to 1324 * the next four data bytes. BGI supports two kinds of patterns (fine or coarse) 1325 * but I'm handling each in the same way - for now. In a fine pattern the four 1326 * data bytes assign a color to four individual pixels (upperleft first) while 1327 * in a coarse pattern the four colors are assigned to groups of four pixels, 1328 * for a total of 16. Again the first color goes to the group in the upper left 1329 * corner. The byte immediately following the BPAT opcode selects fine (040) or 1330 * coarse (041) patterns. The PostScript RGB color is assigned by averaging the 1331 * RED, GREEN, and BLUE components assigned to the four pixels (or groups of 1332 * pixels). Acceptable results, but there's no distinction between fine and 1333 * coarse patterns. 1334 * 1335 */ 1336 1337 if ( (kind = get_char()) == EOF ) 1338 error(FATAL, "bad pattern command"); 1339 1340 for ( i = 0; i < 4; i++ ) { 1341 val = get_data(); 1342 red += get_color(val, RED); 1343 green += get_color(val, GREEN); 1344 blue += get_color(val, BLUE); 1345 } /* End for */ 1346 1347 fprintf(fp_out, "%g %g %g c\n", red/4, green/4, blue/4); 1348 1349 } /* End of pattern */ 1350 1351 /*****************************************************************************/ 1352 1353 get_color(val, component) 1354 1355 int val; /* color data byte */ 1356 int component; /* RED, GREEN, or BLUE component */ 1357 1358 { 1359 1360 1361 int primary; /* color mixing mode - bits 2 to 4 */ 1362 int plane; /* primary color plane - bits 5 to 7 */ 1363 unsigned rgbcolor; /* PostScript expects an RGB triple */ 1364 1365 /* 1366 * 1367 * Picks the requested color component (RED, GREEN, or BLUE) from val and returns 1368 * the result to the caller. BGI works with Cyan, Yellow, and Magenta so the one's 1369 * complement stuff (following the exclusive or'ing) recovers the RED, BLUE, and 1370 * GREEN components that PostScript's setrgbcolor operator needs. The PostScript 1371 * interpreter in the ColorScript 100 has a setcmycolor operator, but it's not 1372 * generally available so I've decided to stick with setrgbcolor. 1373 * 1374 */ 1375 1376 primary = (val >> 3) & 07; 1377 plane = val & 07; 1378 rgbcolor = (~(primary ^ plane)) & 07; 1379 1380 if ( debug == ON ) 1381 fprintf(stderr, "val = %o, primary = %o, plane = %o, rgbcolor = %o\n", 1382 val, primary, plane, rgbcolor); 1383 1384 switch ( component ) { 1385 case RED: 1386 return(rgbcolor>>2); 1387 1388 case GREEN: 1389 return(rgbcolor&01); 1390 1391 case BLUE: 1392 return((rgbcolor>>1)&01); 1393 1394 default: 1395 error(FATAL, "unknown color component"); 1396 return(0); 1397 } /* End switch */ 1398 1399 } /* End of get_color */ 1400 1401 /*****************************************************************************/ 1402 1403 set_color(val) 1404 1405 int val; /* color data byte */ 1406 1407 { 1408 1409 /* 1410 * 1411 * Arranges to have the color set to the value requested in the BGI data byte val. 1412 * 1413 */ 1414 1415 fprintf(fp_out, "%d %d %d c\n", get_color(val, RED), get_color(val, GREEN), get_color(val, BLUE)); 1416 1417 } /* End of set_color */ 1418 1419 /*****************************************************************************/ 1420 1421 get_int(highbyte) 1422 1423 int highbyte; /* already read this byte */ 1424 1425 { 1426 1427 int lowbyte; /* this and highbyte make the int */ 1428 1429 /* 1430 * 1431 * Figures out the value on the integer (sign magnitude form) that's next in the 1432 * input file. If highbyte is nonzero we'll use it and the next byte to build the 1433 * integer, otherwise two bytes are read from fp_in. 1434 * 1435 */ 1436 1437 1438 if ( highbyte == 0 ) /* need to read the first byte */ 1439 highbyte = get_data(); 1440 1441 lowbyte = get_data(); /* always need the second byte */ 1442 1443 return(highbyte & SGNB ? -MAG(highbyte, lowbyte) : MAG(highbyte, lowbyte)); 1444 1445 } /* End of get_int */ 1446 1447 /*****************************************************************************/ 1448 1449 get_data() 1450 1451 { 1452 1453 int val; /* data value returned to caller */ 1454 1455 /* 1456 * 1457 * Called when we expect to find a single data character in the input file. The 1458 * data bit is turned off and the resulting value is returned to the caller. 1459 * 1460 */ 1461 1462 if ( (val = get_char()) == EOF || ! (val & MSB) ) 1463 error(FATAL, "missing data value"); 1464 1465 return(val & DMASK); 1466 1467 } /* End of get_data */ 1468 1469 /*****************************************************************************/ 1470 1471 get_char() 1472 1473 { 1474 1475 int ch; /* character we just read */ 1476 1477 /* 1478 * 1479 * Reads the next character from file *fp_in and returns the value to the caller. 1480 * This routine isn't really needed, but we may want to deal directly with some 1481 * screwball file formats so I thought it would probably be a good idea to isolate 1482 * all the input in one routine that could be easily changed. 1483 * 1484 */ 1485 1486 if ( (ch = getc(fp_in)) != EOF ) { 1487 position++; 1488 ch &= CHMASK; 1489 } /* End if */ 1490 1491 if ( debug == ON ) 1492 fprintf(stderr, "%o ", ch); 1493 1494 return(ch); 1495 1496 } /* End of get_char */ 1497 1498 /*****************************************************************************/ 1499 1500 redirect(pg) 1501 1502 int pg; /* next page we're printing */ 1503 1504 { 1505 1506 static FILE *fp_null = NULL; /* if output is turned off */ 1507 1508 /* 1509 * 1510 * If we're not supposed to print page pg, fp_out will be directed to /dev/null, 1511 * otherwise output goes to stdout. 1512 * 1513 */ 1514 1515 if ( pg >= 0 && in_olist(pg) == ON ) 1516 fp_out = stdout; 1517 else if ( (fp_out = fp_null) == NULL ) 1518 fp_out = fp_null = fopen("/dev/null", "w"); 1519 1520 } /* End of redirect */ 1521 1522 /*****************************************************************************/ 1523 1524