1 /* Last non-groff version: main.c 1.23 (Berkeley) 85/08/05
3 * Adapted to GNU troff by Daniel Senderowicz 99/12/29.
5 * Further refinements by Werner Lemberg 00/02/20.
8 * This file contains the main and file system dependent routines for
9 * processing gremlin files into troff input. The program watches input go
10 * by to standard output, only interpreting things between .GS and .GE
11 * lines. Default values (font, size, scale, thickness) may be overridden
12 * with a 'default' command and are further overridden by commands in the
15 * Inside the GS and GE, commands are accepted to reconfigure the picture.
16 * At most one command may reside on each line, and each command is followed
17 * by a parameter separated by white space. The commands are as follows,
18 * and may be abbreviated down to one character (with exception of 'scale'
19 * and 'stipple' down to "sc" and "st") and may be upper or lower case.
21 * default - Make all settings in the current
22 * .GS/.GE the global defaults. Height,
23 * width and file are NOT saved.
24 * 1, 2, 3, 4 - Set size 1, 2, 3, or 4 (followed by an
25 * integer point size).
26 * roman, italics, bold, special - Set gremlin's fonts to any other troff
27 * font (one or two characters).
28 * stipple, l - Use a stipple font for polygons. Arg
29 * is troff font name. No Default. Can
30 * use only one stipple font per picture.
31 * (See below for stipple font index.)
32 * scale, x - Scale is IN ADDITION to the global
33 * scale factor from the default.
34 * pointscale - Turn on scaling point sizes to match
35 * 'scale' commands. (Optional operand
36 * 'off' to turn it off.)
37 * narrow, medium, thick - Set widths of lines.
38 * file - Set the file name to read the gremlin
39 * picture from. If the file isn't in
40 * the current directory, the gremlin
42 * width, height - These two commands override any
43 * scaling factor that is in effect, and
44 * forces the picture to fit into either
45 * the height or width specified,
46 * whichever makes the picture smaller.
47 * The operand for these two commands is
48 * a floating-point number in units of
50 * l<nn> (integer <nn>) - Set association between stipple <nn>
51 * and a stipple 'character'. <nn> must
52 * be in the range 0 to NSTIPPLES (16)
53 * inclusive. The integer operand is an
54 * index in the stipple font selected.
55 * Valid cf (cifplot) indices are 1-32
56 * (although 24 is not defined), valid ug
57 * (unigrafix) indices are 1-14, and
58 * valid gs (gray scale) indices are
59 * 0-16. Nonetheless, any number between
60 * 0 and 255 is accepted since new
61 * stipple fonts may be added. An
62 * integer operand is required.
64 * Troff number registers used: g1 through g9. g1 is the width of the
65 * picture, and g2 is the height. g3, and g4, save information, g8 and g9
66 * are used for text processing and g5-g7 are reserved.
78 #include "searchpath.h"
79 #include "macropath.h"
85 extern "C" const char *Version_string;
87 /* database imports */
89 extern void HGPrintElt(ELT *element, int baseline);
91 extern ELT *DBRead(register FILE *file);
92 extern POINT *PTInit();
93 extern POINT *PTMakePoint(double x, double y, POINT **pplist);
95 #define INIT_FILE_SIZE 50 /* Initial size of array of files from cmd line. */
96 #define FILE_SIZE_INCR 50 /* Amount to increase array of files by. */
98 #define SUN_SCALEFACTOR 0.70
100 /* #define DEFSTIPPLE "gs" */
101 #define DEFSTIPPLE "cf"
103 * This grn implementation emits '.st' requests to control stipple effects,
104 * but groff does not (currently) support any such request.
106 * This hack disables the emission of such requests, without destroying the
107 * infrastructure necessary to support the feature in the future; to enable
108 * the emission of '.st' requests, at a future date when groff can support
109 * them, simply rewrite the following #define as:
111 * #define USE_ST_REQUEST stipple
113 * with accompanying comment: "emit '.st' requests as required".
115 #define USE_ST_REQUEST 0 /* never emit '.st' requests */
117 #define MAXINLINE 100 /* input line length */
119 #define SCREENtoINCH 0.02 /* scaling factor, screen to inches */
121 #define BIG 999999999999.0 /* unweildly large floating number */
124 /* static char sccsid[] = "@(#) (Berkeley) 8/5/85, 12/28/99"; */
126 int res; /* the printer's resolution goes here */
128 int dotshifter; /* for the length of dotted curves */
130 double linethickness; /* brush styles */
132 int lastx; /* point registers for printing elements */
134 int lastyline; /* A line's vertical position is NOT the */
135 /* same after that line is over, so for a */
136 /* line of drawing commands, vertical */
137 /* spacing is kept in lastyline */
139 /* These are the default fonts, sizes, line styles, */
140 /* and thicknesses. They can be modified from a */
141 /* 'default' command and are reset each time the */
142 /* start of a picture (.GS) is found. */
144 const char *deffont[] =
145 {"R", "I", "B", "S"};
148 /* #define BASE_THICKNESS 1.0 */
149 #define BASE_THICKNESS 0.15
150 double defthick[STYLES] =
158 /* int cf_stipple_index[NSTIPPLES + 1] = */
159 /* {0, 1, 3, 12, 14, 16, 19, 21, 23}; */
160 /* a logarithmic scale looks better than a linear one for the gray shades */
162 /* int other_stipple_index[NSTIPPLES + 1] = */
163 /* {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; */
165 int cf_stipple_index[NSTIPPLES + 1] =
166 {0, 18, 32, 56, 100, 178, 316, 562, 1000}; /* only 1-8 used */
167 int other_stipple_index[NSTIPPLES + 1] =
168 {0, 62, 125, 187, 250, 312, 375, 437, 500,
169 562, 625, 687, 750, 812, 875, 937, 1000};
171 /* int *defstipple_index = other_stipple_index; */
172 int *defstipple_index = cf_stipple_index;
175 {DOTTED, DOTDASHED, SOLID, DASHED, SOLID, SOLID};
176 double scale = 1.0; /* no scaling, default */
177 int defpoint = 0; /* flag for pointsize scaling */
178 char *defstipple = (char *) 0;
183 /* flag to controll filling of polygons */
190 double thick[STYLES]; /* thicknesses set by defaults, then by */
192 char *tfont[FONTS]; /* fonts originally set to deffont values, */
194 int tsize[SIZES]; /* optionally changed by commands inside */
196 int stipple_index[NSTIPPLES + 1]; /* stipple font file indices */
199 double xscale; /* scaling factor from individual pictures */
200 double troffscale; /* scaling factor at output time */
202 double width; /* user-request maximum width for picture */
204 double height; /* user-request height */
205 int pointscale; /* flag for pointsize scaling */
206 int setdefault; /* flag for a .GS/.GE to remember all */
208 int sflag; /* -s flag: sort order (do polyfill first) */
210 double toppoint; /* remember the picture */
211 double bottompoint; /* bounds in these variables */
215 int ytop; /* these are integer versions of the above */
216 int ybottom; /* so not to convert each time they're used */
220 int linenum = 0; /* line number of input file */
221 char inputline[MAXINLINE]; /* spot to filter through the file */
222 char *c1 = inputline; /* c1, c2, and c3 will be used to */
223 char *c2 = inputline + 1; /* hunt for lines that begin with */
224 char *c3 = inputline + 2; /* '.GS' by looking individually */
225 char *c4 = inputline + 3; /* needed for compatibility mode */
226 char GScommand[MAXINLINE]; /* put user's '.GS' command line here */
227 char gremlinfile[MAXINLINE]; /* filename to use for a picture */
228 int SUNFILE = FALSE; /* TRUE if SUN gremlin file */
229 int compatibility_flag = FALSE; /* TRUE if in compatibility mode */
233 int doinput(FILE *fp);
234 void conv(register FILE *fp, int baseline);
236 int has_polygon(register ELT *elist);
237 void interpret(char *line);
244 "usage: %s [ -vCs ] [ -M dir ] [ -F dir ] [ -T dev ] [ file ]\n",
249 /* Add a new file entry in the array, expanding array if needs be. */
252 add_file(char **file,
257 if (*count >= *cur_size) {
258 *cur_size += FILE_SIZE_INCR;
259 file = (char **) realloc((char **) file, *cur_size * sizeof(char *));
261 fatal("unable to extend file array");
264 file[*count] = new_file;
271 /*----------------------------------------------------------------------------*
272 | Routine: main (argument_count, argument_pointer)
274 | Results: Parses the command line, accumulating input file names, then
275 | reads the inputs, passing it directly to output until a '.GS'
276 | line is read. Main then passes control to 'conv' to do the
277 | gremlin file conversions.
278 *----------------------------------------------------------------------------*/
284 setlocale(LC_NUMERIC, "C");
285 program_name = argv[0];
291 int file_cur_size = INIT_FILE_SIZE;
292 char *operand(int *argcp, char ***argvp);
294 if ((file = (char **) malloc(file_cur_size * sizeof(char *))) == NULL) {
295 fatal("unable to create file array");
299 file = add_file(file, *argv, &gfil, &file_cur_size);
301 switch (c = (*argv)[1]) {
304 file = add_file(file, NULL, &gfil, &file_cur_size);
307 case 'C': /* compatibility mode */
308 compatibility_flag = TRUE;
311 case 'F': /* font path to find DESC */
312 font::command_line_font_dir(operand(&argc, &argv));
315 case 'T': /* final output typesetter name */
316 device = operand(&argc, &argv);
319 case 'M': /* set library directory */
320 macro_path.command_line_dir(operand(&argc, &argv));
323 case 's': /* preserve order of elements */
328 if (strcmp(*argv,"--version")==0) {
330 printf("GNU grn (groff) version %s\n", Version_string);
334 if (strcmp(*argv,"--help")==0) {
342 error("unknown switch: %1", c);
348 getres(); /* set the resolution for an output device */
350 if (gfil == 0) { /* no filename, use standard input */
355 for (k = 0; k < gfil; k++) {
356 if (file[k] != NULL) {
357 if ((fp = fopen(file[k], "r")) == NULL)
358 fatal("can't open %1", file[k]);
362 while (doinput(fp)) {
363 if (*c1 == '.' && *c2 == 'G' && *c3 == 'S') {
364 if (compatibility_flag ||
365 *c4 == '\n' || *c4 == ' ' || *c4 == '\0')
368 fputs(inputline, stdout);
370 fputs(inputline, stdout);
378 /*----------------------------------------------------------------------------*
379 | Routine: char * operand (& argc, & argv)
381 | Results: Returns address of the operand given with a command-line
382 | option. It uses either '-Xoperand' or '-X operand', whichever
383 | is present. The program is terminated if no option is
386 | Side Efct: argc and argv are updated as necessary.
387 *----------------------------------------------------------------------------*/
394 return (**argvp + 2); /* operand immediately follows */
395 if ((--*argcp) <= 0) { /* no operand */
396 error("command-line option operand missing.");
399 return (*(++(*argvp))); /* operand is next word */
403 /*----------------------------------------------------------------------------*
406 | Results: Sets 'res' to the resolution of the output device.
407 *----------------------------------------------------------------------------*/
414 if (!font::load_desc())
415 fatal("sorry, I can't continue");
419 /* Correct the brush thicknesses based on res */
421 defthick[0] = res >> 8;
422 defthick[1] = res >> 8;
423 defthick[2] = res >> 4;
424 defthick[3] = res >> 8;
425 defthick[4] = res >> 8;
426 defthick[5] = res >> 6;
429 linepiece = res >> 9;
430 for (dotshifter = 0; linepiece; dotshifter++)
431 linepiece = linepiece >> 1;
435 /*----------------------------------------------------------------------------*
436 | Routine: int doinput (file_pointer)
438 | Results: A line of input is read into 'inputline'.
440 | Side Efct: "linenum" is incremented.
442 | Bugs: Lines longer than MAXINLINE are NOT checked, except for
443 | updating 'linenum'.
444 *----------------------------------------------------------------------------*/
449 if (fgets(inputline, MAXINLINE, fp) == NULL)
451 if (strchr(inputline, '\n')) /* ++ only if it's a complete line */
457 /*----------------------------------------------------------------------------*
458 | Routine: initpic ( )
460 | Results: Sets all parameters to the normal defaults, possibly
461 | overridden by a setdefault command. Initialize the picture
462 | variables, and output the startup commands to troff to begin
464 *----------------------------------------------------------------------------*/
471 for (i = 0; i < STYLES; i++) { /* line thickness defaults */
472 thick[i] = defthick[i];
474 for (i = 0; i < FONTS; i++) { /* font name defaults */
475 tfont[i] = (char *)deffont[i];
477 for (i = 0; i < SIZES; i++) { /* font size defaults */
478 tsize[i] = defsize[i];
480 for (i = 0; i <= NSTIPPLES; i++) { /* stipple font file default indices */
481 stipple_index[i] = defstipple_index[i];
483 stipple = defstipple;
485 gremlinfile[0] = 0; /* filename is 'null' */
486 setdefault = 0; /* this is not the default settings (yet) */
488 toppoint = BIG; /* set the picture bounds out */
489 bottompoint = -BIG; /* of range so they'll be set */
490 leftpoint = BIG; /* by 'savebounds' on input */
493 pointscale = defpoint; /* flag for scaling point sizes default */
494 xscale = scale; /* default scale of individual pictures */
495 width = 0.0; /* size specifications input by user */
498 linethickness = DEFTHICK; /* brush styles */
503 /*----------------------------------------------------------------------------*
504 | Routine: conv (file_pointer, starting_line)
506 | Results: At this point, we just passed a '.GS' line in the input
507 | file. conv reads the input and calls 'interpret' to process
508 | commands, gathering up information until a '.GE' line is
509 | found. It then calls 'HGPrint' to do the translation of the
510 | gremlin file to troff commands.
511 *----------------------------------------------------------------------------*/
514 conv(register FILE *fp,
517 register FILE *gfp = NULL; /* input file pointer */
518 register int done = 0; /* flag to remember if finished */
519 register ELT *e; /* current element pointer */
520 ELT *PICTURE; /* whole picture data base pointer */
521 double temp; /* temporary calculating area */
522 /* POINT ptr; */ /* coordinates of a point to pass to 'mov' */
524 int flyback; /* flag 'want to end up at the top of the */
526 int compat; /* test character after .GE or .GF */
529 initpic(); /* set defaults, ranges, etc. */
530 strcpy(GScommand, inputline); /* save '.GS' line for later */
533 done = !doinput(fp); /* test for EOF */
534 flyback = (*c3 == 'F'); /* and .GE or .GF */
535 compat = (compatibility_flag ||
536 *c4 == '\n' || *c4 == ' ' || *c4 == '\0');
537 done |= (*c1 == '.' && *c2 == 'G' && (*c3 == 'E' || flyback) &&
544 if (!gremlinfile[0]) {
546 error("at line %1: no picture filename.\n", baseline);
550 gfp = macro_path.open_file(gremlinfile, &path);
553 PICTURE = DBRead(gfp); /* read picture file */
556 if (DBNullelt(PICTURE))
557 return; /* If a request is made to make the */
558 /* picture fit into a specific area, */
559 /* set the scale to do that. */
561 if (stipple == (char *) NULL) /* if user forgot stipple */
562 if (has_polygon(PICTURE)) /* and picture has a polygon */
563 stipple = (char *)DEFSTIPPLE; /* then set the default */
565 if ((temp = bottompoint - toppoint) < 0.1)
567 temp = (height != 0.0) ? height / (temp * SCREENtoINCH) : BIG;
568 if ((troffscale = rightpoint - leftpoint) < 0.1)
570 troffscale = (width != 0.0) ?
571 width / (troffscale * SCREENtoINCH) : BIG;
572 if (temp == BIG && troffscale == BIG)
575 if (temp < troffscale)
577 } /* here, troffscale is the */
578 /* picture's scaling factor */
580 register int i; /* do pointscaling here, when */
581 /* scale is known, before output */
582 for (i = 0; i < SIZES; i++)
583 tsize[i] = (int) (troffscale * (double) tsize[i] + 0.5);
586 /* change to device units */
587 troffscale *= SCREENtoINCH * res; /* from screen units */
589 ytop = (int) (toppoint * troffscale); /* calculate integer */
590 ybottom = (int) (bottompoint * troffscale); /* versions of the */
591 xleft = (int) (leftpoint * troffscale); /* picture limits */
592 xright = (int) (rightpoint * troffscale);
594 /* save stuff in number registers, */
595 /* register g1 = picture width and */
596 /* register g2 = picture height, */
597 /* set vertical spacing, no fill, */
598 /* and break (to make sure picture */
599 /* starts on left), and put out the */
600 /* user's '.GS' line. */
609 xright - xleft, ybottom - ytop, GScommand);
611 if (USE_ST_REQUEST) /* stipple requested for this picture */
612 printf(".st %s\n", stipple);
613 lastx = xleft; /* note where we are (upper left */
614 lastyline = lasty = ytop; /* corner of the picture) */
616 /* Just dump everything in the order it appears.
618 * If -s command-line option, traverse picture twice: First time,
619 * print only the interiors of filled polygons (as borderless
620 * polygons). Second time, print the outline as series of line
621 * segments. This way, postprocessors that overwrite rather than
622 * merge picture elements (such as Postscript) can still have text and
623 * graphics on a shaded background.
626 if (!sflag) { /* changing the default for filled polygons */
629 while (!DBNullelt(e)) {
631 if (e->type == POLYGON)
632 HGPrintElt(e, baseline);
635 lastyline = lasty = ytop;
641 /* polyfill = !sflag ? BOTH : OUTLINE; */
642 polyfill = sflag ? BOTH : OUTLINE; /* changing the default */
643 while (!DBNullelt(e)) {
645 HGPrintElt(e, baseline);
648 lastyline = lasty = ytop;
652 /* decide where to end picture */
654 /* I changed everything here. I always use the combination .mk and */
655 /* .rt so once finished I just space down the heigth of the picture */
657 if (flyback) { /* end picture at upper left */
658 /* ptr.x = leftpoint;
660 } else { /* end picture at lower left */
661 /* ptr.x = leftpoint;
662 ptr.y = bottompoint; */
663 printf(".sp \\n(g2u\n");
666 /* tmove(&ptr); */ /* restore default line parameters */
668 /* restore everything to the way it was before the .GS, then put */
669 /* out the '.GE' line from user */
671 /* printf("\\D't %du'\\D's %du'\n", DEFTHICK, DEFSTYLE); */
672 /* groff doesn't understand the \Ds command */
674 printf("\\D't %du'\n", DEFTHICK);
675 if (flyback) /* make sure we end up at top of */
676 printf(".sp -1\n"); /* picture if 'flying back' */
677 if (USE_ST_REQUEST) /* restore stipple to previous */
684 interpret(inputline); /* take commands from the input file */
689 /*----------------------------------------------------------------------------*
690 | Routine: savestate ( )
692 | Results: all the current scaling / font size / font name / thickness
693 | / pointscale settings are saved to be the defaults. Scaled
694 | point sizes are NOT saved. The scaling is done each time a
695 | new picture is started.
697 | Side Efct: scale, and def* are modified.
698 *----------------------------------------------------------------------------*/
705 for (i = 0; i < STYLES; i++) /* line thickness defaults */
706 defthick[i] = thick[i];
707 for (i = 0; i < FONTS; i++) /* font name defaults */
708 deffont[i] = tfont[i];
709 for (i = 0; i < SIZES; i++) /* font size defaults */
710 defsize[i] = tsize[i];
711 for (i = 0; i <= NSTIPPLES; i++) /* stipple font file default indices */
712 defstipple_index[i] = stipple_index[i];
714 defstipple = stipple; /* if stipple has been set, it's remembered */
715 scale *= xscale; /* default scale of individual pictures */
716 defpoint = pointscale; /* flag for scaling pointsizes from x factors */
720 /*----------------------------------------------------------------------------*
721 | Routine: savebounds (x_coordinate, y_coordinate)
723 | Results: Keeps track of the maximum and minimum extent of a picture
724 | in the global variables: left-, right-, top- and
725 | bottompoint. 'savebounds' assumes that the points have been
726 | oriented to the correct direction. No scaling has taken
728 *----------------------------------------------------------------------------*/
745 /*----------------------------------------------------------------------------*
746 | Routine: interpret (character_string)
748 | Results: Commands are taken from the input string and performed.
749 | Commands are separated by the endofline, and are of the
753 | where string1 is the command and string2 is the argument.
755 | Side Efct: Font and size strings, plus the gremlin file name and the
756 | width and height variables are set by this routine.
757 *----------------------------------------------------------------------------*/
760 interpret(char *line)
762 char str1[MAXINLINE];
763 char str2[MAXINLINE];
769 sscanf(line, "%80s%80s", &str1[0], &str2[0]);
770 for (chr = &str1[0]; *chr; chr++) /* convert command to */
772 *chr = tolower(*chr); /* lower case */
777 case '2': /* font sizes */
781 if (i > 0 && i < 1000)
782 tsize[str1[0] - '1'] = i;
784 error("bad font size value at line %1", linenum);
787 case 'r': /* roman */
790 tfont[0] = (char *) malloc(strlen(str2) + 1);
791 strcpy(tfont[0], str2);
794 case 'i': /* italics */
797 tfont[1] = (char *) malloc(strlen(str2) + 1);
798 strcpy(tfont[1], str2);
804 tfont[2] = (char *) malloc(strlen(str2) + 1);
805 strcpy(tfont[2], str2);
808 case 's': /* special */
810 goto scalecommand; /* or scale */
814 error("no fontname specified in line %1", linenum);
818 goto stipplecommand; /* or stipple */
820 tfont[3] = (char *) malloc(strlen(str2) + 1);
821 strcpy(tfont[3], str2);
825 if (isdigit(str1[1])) { /* set stipple index */
826 int idx = atoi(str1 + 1), val;
828 if (idx < 0 || idx > NSTIPPLES) {
829 error("bad stipple number %1 at line %2", idx, linenum);
832 if (!defstipple_index)
833 defstipple_index = other_stipple_index;
835 if (val >= 0 && val < 256)
836 stipple_index[idx] = val;
838 error("bad stipple index value at line %1", linenum);
842 stipplecommand: /* set stipple name */
843 stipple = (char *) malloc(strlen(str2) + 1);
844 strcpy(stipple, str2);
845 /* if it's a 'known' font (currently only 'cf'), set indices */
846 if (strcmp(stipple, "cf") == 0)
847 defstipple_index = cf_stipple_index;
849 defstipple_index = other_stipple_index;
850 for (i = 0; i <= NSTIPPLES; i++)
851 stipple_index[i] = defstipple_index[i];
854 case 'a': /* text adjust */
870 error("bad adjust command at line %1", linenum);
875 case 't': /* thick */
876 thick[2] = defthick[0] * atof(str2);
879 case 'm': /* medium */
880 thick[5] = defthick[0] * atof(str2);
883 case 'n': /* narrow */
884 thick[0] = thick[1] = thick[3] = thick[4] =
885 defthick[0] * atof(str2);
889 scalecommand: /* scale */
894 error("invalid scale value on line %1", linenum);
898 strcpy(gremlinfile, str2);
901 case 'w': /* width */
907 case 'h': /* height */
913 case 'd': /* defaults */
917 case 'p': /* pointscale */
918 if (strcmp("off", str2))
925 error("unknown command '%1' on line %2", str1, linenum);
933 * return TRUE if picture contains a polygon
938 has_polygon(register ELT *elist)
940 while (!DBNullelt(elist)) {
941 if (elist->type == POLYGON)
943 elist = DBNextElt(elist);