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