/* * * dpost - troff post-processor for PostScript printers. * * A program that translates output generated by the device independent troff * into PostScript. Much was borrowed from dimpress and dps (formally dlzw), * and even though the code has changed, credit has to be given to Richard * Flood for his early work on the PostScript driver. * * The big change is in the font table routines. The old binary format and * makedev are gone. dpost and troff now read ASCII tables, and both skip * unrecognized entries in the ASCII tables. That means problems, like where * to put the real name of the PostScript font, have disappeared. The added * flexibility means some overhead translating the ASCII tables, but the * overhead isn't too bad. * * dpost can also now calculate a reasonably tight BoundingBox, which helps * picture inclusion. The calculations, by default, are disabled. Couldn't * justify the overhead for a comment, particularly one that's only needed * occasionally. Use the -B option to get the comment. * * Output produced by dpost is still nonconforming. Definitions made in pages * and exported to the job's global environment are the primary problem. It's * an efficient approach, but means pages are not independent. Violations are * bracketed by %%BeginGlobal and %%EndGlobal comments and can be pulled into * the prologue by utility programs (like postreverse) that recognize the new * comments. * * The program handles files formatted for any device, although the best and * most efficient output is generated when the font and description files * match PostScript's resident fonts. Emulation is relatively expensive, and * can produce output files that are more than twice the size of the input * files. * * Several different methods can be used to encode lines of text. What's done * depends on the value assigned to encoding. Print time should decrease as * encoding increases (up to MAXENCODING). Setting encoding to 3 (or higher) * is not normally recommended. It's fast and produces very compact output, * but rounding errors in troff's width tables can accumulate and lead to a * ragged right margin. encoding can be changed on the command line using the * -e option. * * PostScript fonts don't support all of troff's characters. Some are built * by special PostScript procedures in directory *fontdir/devpost/charlib. * The charlib approach is not meant to replace user defined fonts. It was * a quick implementation designed to handle characters that aren't used * often - charlib should not be overused! The charlib lookup is triggered * when a character in a font table is assigned a code less than 32. * * Most defaults are set in the prologue, but can be changed by options. The * -P option passes arbitrary PostScript into the setup section of the output * file. It can be used to set (or change) values that can't be accessed by * other options. For example, * * dpost -P'/useclippath false def' file > file.ps * * defines useclippath to be false. Everything passed through using the -P * (-C to copy a file) options become part of the job's global environment. * Definitions override defaults in the prologue. * * dpost expects to find the following procedures in the prologue: * * setup * * mark ... setup - * * Initialization procedure mainly responsible for setting up an * appropriate coordinate system. * * pagesetup * * page pagesetup - * * Called at the start of each page, immediately after the page * level save. Argument is the current page number. * * setdecoding * * num setdecoding - * * Select the decoding procedure used to print text strings encoded * by dpost. num is whatever has been assigned to encoding. * * f * * size font f - * * Set the font and size used for character imaging. The font name * argument is (normally) the name troff used. Mapping to the real * PostScript font name is made using the fontname field in the * ASCII width tables. * * m * * x y m - * * Move to point (x, y). Not used for positioning words in text * strings. * * t * * mark text t mark * * Everything on the stack (up to the mark) is treated as a line * of text to be decoded and printed. What's on the stack depends * on encoding. * * w * * string x y w - * * Print a single word starting at position (x, y). Only used in * the more complicated encoding schemes, like the ones based on * widthshow. * * done * * Make sure the last page prints. Always called, but only needed * when printing more than one page on each sheet of paper. * * output language from troff: * all numbers are character strings * * sn size in points * fn font as number from 1-n * cx ascii character x * Cxyz funny char xyz. terminated by white space * Hn go to absolute horizontal position n * Vn go to absolute vertical position n (down is positive) * hn go n units horizontally (relative) * vn ditto vertically * nnc move right nn, then print c (exactly 2 digits!) * (this wart is an optimization that shrinks output file size * about 35% and run-time about 15% while preserving ascii-ness) * Dt ...\n draw operation 't': * Dl x y line from here by x,y * Dc d circle of diameter d with left side here * De x y ellipse of axes x,y with left side here * Da x1 y1 x2 y2 arc counter-clockwise from current point (x, y) to * (x + x1 + x2, y + y1 + y2) * D~ x y x y ... wiggly line by x,y then x,y ... * nb a end of line (information only -- no action needed) * b = space before line, a = after * p new page begins -- set v to 0 * #...\n comment * x ...\n device control functions: * x i init * x T s name of device is s * x r n h v resolution is n/inch * h = min horizontal motion, v = min vert * x p pause (can restart) * x s stop -- done forever * x t generate trailer * x f n s font position n contains font s * x H n set character height to n * x S n set slant to N * * Subcommands like "i" are often spelled out like "init". * */ #include <stdio.h> #include <fcntl.h> #include <signal.h> #include <math.h> #include <ctype.h> #include <time.h> #include "comments.h" /* structuring comments */ #include "gen.h" /* general purpose definitions */ #include "path.h" /* prologue and a few other files */ #include "ext.h" /* external variable declarations */ #include "font.h" /* font table definitions */ #include "dpost.h" /* a few definitions just used here */ #include "motion.h" /* positioning macros */ char *prologue = DPOST; /* the PostScript prologue */ char *colorfile = COLOR; /* color support */ char *drawfile = DRAW; /* drawing routines */ char *formfile = FORMFILE; /* multiple pages on each sheet */ char *baselinefile = BASELINE; /* for text along curved baseline */ char *fontdir = FONTDIR; /* font table directories */ char *hostfontdir = NULL; /* host resident font directory */ char *realdev = DEVNAME; /* use these width tables */ char devname[20] = ""; /* job formatted for this device */ int emulate = FALSE; /* TRUE if devname != realdev */ Fontmap fontmap[] = FONTMAP; /* font translation table - emulation */ int copies = 1; /* copies of each sheet */ int printed = 0; /* pages processed and printed */ int formsperpage = 1; /* pages on each sheet of paper */ int picflag = ON; /* enable/disable picture inclusion */ int encoding = DFLTENCODING; /* how text is translated to PostScript */ int realencoding = DFLTENCODING; /* where we started */ int maxencoding = MAXENCODING; /* max that users can select */ int landscape = FALSE; /* for BoundingBox calculations only */ double magnification = 1.0; double xoffset = 0.0; double yoffset = 0.0; int smnt; /* special fonts start here */ int devres; /* device resolution */ int unitwidth; /* and unitwidth - from DESC file */ char downloaded[MAXCH+32]; /* status of charlib characters */ int nfonts = 0; /* number of font positions */ int size = 10; /* current point size */ int font = 0; /* and font position */ int hpos = 0; /* where troff wants to be */ int vpos = 0; float lastw = 0; /* width of the last input character */ int lastc = 0; /* its name (or index) - for charlib() */ int fontheight = 0; /* points from x H ... */ int fontslant = 0; /* angle from x S ... */ int res; /* resolution assumed in input file */ float widthfac = 1.0; /* for emulation = res/devres */ int lastsize = -1; /* for tracking printer's current size */ int lastfont = -1; /* current font */ float lastx = -1; /* and current position */ int lasty = -1; int lastend; /* where last character on this line was */ int seenpage = FALSE; /* expect fonts are now all mounted */ int gotspecial = FALSE; /* append special fonts - emulation */ float pointslop = SLOP; /* horizontal error in points */ int slop; /* and machine units */ int rvslop; /* to extend box in reverse video mode */ int textcount = 0; /* strings accumulated so far */ int stringstart = 0; /* where the next one starts */ int spacecount = 0; /* spaces in current string */ Line line[MAXSTACK+3]; /* data about words accumulated so far */ char strings[STRINGSPACE]; /* strings temporarily saved here */ char *strptr; /* next free slot in strings[] */ FILE *tf = NULL; /* most output goes here */ FILE *fp_acct = NULL; /* accounting file */ char *optnames = "a:c:e:m:n:o:p:tw:x:y:A:BC:J:F:H:L:OP:R:S:T:DI"; extern int gotcolor; /* read *colorfile when TRUE */ extern Font fonts[]; /* data about every font we see */ extern Font *mount[]; /* troff mounts fonts here */ /*****************************************************************************/ main(agc, agv) int agc; char *agv[]; { /* * * Translates output from troff into PostScript. Input files must be formatted * for the same device. Each input file begins on a new page. * */ argc = agc; /* global so everyone can use them */ argv = agv; prog_name = argv[0]; /* for error messages */ init_signals(); /* interrupt handling */ header(); /* structuring comments */ options(); /* command line options */ arguments(); /* translate the input files */ done(); /* add trailing comments etc. */ account(); /* job accounting data */ exit(x_stat); } /* End of main */ /*****************************************************************************/ init_signals() { int interrupt(); /* * * Make sure we handle interrupts. * */ if ( signal(SIGINT, interrupt) == SIG_IGN ) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGHUP, SIG_IGN); } else { signal(SIGHUP, interrupt); signal(SIGQUIT, interrupt); } /* End else */ signal(SIGTERM, interrupt); } /* End of init_signals */ /*****************************************************************************/ header() { int ch; int old_optind = optind; /* * * Scan the option list for things needed now (e.g. prologue file), but could * be changed from defaults. An attempt to follow to Adobe's 2.0 structuring * conventions. * */ while ( (ch = getopt(argc, argv, optnames)) != EOF ) if ( ch == 'L' ) setpaths(optarg); else if ( ch == 'B' ) dobbox = TRUE; else if ( ch == '?' ) error(FATAL, ""); optind = old_optind; /* restored for options() */ fprintf(stdout, "%s", NONCONFORMING); fprintf(stdout, "%s %s\n", VERSION, PROGRAMVERSION); fprintf(stdout, "%s %s\n", DOCUMENTFONTS, ATEND); fprintf(stdout, "%s %s\n", PAGES, ATEND); if ( dobbox == TRUE ) fprintf(stdout, "%s %s\n", BOUNDINGBOX, ATEND); fprintf(stdout, "%s", ENDCOMMENTS); if ( cat(prologue) == FALSE ) error(FATAL, "can't read %s", prologue); if ( DOROUND ) cat(ROUNDPAGE); fprintf(stdout, "%s", ENDPROLOG); fprintf(stdout, "%s", BEGINSETUP); fprintf(stdout, "mark\n"); } /* End of header */ /*****************************************************************************/ options() { int ch; extern char *optarg; extern int optind; /* * * Command line options - there are too many! * */ while ( (ch = getopt(argc, argv, optnames)) != EOF ) { switch ( ch ) { case 'a': /* aspect ratio */ fprintf(stdout, "/aspectratio %s def\n", optarg); break; case 'c': /* number of copies */ copies = atoi(optarg); fprintf(stdout, "/#copies %s store\n", optarg); break; case 'e': /* select the encoding scheme */ if ( (encoding = atoi(optarg)) < 0 || encoding > MAXENCODING ) encoding = DFLTENCODING; realencoding = encoding; break; case 'm': /* magnification */ magnification = atof(optarg); fprintf(stdout, "/magnification %s def\n", optarg); break; case 'n': /* forms per page */ formsperpage = atoi(optarg); fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg); fprintf(stdout, "/formsperpage %s def\n", optarg); break; case 'o': /* output page list */ out_list(optarg); break; case 'p': /* landscape or portrait mode */ landscape = (*optarg == 'l') ? TRUE : FALSE; if ( landscape == TRUE ) fprintf(stdout, "/landscape true def\n"); else fprintf(stdout, "/landscape false def\n"); break; case 't': /* compatibility */ break; case 'w': /* line width - for drawing */ fprintf(stdout, "/linewidth %s def\n", optarg); break; case 'x': /* shift horizontally */ xoffset = atof(optarg); fprintf(stdout, "/xoffset %s def\n", optarg); break; case 'y': /* shift vertically */ yoffset = atof(optarg); fprintf(stdout, "/yoffset %s def\n", optarg); break; case 'A': /* job accounting */ case 'J': if ( (fp_acct = fopen(optarg, "a")) == NULL ) error(FATAL, "can't open accounting file %s", optarg); break; case 'B': /* enable BoundingBox calculations */ dobbox = TRUE; fprintf(stdout, "/rotation 1 def\n"); fprintf(stdout, "/gotpagebbox true def\n"); break; case 'C': /* copy file to output */ if ( cat(optarg) == FALSE ) error(FATAL, "can't read %s", optarg); break; case 'F': /* font table directory */ fontdir = optarg; break; case 'H': /* host resident font directory */ hostfontdir = optarg; break; case 'L': /* prologue file */ setpaths(optarg); /* already been done in header() */ break; case 'O': /* disable picture inclusion */ picflag = OFF; break; case 'P': /* copy string to output */ fprintf(stdout, "%s\n", optarg); break; case 'R': /* global or page level request */ saverequest(optarg); break; case 'S': /* horizontal position error */ if ( (pointslop = atof(optarg)) < 0 ) pointslop = 0; break; case 'T': /* target printer */ realdev = optarg; break; case 'D': /* debug flag */ debug = ON; tf = stdout; break; case 'I': /* ignore FATAL errors */ ignore = ON; break; case '?': /* don't know the option */ error(FATAL, ""); break; default: error(FATAL, "missing case for option %c", ch); break; } /* End switch */ } /* End while */ argc -= optind; argv += optind; } /* End of options */ /*****************************************************************************/ setpaths(name) char *name; { char *path; /* * * Extends the -L option to permit modification of more than just the prologue * file pathname. Syntax is -Lpath or -Lname:path. For debugging and development * only! * */ for ( path = name; *path; path++ ) if ( *path == ':' || *path == ' ' ) { while ( *path == ':' || *path == ' ' ) path++; break; } /* End if */ if ( *path == '\0' ) /* didn't find "name:" prefix */ path = name; if ( path == name || strncmp(name, "prologue", strlen("prologue")) == 0 ) prologue = path; else if ( strncmp(name, "draw", strlen("draw")) == 0 ) drawfile = path; else if ( strncmp(name, "color", strlen("color")) == 0 ) colorfile = path; else if ( strncmp(name, "form", strlen("form")) == 0 ) formfile = path; else if ( strncmp(name, "baseline", strlen("baseline")) == 0 ) baselinefile = path; } /* End of setpaths */ /*****************************************************************************/ setup() { double t; /* * * Job and BoundingBox initialization. Called once from t_init() - must know * the resolution before generating the PostScript call to setup. * */ writerequest(0, stdout); /* global requests e.g. manual feed */ fprintf(stdout, "/resolution %d def\n", res); fprintf(stdout, "setup\n"); fprintf(stdout, "%d setdecoding\n", realencoding); if ( formsperpage > 1 ) { /* multiple pages */ if ( cat(formfile) == FALSE ) error(FATAL, "can't read %s", formfile); fprintf(stdout, "%d setupforms\n", formsperpage); } /* End if */ fprintf(stdout, "%s", ENDSETUP); if ( dobbox == TRUE ) { /* ctm[] - must agree with prologue */ translate(pagewidth/2.0, pageheight/2.0); if ( landscape == TRUE ) { rotate(90.0); t = pagewidth; pagewidth = pageheight; pageheight = t; } /* End if */ translate(-pagewidth/2.0, pageheight/2.0); translate(72.0 * xoffset, -72.0 * yoffset); scale(magnification, magnification); scale(72.0/devres, 72.0/devres); } /* End if */ } /* End of setup */ /*****************************************************************************/ arguments() { FILE *fp; /* * * Everything else is an input file. No arguments or '-' means stdin. * */ if ( argc < 1 ) conv(stdin); else while ( argc > 0 ) { if ( strcmp(*argv, "-") == 0 ) fp = stdin; else if ( (fp = fopen(*argv, "r")) == NULL ) error(FATAL, "can't open %s", *argv); conv(fp); if ( fp != stdin ) fclose(fp); argc--; argv++; } /* End while */ } /* End of arguments */ /*****************************************************************************/ done() { int i; int n; /* * * Force out the last page and add trailing comments. * */ fprintf(stdout, "%s", TRAILER); fprintf(stdout, "done\n"); fprintf(stdout, "%s %d\n", PAGES, printed); for ( i = 0, n = 0; i < MAXFONTS+1; i++ ) if ( (fonts[i].flags & USED) && fonts[i].fontname != NULL ) { if ( n++ == 0 ) fprintf(stdout, "%s", DOCUMENTFONTS); else if ( (n - 1) % 8 == 0 ) fprintf(stdout, "\n%s", CONTINUECOMMENT); fprintf(stdout, " %s", fonts[i].fontname); } /* End if */ if ( n > 0 ) putc('\n', stdout); if ( dobbox == TRUE ) writebbox(stdout, BOUNDINGBOX, 10); } /* End of done */ /*****************************************************************************/ account() { /* * * Accounting record to fp_acct - provided it's not NULL. * */ if ( fp_acct != NULL ) fprintf(fp_acct, " print %d\n copies %d\n", printed, copies); } /* End of account */ /*****************************************************************************/ conv(fp) register FILE *fp; { register int c; int m, n, n1, m1; char str[50]; /* * * Read troff output from file fp and translate it into PostScript. The final * t_page() call means input files start on a new page. * */ redirect(-1); /* do output only after a page command */ lineno = 1; while ((c = getc(fp)) != EOF) { switch (c) { case '\n': /* just count this line */ lineno++; break; case ' ': /* when input is text */ case 0: /* occasional noise creeps in */ break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* two motion digits plus a character */ hmot((c-'0')*10 + getc(fp)-'0'); put1(getc(fp)); break; case 'c': /* single ascii character */ put1(getc(fp)); break; case 'C': /* special character */ fscanf(fp, "%s", str); put1(chindex(str)); break; case 'N': /* character at position n */ fscanf(fp, "%d", &m); flushtext(); oput(m); break; case 'D': /* drawing functions */ flushtext(); getdraw(); if ( size != lastsize ) t_sf(); switch ((c=getc(fp))) { case 'p': /* draw a path */ while (fscanf(fp, "%d %d", &n, &m) == 2) drawline(n, m); lineno++; break; case 'l': /* draw a line */ fscanf(fp, "%d %d %c", &n, &m, &n1); drawline(n, m); break; case 'c': /* circle */ fscanf(fp, "%d", &n); drawcirc(n); break; case 'e': /* ellipse */ fscanf(fp, "%d %d", &m, &n); drawellip(m, n); break; case 'a': /* counter-clockwise arc */ case 'A': /* clockwise arc */ fscanf(fp, "%d %d %d %d", &n, &m, &n1, &m1); drawarc(n, m, n1, m1, c); break; case 'q': /* spline without end points */ drawspline(fp, 1); lineno++; break; case '~': /* wiggly line */ drawspline(fp, 2); lineno++; break; default: error(FATAL, "unknown drawing function %c", c); break; } /* End switch */ break; case 's': /* use this point size */ fscanf(fp, "%d", &size); /* ignore fractional sizes */ break; case 'f': /* use font mounted here */ fscanf(fp, "%s", str); setfont(t_font(str)); break; case 'H': /* absolute horizontal motion */ fscanf(fp, "%d", &n); hgoto(n); break; case 'h': /* relative horizontal motion */ fscanf(fp, "%d", &n); hmot(n); break; case 'w': /* word space */ break; case 'V': /* absolute vertical position */ fscanf(fp, "%d", &n); vgoto(n); break; case 'v': /* relative vertical motion */ fscanf(fp, "%d", &n); vmot(n); break; case 'p': /* new page */ fscanf(fp, "%d", &n); t_page(n); break; case 'n': /* end of line */ while ( (c = getc(fp)) != '\n' && c != EOF ) ; hgoto(0); lineno++; break; case '#': /* comment */ while ( (c = getc(fp)) != '\n' && c != EOF ) ; lineno++; break; case 'x': /* device control function */ devcntrl(fp); lineno++; break; default: error(FATAL, "unknown input character %o %c", c, c); done(); } /* End switch */ } /* End while */ t_page(-1); /* print the last page */ flushtext(); } /* End of conv */ /*****************************************************************************/ devcntrl(fp) FILE *fp; { char str[50], buf[256], str1[50]; int c, n; /* * * Interpret device control commands, ignoring any we don't recognize. The * "x X ..." commands are a device dependent collection generated by troff's * \X'...' request. * */ fscanf(fp, "%s", str); switch ( str[0] ) { case 'f': /* load font in a position */ fscanf(fp, "%d %s", &n, str); fgets(buf, sizeof buf, fp); /* in case there's a filename */ ungetc('\n', fp); /* fgets() goes too far */ str1[0] = '\0'; /* in case there's nothing to come in */ sscanf(buf, "%s", str1); loadfont(n, str, str1); break; case 'i': /* initialize */ t_init(); break; case 'p': /* pause */ break; case 'r': /* resolution assumed when prepared */ fscanf(fp, "%d", &res); break; case 's': /* stop */ case 't': /* trailer */ flushtext(); break; case 'H': /* char height */ fscanf(fp, "%d", &n); t_charht(n); break; case 'S': /* slant */ fscanf(fp, "%d", &n); t_slant(n); break; case 'T': /* device name - set emulating here */ fscanf(fp, "%s", devname); emulate = strcmp(devname, realdev) ? TRUE : FALSE; break; case 'X': /* copy through - from troff */ fscanf(fp, " %[^: \n]:", str); fgets(buf, sizeof(buf), fp); ungetc('\n', fp); if ( strcmp(str, "PI") == 0 || strcmp(str, "PictureInclusion") == 0 ) picture(buf); else if ( strcmp(str, "InlinePicture") == 0 ) inlinepic(fp, buf); else if ( strcmp(str, "BeginPath") == 0 ) beginpath(buf, FALSE); else if ( strcmp(str, "DrawPath") == 0 ) drawpath(buf, FALSE); else if ( strcmp(str, "BeginObject") == 0 ) beginpath(buf, TRUE); else if ( strcmp(str, "EndObject") == 0 ) drawpath(buf, TRUE); else if ( strcmp(str, "NewBaseline") == 0 ) newbaseline(buf); else if ( strcmp(str, "DrawText") == 0 ) drawtext(buf); else if ( strcmp(str, "SetText") == 0 ) settext(buf); else if ( strcmp(str, "SetColor") == 0 ) { newcolor(buf); setcolor(); } else if ( strcmp(str, "PS") == 0 || strcmp(str, "PostScript") == 0 ) { flushtext(); xymove(hpos, vpos); fprintf(tf, "%s", buf); } /* End else */ break; } /* End switch */ while ( (c = getc(fp)) != '\n' && c != EOF ) ; } /* End of devcntrl */ /*****************************************************************************/ loadfont(m, f, dir) int m; char *f; char *dir; { char path[150]; /* * * Load position m with font f. Font file pathname is *fontdir/dev*realdev/*f * or *dir/*f, if dir isn't empty. Use mapfont() to replace the missing font * if we're emulating another device, dir is empty, and the first mount fails. * */ if ( dir[0] == '\0' ) sprintf(path, "%s/dev%s/%s", fontdir, realdev, f); else sprintf(path, "%s/%s", dir, f); if ( mountfont(path, m) == -1 ) { if ( dir[0] == '\0' ) { sprintf(path, "%s/dev%s/%s", fontdir, realdev, mapfont(f)); if ( mountfont(path, m) == -1 ) { sprintf(path, "%s/dev%s/%s", fontdir, realdev, f); error(FATAL, "can't load %s at %d", path, m); } /* End if */ } else error(FATAL, "can't load %s at %d", path, m); } /* End if */ if ( smnt == 0 && mount[m]->specfont ) smnt = m; if ( m == lastfont ) /* force a call to t_sf() */ lastfont = -1; if ( m > nfonts ) { /* got more positions */ nfonts = m; gotspecial = FALSE; } /* End if */ } /* End of loadfont */ /*****************************************************************************/ char *mapfont(name) char *name; { int i; /* * * Map a missing font name into one that should be available. Only used when * we're emulating another device and the first mount fails. Consider deleting * this routine. * */ for ( i = 0; fontmap[i].name != NULL; i++ ) if ( strcmp(name, fontmap[i].name) == 0 ) return(fontmap[i].use); switch ( *++name ) { case 'I': return("I"); case 'B': return("B"); case 'X': return("BI"); default: return("R"); } /* End switch */ } /* End of mapfont */ /*****************************************************************************/ loadspecial() { /* * * Fix - later. * */ gotspecial = TRUE; } /* End of loadspecial */ /*****************************************************************************/ t_init() { char path[150]; static int initialized = FALSE; /* * * Finish initialization - just read an "x init" command. Assumes we already * know the input file resolution. * */ flushtext(); /* moved - for cat'ed troff files */ if ( initialized == FALSE ) { sprintf(path, "%s/dev%s/DESC", fontdir, realdev); if ( getdesc(path) == -1 ) error(FATAL, "can't open %s", path); nfonts = 0; gotspecial = FALSE; widthfac = (float) res /devres; slop = pointslop * res / POINTS + .5; rvslop = res * .025; setup(); initialized = TRUE; } /* End if */ hpos = vpos = 0; size = 10; reset(); } /* End of t_init */ /*****************************************************************************/ t_page(pg) int pg; { static int lastpg = 0; /* * * Finish the previous page and get ready for the next one. End page output * goes to /dev/null at the start of each input file. Start page output goes * to /dev/null at the end of each input file. * * Consider doing showpage after page level restore (as Adobe recommends). If * the order is changed use restore() and save(). forms.ps will likely also * need fixing. * */ if ( tf == stdout ) printed++; flushtext(); /* just in case */ fprintf(tf, "cleartomark\n"); fprintf(tf, "showpage\n"); fprintf(tf, "saveobj restore\n"); if ( dobbox == TRUE ) writebbox(tf, PAGEBOUNDINGBOX, 10); fprintf(tf, "%s %d %d\n", ENDPAGE, lastpg, printed); redirect(pg); fprintf(tf, "%s %d %d\n", PAGE, pg, printed+1); if ( dobbox == TRUE ) fprintf(tf, "%s %s\n", PAGEBOUNDINGBOX, ATEND); fprintf(tf, "/saveobj save def\n"); fprintf(tf, "mark\n"); writerequest(printed+1, tf); fprintf(tf, "%d pagesetup\n", printed+1); if ( encoding != realencoding ) fprintf(tf, "%d setdecoding\n", encoding); if ( gotcolor == TRUE ) setcolor(); lastpg = pg; /* for the next ENDPAGE comment */ hpos = vpos = 0; /* get ready for the next page */ reset(); /* force position and font stuff - later */ seenpage = TRUE; } /* End of t_page */ /*****************************************************************************/ t_font(s) char *s; { int n; /* * * Converts the string *s into an integer and checks to make sure it's a legal * font position. Also arranges to mount all the special fonts after the last * legitimate font (by calling loadspecial()), provided it hasn't already been * done. * */ n = atoi(s); if ( seenpage == TRUE ) { if ( n < 0 || n > nfonts ) error(FATAL, "illegal font position %d", n); if ( gotspecial == FALSE ) loadspecial(); } /* End if */ return(n); } /* End of t_font */ /*****************************************************************************/ setfont(m) int m; { /* * * Use the font mounted at position m. Bounds checks are probably unnecessary. * Changing the font and size used by the printer is handled in t_sf(). * */ if ( m < 0 || m > MAXFONTS ) error(FATAL, "illegal font %d", m); font = m; } /* End of setfont */ /*****************************************************************************/ t_sf() { Font *fpos; char temp[150]; /* * * Force a new font or size. Generates name definitions for fonts that haven't * been named, grabs host resident font files and keeps track of the fonts used * by this job. When necessary also adjusts the font's height and slant. Should * only be called immediately before printing a character. * */ if ( tf == stdout && mounted(font) ) { flushtext(); fpos = mount[font]; if ( (fpos->flags & USED) == 0 ) { if ( (fpos->flags & NAMED) == 0 && fpos->fontname != NULL ) { sprintf(temp, "/%s /%s def\n", fpos->name, fpos->fontname); exportstring(temp); fpos->flags |= NAMED; /* unnecessary */ } /* End if */ if ( hostfontdir != NULL ) { sprintf(temp, "%s/%s", hostfontdir, fpos->name); exportfile(temp); } /* End if */ } /* End if */ fprintf(tf, "%d %s f\n", size, fpos->name); if ( fontheight != 0 || fontslant != 0 ) fprintf(tf, "%d %d changefont\n", fontslant, (fontheight != 0) ? fontheight : size); lastfont = font; lastsize = size; fpos->flags |= USED; } /* End if */ } /* End of t_sf */ /*****************************************************************************/ t_charht(n) int n; { /* * * Set character height to n points. Disabled if n is 0 or the current size. * */ fontheight = (n == size) ? 0 : n; lastfont = -1; } /* End of t_charht */ /*****************************************************************************/ t_slant(n) int n; { /* * * Set slant to n degrees. Disable slanting if n is 0. * */ fontslant = n; lastfont = -1; } /* End of t_slant */ /*****************************************************************************/ xymove(x, y) int x, y; { /* * * Make the the printer and post-processor agree about the current position. * */ flushtext(); hgoto(x); vgoto(y); fprintf(tf, "%d %d m\n", hpos, vpos); lastx = hpos; lasty = vpos; } /* End of xymove */ /*****************************************************************************/ put1(c) register int c; { register int i; register int j; register int k; int code; int ofont; /* * * Print character c. ASCII character if c < 128, otherwise it's special. Look * for c in the current font, then in all others starting at the first special * font. Save c in lastc so it's available when oput() runs. Restore original * font before leaving. * */ lastc = c; /* charlib() needs name not code */ if ( (c -= 32) <= 0 ) return; k = ofont = font; if ( (i = onfont(lastc, k)) == -1 && smnt > 0 ) for ( k = smnt, j = 0; j < nfonts; j++, k = k % nfonts + 1 ) { if ( (i = onfont(lastc, k)) != -1 ) { setfont(k); break; } /* End if */ } /* End for */ if ( i != -1 && (code = mount[k]->wp[i].code) != 0 ) { lastw = widthfac * ((mount[k]->wp[i].wid * size + unitwidth/2) / unitwidth); oput(code); } /* End if */ if ( font != ofont ) setfont(ofont); } /* End of put1 */ /*****************************************************************************/ oput(c) int c; { double llx, lly, urx, ury; /* boundingbox corners */ /* * * Arranges to print the character whose code is c in the current font. All the * actual positioning is done here, in charlib(), or in the drawing routines. * */ if ( textcount > MAXSTACK ) /* don't put too much on the stack? */ flushtext(); if ( font != lastfont || size != lastsize ) t_sf(); if ( vpos != lasty ) endline(); starttext(); if ( ABS(hpos - lastx) > slop ) endstring(); if ( isascii(c) && isprint(c) ) switch ( c ) { case '(': case ')': case '\\': addchar('\\'); default: addchar(c); } /* End switch */ else if ( c > 040 ) addoctal(c); else charlib(c); if ( dobbox == TRUE ) { llx = lastx; lly = -(vpos + 0.5 * (devres * size / 72.0)); urx = lastx + lastw; ury = -(vpos - (devres * size / 72.0)); cover(llx, lly); cover(urx, ury); } /* End if */ lastx += lastw; } /* End of oput */ /*****************************************************************************/ starttext() { /* * Called whenever we want to be sure we're ready to start collecting characters * for the next call to PostScript procedure t (ie. the one that prints them). If * textcount is positive we've already started, so there's nothing to do. The more * complicated encoding schemes save text strings in the strings[] array and need * detailed information about the strings when they're written to the output file * in flushtext(). * */ if ( textcount < 1 ) { switch ( encoding ) { case 0: case 1: putc('(', tf); break; case 2: case 3: strptr = strings; spacecount = 0; line[1].str = strptr; line[1].dx = 0; line[1].spaces = 0; line[1].start = hpos; line[1].width = 0; break; case MAXENCODING+1: /* reverse video */ if ( lastend == -1 ) lastend = hpos; putc('(', tf); break; case MAXENCODING+2: /* follow a funny baseline */ putc('(', tf); break; } /* End switch */ textcount = 1; lastx = stringstart = hpos; } /* End if */ } /* End of starttext */ /*****************************************************************************/ flushtext() { int i; /* * * Generates a call to the PostScript procedure that processes all the text we've * accumulated - provided textcount is positive. * */ if ( textcount > 0 ) { switch ( encoding ) { case 0: fprintf(tf, ")%d t\n", stringstart); break; case 1: fprintf(tf, ")%d %d t\n", stringstart, lasty); break; case 2: *strptr = '\0'; line[textcount].width = lastx - line[textcount].start; if ( spacecount != 0 || textcount != 1 ) { for ( i = textcount; i > 0; i-- ) fprintf(tf, "(%s)%d %d", line[i].str, line[i].spaces, line[i].width); fprintf(tf, " %d %d %d t\n", textcount, stringstart, lasty); } else fprintf(tf, "(%s)%d %d w\n", line[1].str, stringstart, lasty); break; case 3: *strptr = '\0'; if ( spacecount != 0 || textcount != 1 ) { for ( i = textcount; i > 0; i-- ) fprintf(tf, "(%s)%d", line[i].str, line[i].dx); fprintf(tf, " %d %d %d t\n", textcount, stringstart, lasty); } else fprintf(tf, "(%s)%d %d w\n", line[1].str, stringstart, lasty); break; case MAXENCODING+1: fprintf(tf, ")%d ", stringstart); fprintf(tf, "%d %d drawrvbox ", lastend - rvslop, (int)(lastx + .5) + rvslop); fprintf(tf, "t\n", stringstart); lastend = (lastx + .5) + 2 * rvslop; break; case MAXENCODING+2: fprintf(tf, ")%d %d t\n", stringstart, lasty); break; } /* End switch */ } /* End if */ textcount = 0; } /* End of flushtext */ /*****************************************************************************/ endstring() { int dx; /* * * Horizontal positions are out of sync. End the last open string, adjust the * printer's position, and start a new string. Assumes we've already started * accumulating text. * */ switch ( encoding ) { case 0: case 1: fprintf(tf, ")%d(", stringstart); textcount++; lastx = stringstart = hpos; break; case 2: case 3: dx = hpos - lastx; if ( spacecount++ == 0 ) line[textcount].dx = dx; if ( line[textcount].dx != dx ) { *strptr++ = '\0'; line[textcount].width = lastx - line[textcount].start; line[++textcount].str = strptr; *strptr++ = ' '; line[textcount].dx = dx; line[textcount].start = lastx; line[textcount].width = 0; line[textcount].spaces = 1; } else { *strptr++ = ' '; line[textcount].spaces++; } /* End else */ lastx += dx; break; case MAXENCODING+1: fprintf(tf, ")%d(", stringstart); textcount++; lastx = stringstart = hpos; break; case MAXENCODING+2: flushtext(); starttext(); break; } /* End switch */ } /* End of endstring */ /*****************************************************************************/ endline() { /* * * The vertical position has changed. Dump any accumulated text, then adjust * the printer's vertical position. * */ flushtext(); if ( encoding == 0 || encoding == MAXENCODING+1 ) fprintf(tf, "%d %d m\n", hpos, vpos); lastx = stringstart = lastend = hpos; lasty = vpos; } /* End of endline */ /*****************************************************************************/ addchar(c) int c; { /* * * Does whatever is needed to add character c to the current string. * */ switch ( encoding ) { case 0: case 1: putc(c, tf); break; case 2: case 3: *strptr++ = c; break; case MAXENCODING+1: case MAXENCODING+2: putc(c, tf); break; } /* End switch */ } /* End of addchar */ /*****************************************************************************/ addoctal(c) int c; { /* * * Add c to the current string as an octal escape. * */ switch ( encoding ) { case 0: case 1: fprintf(tf, "\\%o", c); break; case 2: case 3: sprintf(strptr, "\\%o", c); strptr += strlen(strptr); break; case MAXENCODING+1: case MAXENCODING+2: fprintf(tf, "\\%o", c); break; } /* End switch */ } /* End of addoctal */ /*****************************************************************************/ charlib(code) int code; /* either 1 or 2 */ { char *name; /* name of the character */ char tname[10]; /* in case it's a single ASCII character */ char temp[150]; /* * * Called from oput() for characters having codes less than 040. Special files * that define PostScript procedures for certain characters can be found in * directory *fontdir/devpost/charlib. If there's a file that has the same name as * the character we're trying to print it's copied to the output file, otherwise * nothing, except some positioning, is done. * * All character definitions are only made once. Subsequent requests to print the * character generate a call to a procedure that begins with the prefix build_ and * ends with the character's name. Special characters that are assigned codes * other than 1 are assumed to have additional data files that should be copied * to the output file immediately after the build_ call. Those data files should * end in the suffix .map, and usually will be a hex representation of a bitmap. * */ flushtext(); if ( lastc < 128 ) { /* just a simple ASCII character */ sprintf(tname, "%.3o", lastc); name = tname; } else name = chname(lastc); if ( downloaded[lastc] == 0 ) { sprintf(temp, "%s/dev%s/charlib/%s", fontdir, realdev, name); if ( exportfile(temp) == TRUE ) { downloaded[lastc] = 1; t_sf(); } /* End if */ } /* End if */ if ( downloaded[lastc] == 1 ) { xymove(hpos, vpos); fprintf(tf, "%d build_%s\n", (int) lastw, name); if ( code != 1 ) { /* get the bitmap or whatever */ sprintf(temp, "%s/dev%s/charlib/%s.map", fontdir, realdev, name); if ( access(temp, 04) == 0 && tf == stdout ) cat(temp); } /* End if */ fprintf(tf, "%d %d m\n", stringstart = hpos + lastw, vpos); } /* End if */ } /* End of charlib */ /*****************************************************************************/ reset() { /* * * Reset variables that keep track of the printer's current position, size and * font. Eventually forces things back in sync before oput() prints the next * character. * */ lastx = -(slop + 1); lasty = -1; lastfont = lastsize = -1; } /* End of reset */ /*****************************************************************************/ resetpos() { /* * * Reset the position tracking variables. Forces oput() to get positions back * in sync before printing the next character. * */ lastx = -(slop + 1); lasty = -1; } /* End of resetpos */ /*****************************************************************************/ save() { /* * * Save the current PostScript environment. Initialize things that may have * disappeared after the preceeding restore. * */ fprintf(tf, "/saveobj save def\n"); fprintf(tf, "mark\n"); if ( encoding != realencoding ) fprintf(tf, "%d setdecoding\n", encoding); if ( gotcolor == TRUE ) /* prevent getcolor() recursion */ setcolor(); } /* End of save */ /*****************************************************************************/ restore() { /* * * Restore the previous PostScript environment. * */ flushtext(); fprintf(tf, "cleartomark\n"); fprintf(tf, "saveobj restore\n"); reset(); } /* End of restore */ /*****************************************************************************/ exportfile(path) char *path; { int val = FALSE; /* * * Exports the contents of file path to the global environment. Returns TRUE * if we're doing output (i.e. tf == stdout) and the copy worked. * */ if ( tf == stdout && access(path, 04) == 0 ) { restore(); fprintf(tf, "%s", BEGINGLOBAL); val = cat(path); fprintf(tf, "%s", ENDGLOBAL); save(); } /* End if */ return(val); } /* End of exportfile */ /*****************************************************************************/ exportstring(str) char *str; { /* * * Exports string str to the global environment. No return value needed yet. * */ if ( tf == stdout && str != NULL && *str != '\0' ) { restore(); fprintf(tf, "%s", BEGINGLOBAL); fprintf(tf, "%s", str); fprintf(tf, "%s", ENDGLOBAL); save(); } /* End if */ } /* End of exportstring */ /*****************************************************************************/ redirect(pg) int pg; { static FILE *fp_null = NULL; /* * * If we're not supposed to print page pg, tf will be directed to /dev/null, * otherwise output goes to stdout. * */ if ( pg >= 0 && in_olist(pg) == ON ) tf = stdout; else if ( (tf = fp_null) == NULL ) tf = fp_null = fopen("/dev/null", "w"); } /* End of redirect */ /*****************************************************************************/