/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2005 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * Copyright (c) 2016 by Delphix. All rights reserved. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ #pragma ident "%Z%%M% %I% %E% SMI" /* * * 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 been changed, credit has to be given to Richard * Flood for his early work on the PostScript driver. * * Among the most interesting new features are color support (see devcntrl() and * file color.c) and code to handle complex paths pieced together using any of the * standard drawing commands (see devcntrl() and file draw.c). Reverse video mode * has also been included as a special case of the color support. Two encoding * schemes based on widthshow are also new additions. The safe one is obtained when * you set encoding to 2 (eg. using the -e2 option). The slightly faster method * is obtained by setting encoding to 3 (eg. using the -e3 option), although it's * not recommended. Rounding errors in character widths can accumulate and become * quite noticeable by the time you get to the right margin. More often than not * you end up getting a ragged right margin. * * 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. Device emulation is relatively expensive, and * can produce output files that are more than twice the size of the input files. * In most cases output files will be smaller than input files, perhaps by up to * 40 percent, although the results you get depend on what you're doing and the * text encoding you're using. You'll get the worst results if you're emulating * another device, using special bitmap characters, like the logo, or doing lots * of vertical motion or drawing. * * PostScript fonts don't support all of troff's characters, so some have to * be built by special PostScript procedures. Those routines can be found in * *fontdir/devpost/charlib, and are only used when we try to print a character * that has been assigned a code less than 32. Definitions are only made the * first time each character is used. Subsequent requests to print the character * only generate a call to the PostScript procedure that's been copied to the * output file. For example you'll find a file called sq in directory * *fontdir/devpost/charlib. It defines a PostScript procedure called build_sq * that's called whenever we need to print a square. Special characters that * have been assigned a code of 2 are expected to come in two pieces. The * definition part and bitmap part (or whatever). The definition is only made * once, but the contents of the character's .map file are copied to the output * file each time, immediately after charlib() generates the call to the * PostScript procedure (build_?? ) that builds the character. That's typically * how logos built from bitmaps would be handled. * * 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 0, which should * probably be the default, produces output essentially identical to the original * version of dpost. It's the slowest but most stable method of encoding lines of * text, and won't be bothered by rounding errors in the font width tables that * could become noticeable by the time you get to the end of a line. Other schemes * seem to work, but aren't well tested and are not guaranteed for all possible * jobs. encoding can be changed on the command line using the -e option. Part of * the support for different encoding schemes was to move control of all text * related output to separate routines. It makes dpost work harder, but changing * things is easy. For example adding stuff to support widthshow took less than * an hour. * * According to Adobe's structuring conventions, the output produced by dpost is * still nonconforming. Global definitions that are occasionally made in individual * pages are the primary problem. Among other things they handle downloading host * resident fonts and defining special characters not generally available on * PostScript printers. The approach used here works on a demand basis and violates * page independence. A definition is made once in the first page that needs it * and is bracketed by PostScript code that ensures the definition is exported to * the global environment where it will be available for use by all the pages that * follow. Simple changes, like downloading definitions the first time they're * used in each page, restores page independence but wouldn't be an efficient * solution. Other approaches are also available, but every one I've considered * sacrifices much in efficiency - just to maintain page independence. I'll leave * things be for now. Global definitions made in individual pages are bracketed * by %%BeginGlobal and %%EndGlobal comments and can easily be pulled out of * individual pages and put in the prologue by utility programs like postreverse. * * I've also added code that handles the DOCUMENTFONTS comment, although it's * only produced for those fonts in directory /usr/lib/font/devpost that have an * associated .name file. The first string in a .name file should be the (long) * PostScript name (eg. Times-Roman in R.name). For now everything else in the * .name file is ignored, although that may also change. You'll find .name files * for all the supported fonts in the devpost source directory, although they may * not be installed in /usr/lib/font/devpost. * * The PostScript prologue is copied from *prologue before any of the input files * are translated. The program expects the following procedures are avaliable: * * setup * * mark ... setup - * * Handles special initialization stuff that depends on how the program * was called. Expects to find a mark followed by key/value pairs on the * stack. The def operator is applied to each pair up to the mark, then * the default state is set up. An 'x res' command must preceed the * 'x init' command! * * pagesetup * * page pagesetup - * * Called at the start of each page, immediately after the page level * save, to do special initialization on a per page basis. Right now the * only argument is the current page number, and actually nothing of any * importance is currently done. * * setdecoding * * num setdecoding - * * Selects the text decoding procedure (ie. what's assigned to PostScript * procedure t) from the decodingdefs array defined in the prologue. num * should be the value assigned to variable encoding (in dpost) and will * remain constant throughout a job, unless special features, like reverse * video printing, are requested. The text encoding scheme can be set on * the command line using the -e option. Print time and the size of the * output file will usually decrease as the value assigned to encoding * increases. * * f * * size font f - * * Selects the size and font to be used for character imaging. Font names * are defined, in *prologue, so they agree with the one or two character * names used by troff. * * m * * x y m - * * Moves to point (x, y). Normally only used when the vertical position * changes. Horizontal positioning between words (or letters) is handled * in procedure t (below). * * t * * mark text t mark * * Processes everything on the stack, up to the mark, as a single line * of text to be printed at a fixed vertical position. What's put out as * text depends on the encoding scheme. Setting encoding to 0 produces * output essentially identical to the original version of dpost. In that * case everything on the stack, up to a mark, is interpreted (from top * down) as an absolute horizontal position and a string to be printed at * that point. For example the stack might look like, * * mark(this)1000(is)1100(an)1200(example)1300 t * * Procedure t would go through the stack, up to the mark, adjusting the * horizontal position before printing each string. In other encoding * schemes, like the one based on widthshow, strings containing several * space separated words would appear on the stack, and each one would be * preceeded by a number that's expected to be added to the width of a * space. For example we might have, * * mark(an example)30(this is)40 2 1000 2000 t * * where (1000, 2000) is where the first string starts and 2 is the repeat * count (ie. number of string and space pairs on the stack). * * w * * string x y w - * * Prints a single word starting at position (x, y). Only used in the more * complicated encoding schemes (eg. the ones based on widthshow). * * done * * Makes sure the last page is printed. Only needed when we're printing * more than one page on each sheet of paper. * * The PostScript procedures that support troff's drawing commands have been moved * out of *prologue and put in a separate file (ie. DRAW as defined in path.h). * The procedures are used by the routines in file draw.c, and are copied to the * output file at most once and only when needed. Yet another convenient violation * of page independence. If you don't approve append *drawfile to *prologue and * make sure *drawfile can't be read when DPOST runs. * * Many default values, like the magnification and orientation, are defined in * the prologue, which is where they belong. If they're changed (by options), an * appropriate definition is made after the prologue is added to the output file. * The -P option passes arbitrary PostScript through to the output file. Among * other things it can be used to set (or change) values that can't be accessed by * other options. * * * 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 #include #include #include #include #include #include "comments.h" /* PostScript file structuring comments */ #include "gen.h" /* general purpose definitions */ #include "path.h" /* for the prologue and a few other files */ #include "ext.h" /* external variable definitions */ #include "dev.h" /* typesetter and font descriptions */ #include "dpost.h" /* a few definitions just used here */ char *prologue = DPOST; /* the basic PostScript prologue */ char *colorfile = COLOR; /* things needed for color support */ char *drawfile = DRAW; /* and drawing */ char *formfile = FORMFILE; /* stuff for multiple pages per sheet */ char *baselinefile = BASELINE; char *fontdir = FONTDIR; /* binary device directories found here */ char *hostfontdir = NULL; /* host resident font directory */ int formsperpage = 1; /* page images on each piece of paper */ int copies = 1; /* and this many copies of each sheet */ int picflag = ON; /* enable/disable picture inclusion */ /* * * encoding selects the encoding scheme used to output lines of text. Change it * to something other than 0 at your own risk. The other methods seem to work but * aren't well tested and are not guaranteed. Some special features, like reverse * video, may temporarily change the encoding scheme and reset it to realencoding * when done. * */ int encoding = DFLTENCODING; int realencoding = DFLTENCODING; int maxencoding = MAXENCODING; /* * * seenfonts[] keeps track of the fonts we've used, based on internal numbers. It * helps manage host resident fonts and the DOCUMENTFONTS comment, but only works * if all fonts have internal numbers less than MAXINTERNAL. *docfonts counts the * number of font names we've recorded in *temp_file. If it's positive routine * done() adds *temp_file to the output file before quitting. * */ char seenfonts[MAXINTERNAL+1]; int docfonts = 0; /* * * devname[] is the device troff used when the job was formatted, while *realdev * is combined with *fontdir and used to locate the font and device tables that * that control the translation of the input files into PostScript. *realdev can * be changed using the -T option, but if you do you may end up getting garbage. * The character code field must agree with PostScript's font encoding and font * names must be properly mapped into PostScript font names in the prologue. * */ char devname[20] = ""; /* job is formatted for this printer */ char *realdev = DEVNAME; /* a good description of target printer */ /* * * Standard things that come from binary font and description files for *realdev. * Most are initialized in fontinit() or loadfont(). * */ struct dev dev; /* DESC.out starts this way */ struct Font *fontbase[NFONT+1]; /* FONT.out files begin this way */ short *pstab; /* list of available sizes */ int nsizes = 1; /* and the number of sizes in that list */ int smnt; /* index of first special font */ int nchtab; /* number of special character names */ int fsize; /* max size of a font files in bytes */ int unitwidth; /* set to dev.unitwidth */ char *chname; /* special character strings */ short *chtab; /* used to locate character names */ char *fitab[NFONT+1]; /* locates char info on each font */ char *widthtab[NFONT+1]; /* character width data for each font */ char *codetab[NFONT+1]; /* and codes to get characters printed */ /* * * Special characters missing from standard PostScript fonts are defined by files * in directory *fontdir/devpost/charlib. Files have the same names as the troff * special character names (for now at least) and each one defines a PostScript * procedure that begins with the prefix build_ and ends with the character's * name. * * For example, the routine used to build character \(12, would be build_12. * downloaded[] points to an array, allocated in fontinit(), that keeps track of * the characters that have already been defined - so we only do it once. * */ char *downloaded; /* nonzero means it's been downloaded */ /* * * Variables that keep track of troff's requests. All are set from values in the * input files. nfonts is adjusted in t_fp() as new fonts are mounted. * */ int nfonts = 0; /* number of font positions */ int size = 1; /* current size - internal value */ int font = 0; /* font position we're using now */ int hpos = 0; /* where troff wants to be - horizontally */ int vpos = 0; /* same but vertically */ float lastw = 0; /* width of the last input character */ int lastc = 0; /* and its name (or index) */ 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/dev.res */ /* * * Remember some of the same things, but this time for the printer. lastend is only * used when we're doing reverse video, and is where the last character on the * current line was printed. * */ int lastsize = -1; /* last internal size we used */ int lastfont = -1; /* last font we told printer about */ float lastx = -1; /* printer's current position */ int lasty = -1; int lastend; /* where last character on this line was */ /* * * fontname[] keeps track of the mounted fonts. Filled in (by t_fp()) from data * in the binary font files. * */ struct { char *name; /* name of the font loaded here */ int number; /* its internal number */ } fontname[NFONT+1] = {NULL, 0}; /* * * All the special fonts will be mounted after the last legitimate font position. * It helps when we're translating files prepared for devices, like the 202, that * have a different set of special fonts. The set of special fonts needed when * *realdev's tables are used may not get mounted when we're emulating another * device. gotspecial keeps track of whether we've done it yet. seenpage is set * to TRUE after we've seen the first page command in the input file. It controls * what's done in t_font() and is needed because nfonts is no longer set when the * DESC.out file is read, but rather is updated from "x font" commands in the * input files. * */ int gotspecial = FALSE; int seenpage = FALSE; /* * * The amount of horizontal positioning error we accept controls both the size * of the output file and the appearance of the printed text. It's probably most * important when we're emulating other devices, like the APS-5. The error can be * set using the -S option. It's converted from points to machine units in t_init() * after the resolution is known. rvslop is also set in t_init() and only used to * adjust the width of the box that's drawn around text when we're printing in * reverse video mode. * */ float pointslop = SLOP; /* horizontal error in points */ int slop; /* and machine units */ int rvslop; /* to extend box in reverse video mode */ /* * * Characters are accumulated and saved in PostScript strings that are eventually * processed by making a single call to procedure t. textcount counts the number * of individual strings collected but not yet processed, and is primarily used to * make sure PostScript's stack doesn't get too big. When textcount is positive * we've started accumulating strings and need to generate a call to PostScript * procedure t to process the text before anything else (like a font change) is * done. * */ int textcount = 0; /* strings accumulated so far */ int stringstart = 0; /* where the next one starts */ int spacecount = 0; /* spaces seen so far on current line */ /* * * Things that can be used by text line encoding schemes that need to read and * remember an entire line before doing any output. The strings that make up the * line can be saved in array strings[] and accessed by fields in line[]. *strptr * points to the next free slot in strings[]. * */ char strings[STRINGSPACE]; char *strptr; Line line[MAXSTACK+3]; /* * * When we're emulating another device we may want to map font name requests that * come in as "x font pos name" commands into some other font name before anything * else is done (ie. calling loadfont()). Font names can collide or we may just * want to a mapping that depends on the device troff used to format the input * files. devfontmap points to a structure that's filled in by getdevmap() if the * mapping file /usr/lib/font/dev*realdev/fontmaps/devname exists. mapdevfont() * then uses that table to translate font name requests into something else before * loadfont() gets called. * * fontmap[] provides a simple minded translation that maps an unrecognized font * name (in loadfont()) into another font name that we know will be available. It * doesn't provide the fine control available with *devfontmap, but should be good * enough for most jobs. Both structures are only needed when emulating another * device using *realdev's font tables. * */ Devfontmap *devfontmap = NULL; /* device level */ Fontmap fontmap[] = FONTMAP; /* and general mapping tables - emulation */ /* * * A few variables that are really only used if we're doing accounting. Designed * for our use at Murray Hill and probably won't suit your needs. Changes should * be easy and can be made in routine account(). * */ int printed = 0; /* charge for this many pages */ /* * * Output and accounting file definitions. The PostScript output always goes to * stdout or /dev/null, while the accounting file can be selected using the -A * option. * */ FILE *tf = NULL; /* PostScript output goes here */ FILE *fp_acct = NULL; /* accounting stuff written here */ /* * * Need the list of valid options in header() and options(), so I've moved the * definition here. * */ char *optnames = "a:c:e:m:n:o:p:tw:x:y:A:C:J:F:H:L:OP:R:S:T:DI"; /* * * Very temporary space that can be used to do things like building up pathnames * immediately before opening a file. Contents may not be preserved across calls * to subroutines defined in this file, so it probably should only be used in low * level subroutines like loadfont() or fontinit() and nowhere else. * */ char temp[150]; static void account(void); static void addchar(int); static void addoctal(int); static void arguments(void); static void charlib(int); static void conv(FILE *); static void devcntrl(FILE *); static void documentfonts(void); static void done(void); static void endline(void); static void endstring(void); void endtext(void); static void fontinit(void); static void fontprint(int); static void getdevmap(void); static void header(void); void hgoto(int); static void hmot(int); static void init_signals(void); static void loaddefault(void); static void loadfont(int, char *, char *); static void loadspecial(void); static void options(void); static void oput(int); static void put1(int); static void put1s(char *); static void redirect(int); void reset(void); void resetpos(void); static void setfont(int); static void setpaths(char *); static void setsize(int); static void setup(void); static void starttext(void); static void t_charht(int); static void t_fp(int, char *, char *); static void t_init(void); static void t_newline(void); static void t_page(int); static void t_reset(int); void t_sf(void); static void t_slant(int); static void t_trailer(void); void vgoto(int); static void vmot(int); /*****************************************************************************/ int main(int agc, char *agv[]) { /* * * A program that translates troff output into PostScript. All the input files * must have been formatted for the same device, which doesn't necessarily have to * be *realdev. If there's more than one input file, each begins on a new page. * */ argc = agc; /* global so everyone can use them */ argv = agv; prog_name = argv[0]; /* just for error messages */ init_signals(); /* sets up interrupt handling */ header(); /* PostScript file structuring comments */ options(); /* command line options */ arguments(); /* translate all the input files */ done(); /* add trailing comments etc. */ account(); /* job accounting data */ return (x_stat); /* everything probably went OK */ } /* End of main */ /*****************************************************************************/ static void init_signals(void) { void interrupt(); /* signal handler */ /* * * 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 */ /*****************************************************************************/ static void header(void) { int ch; /* return value from getopt() */ int old_optind = optind; /* for restoring optind - should be 1 */ /* * * Scans the option list looking for things, like the prologue file, that we need * right away but could be changed from the default. Doing things this way is an * attempt to conform to Adobe's latest file structuring conventions. In particular * they now say there should be nothing executed in the prologue, and they have * added two new comments that delimit global initialization calls. Once we know * where things really are we write out the job header, follow it by the prologue, * and then add the ENDPROLOG and BEGINSETUP comments. * */ while ( (ch = getopt(argc, argv, optnames)) != EOF ) if ( ch == 'L' ) setpaths(optarg); else if ( ch == '?' ) error(FATAL, ""); optind = old_optind; /* get ready for option scanning */ 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); fprintf(stdout, "%s", ENDCOMMENTS); if ( cat(prologue) == FALSE ) error(FATAL, "can't read %s", prologue); fprintf(stdout, "%s", ENDPROLOG); fprintf(stdout, "%s", BEGINSETUP); fprintf(stdout, "mark\n"); } /* End of header */ /*****************************************************************************/ static void options(void) { int ch; /* name returned by getopt() */ extern char *optarg; /* option argument set by getopt() */ extern int optind; /* * * Reads and processes the command line options. There are, without a doubt, too * many options! * */ 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': /* change the encoding scheme */ if ( (encoding = atoi(optarg)) < 0 || encoding > MAXENCODING ) encoding = DFLTENCODING; realencoding = encoding; break; case 'm': /* magnification */ 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 */ if ( *optarg == 'l' ) fprintf(stdout, "/landscape true def\n"); else fprintf(stdout, "/landscape false def\n"); break; case 't': /* just for compatibility */ break; case 'w': /* line width for drawing */ fprintf(stdout, "/linewidth %s def\n", optarg); break; case 'x': /* shift horizontally */ fprintf(stdout, "/xoffset %s def\n", optarg); break; case 'y': /* and vertically on the page */ fprintf(stdout, "/yoffset %s def\n", optarg); break; case 'A': /* force job accounting */ case 'J': if ( (fp_acct = fopen(optarg, "a")) == NULL ) error(FATAL, "can't open accounting file %s", optarg); break; case 'C': /* copy file to straight 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': /* PostScript prologue file */ setpaths(optarg); /* already been done in header() */ break; case 'O': /* turn picture inclusion off */ picflag = OFF; break; case 'P': /* PostScript pass through */ fprintf(stdout, "%s\n", optarg); break; case 'R': /* special 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; /* get ready for non-options args */ argv += optind; } /* End of options */ /*****************************************************************************/ static void setpaths(char *name) /* string that followed the -L option */ { char *path; /* start of the pathname */ /* * * Extends the -L option to permit run time modification of pathnames that were * fixed or didn't exist in previous versions of dpost. For example, the PostScript * drawing procedures have been moved out of *prologue and put in *drawfile. The * new syntax can be either -Lfile or -Lname:file. If the "name:" prefix is omitted * file will be used as the prologue, otherwise name should be one of "prologue", * "font", "draw", "color", or "form" and is used to select the pointer that gets * set to string "file". * */ for ( path = name; *path; path++ ) if ( *path == ':' || *path == ' ' ) { while ( *path == ':' || *path == ' ' ) path++; break; } /* End if */ if ( *path == '\0' ) /* didn't find a "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 */ /*****************************************************************************/ static void setup(void) { /* * Handles things that must be done after the options are read but before the * input files are processed. Called from t_init() after an "x init" command is * read, because we need the resolution before we can generate the call to the * setup procedure defined in *prologue. Only allowing one call to setup assumes * all the input files have been prepared for the same device. * */ writerequest(0, stdout); /* global requests eg. manual feed */ fprintf(stdout, "/resolution %d def\n", res); fprintf(stdout, "setup\n"); fprintf(stdout, "%d setdecoding\n", encoding); if ( formsperpage > 1 ) { /* followed by stuff for 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); } /* End of setup */ /*****************************************************************************/ static void arguments(void) { FILE *fp; /* next input file */ /* * * Makes sure all the non-option command line arguments are processed. If we get * here and there aren't any arguments left, or if '-' is one of the input files * we'll translate 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 */ /*****************************************************************************/ static void done(void) { /* * * Finished with all the input files, so mark the end of the pages with a TRAILER * comment, make sure the last page prints, and add things like the DOCUMENTFONTS * and PAGES comments that can only be determined after all the input files have * been read. * */ fprintf(stdout, "%s", TRAILER); fprintf(stdout, "done\n"); if ( temp_file != NULL ) { if ( docfonts > 0 ) { cat(temp_file); putc('\n', stdout); } /* End if */ unlink(temp_file); } /* End if */ fprintf(stdout, "%s %d\n", PAGES, printed); } /* End of done */ /*****************************************************************************/ static void account(void) { /* * * Writes an accounting record to *fp_acct provided it's not NULL. Accounting is * requested using the -A or -J options. * */ if ( fp_acct != NULL ) fprintf(fp_acct, " print %d\n copies %d\n", printed, copies); } /* End of account */ /*****************************************************************************/ static void conv(FILE *fp) /* next input file */ { int c; /* usually first char in next command */ int m, n, n1, m1; /* when we need to read integers */ char str[50]; /* for special chars and font numbers */ /* * * Controls the translation of troff's device independent output language into * PostScript. The call to t_page() that prints the last page is made when we * exit the loop, but probably belongs in t_trailer(). * */ redirect(-1); /* only do output after a page command */ lineno = 1; /* line in current file */ 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); put1s(str); break; case 'N': /* character at position n */ fscanf(fp, "%d", &m); endtext(); oput(m); break; case 'D': /* drawing functions */ endtext(); 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", &n); /* ignore fractional sizes */ setsize(t_size(n)); 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 ) ; t_newline(); 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 */ endtext(); } /* End of conv */ /*****************************************************************************/ static void devcntrl(FILE *fp) /* current input file */ { char str[50], buf[256], str1[50]; int c, n; /* * * Called from conv() to process the rest of a device control function. There's * a whole family of them and they all start with the string "x ", which we've * already read. The "x X ..." commands are an extensible (and device dependent) * family that we use here for things like picture inclusion. Unrecognized device * control commands are ignored. * */ fscanf(fp, "%s", str); /* get the control function name */ switch ( str[0] ) { /* only the first character counts */ case 'i': /* initialize */ t_init(); break; case 'T': /* device name */ fscanf(fp, "%s", devname); getdevmap(); strcpy(devname, realdev); break; case 't': /* trailer */ t_trailer(); break; case 'p': /* pause -- can restart */ t_reset('p'); break; case 's': /* stop */ t_reset('s'); break; case 'r': /* resolution assumed when prepared */ fscanf(fp, "%d", &res); break; 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, mapdevfont(str), str1); break; /* these don't belong here... */ case 'H': /* char height */ fscanf(fp, "%d", &n); t_charht(n); break; case 'S': /* slant */ fscanf(fp, "%d", &n); t_slant(n); 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 ) { endtext(); /* xymove(hpos, vpos); ul90-22006 */ fprintf(tf, "%s", buf); } /* End else */ break; } /* End switch */ while ( (c = getc(fp)) != '\n' && c != EOF ) ; } /* End of devcntrl */ /*****************************************************************************/ static void fontinit(void) { int fin; /* for reading the DESC.out file */ char *filebase; /* the whole thing goes here */ int i; /* loop index */ /* * * Reads *realdev's DESC.out file and uses what's there to initialize things like * the list of available point sizes. Old versions of the program used *devname's * DESC.out file to initialize nfonts, but that meant we needed to have *devname's * binary font files available for emulation. That restriction has been removed * and we now set nfonts using the "x font" commands in the input file, so by the * time we get here all we really need is *realdev. In fact devcntrl() reads the * device name from the "x T ..." command, but almost immediately replaces it with * string *realdev so we end up using *realdev's DESC.out file. Later on (in * t_font()) we mount all of *realdev's special fonts after the last legitimate * font position, just to be sure device emulation works reasonably well - there's * no guarantee *devname's special fonts match what's needed when *realdev's tables * are used. * */ sprintf(temp, "%s/dev%s/DESC.out", fontdir, devname); if ( (fin = open(temp, 0)) < 0 ) error(FATAL, "can't open tables for %s", temp); read(fin, &dev, sizeof(struct dev)); nfonts = 0; /* was dev.nfonts - now set in t_fp() */ nsizes = dev.nsizes; nchtab = dev.nchtab; unitwidth = dev.unitwidth; if ( (filebase = malloc(dev.filesize)) == NULL ) error(FATAL, "no memory for description file"); read(fin, filebase, dev.filesize); /* all at once */ close(fin); pstab = (short *) filebase; chtab = pstab + nsizes + 1; chname = (char *) (chtab + nchtab); fsize = 3 * 255 + nchtab + 128 - 32 + sizeof(struct Font); for ( i = 1; i <= NFONT; i++ ) { /* so loadfont() knows nothing's there */ fontbase[i] = NULL; widthtab[i] = codetab[i] = fitab[i] = NULL; } /* End for */ if ( (downloaded = (char *) calloc(nchtab + 128, sizeof(char))) == NULL ) error(FATAL, "no memory"); } /* End of fontinit */ /*****************************************************************************/ static void loadfont(int n, char *s, char *s1) /* n - load this font position */ /* s - with the .out file for this font */ /* s1 - taken from here - possibly */ { int fin; /* for reading *s.out file */ int nw; /* number of width table entries */ /* * * Loads font position n with the binary font file for *s.out provided it's not * already there. If *s1 is NULL or points to the empty string we read files from * directory *fontdir/dev*devname, otherwise directory *s1 is used. If the first * open fails we try to map font *s into one we expect will be available, and then * we try again. * */ if ( n < 0 || n > NFONT ) /* make sure it's a legal position */ error(FATAL, "illegal fp command %d %s", n, s); if ( fontbase[n] != NULL && strcmp(s, fontbase[n]->namefont) == 0 ) return; if ( s1 == NULL || s1[0] == '\0' ) sprintf(temp, "%s/dev%s/%s.out", fontdir, devname, s); else sprintf(temp, "%s/%s.out", s1, s); if ( (fin = open(temp, 0)) < 0 ) { sprintf(temp, "%s/dev%s/%s.out", fontdir, devname, mapfont(s)); if ( (fin = open(temp, 0)) < 0 ) error(FATAL, "can't open font table %s", temp); } /* End if */ if ( fontbase[n] != NULL ) /* something's already there */ free(fontbase[n]); /* so release the memory first */ fontbase[n] = (struct Font *) malloc(fsize); if ( fontbase[n] == NULL ) error(FATAL, "Out of space in loadfont %s", s); read(fin, fontbase[n], fsize); close(fin); if ( smnt == 0 && fontbase[n]->specfont == 1 ) smnt = n; nw = fontbase[n]->nwfont & BMASK; widthtab[n] = (char *) fontbase[n] + sizeof(struct Font); codetab[n] = (char *) widthtab[n] + 2 * nw; fitab[n] = (char *) widthtab[n] + 3 * nw; t_fp(n, fontbase[n]->namefont, fontbase[n]->intname); if ( debug == ON ) fontprint(n); } /* End of loadfont */ /*****************************************************************************/ static void loadspecial(void) { char *p; /* for next binary font file */ int nw; /* width entries in next font */ int i; /* loop index */ /* * * Loads all the special fonts after the last legal font position. Mostly used * for device emulation, but we'll do it no matter what. Needed because there's * no consistency in special fonts across different devices, and relying on having * them mounted in the input file doesn't guarantee the whole collection will be * there. The special fonts are determined and mounted using the copy of the * DESC.out file that's been read into memory. Initially had this stuff at the * end of fontinit(), but we now don't know nfonts until much later. * */ if ( gotspecial == FALSE ) for ( i = 1, p = chname + dev.lchname; i <= dev.nfonts; i++ ) { nw = *p & BMASK; if ( ((struct Font *) p)->specfont == 1 ) loadfont(++nfonts, ((struct Font *)p)->namefont, NULL); p += 3 * nw + dev.nchtab + 128 - 32 + sizeof(struct Font); } /* End for */ gotspecial = TRUE; } /* End of loadspecial */ /*****************************************************************************/ char *defaultFonts[] = { "R", "I", "B", "BI", "CW", "H", "HB", "HX", "S1", "S", NULL }; static void loaddefault(void) { int i; for (i = 0; defaultFonts[i] != NULL ; i++) loadfont(++nfonts, defaultFonts[i], NULL); } static void fontprint(int i) /* font's index in fontbase[] */ { int j, n; char *p; /* * * Debugging routine that dumps data about the font mounted in position i. * */ fprintf(tf, "font %d:\n", i); p = (char *) fontbase[i]; n = fontbase[i]->nwfont & BMASK; fprintf(tf, "base=0%o, nchars=%d, spec=%d, name=%s, widtab=0%o, fitab=0%o\n", p, n, fontbase[i]->specfont, fontbase[i]->namefont, widthtab[i], fitab[i]); fprintf(tf, "widths:\n"); for ( j = 0; j <= n; j++ ) { fprintf(tf, " %2d", widthtab[i][j] & BMASK); if ( j % 20 == 19 ) putc('\n', tf); } /* End for */ fprintf(tf, "\ncodetab:\n"); for ( j = 0; j <= n; j++ ) { fprintf(tf, " %2d", codetab[i][j] & BMASK); if ( j % 20 == 19 ) putc('\n', tf); } /* End for */ fprintf(tf, "\nfitab:\n"); for ( j = 0; j <= dev.nchtab + 128-32; j++ ) { fprintf(tf, " %2d", fitab[i][j] & BMASK); if ( j % 20 == 19 ) putc('\n', tf); } /* End for */ putc('\n', tf); } /* End of fontprint */ /*****************************************************************************/ char * mapfont(char *name) /* troff wanted this font */ { int i; /* loop index */ /* * * If loadfont() can't find font *name we map it into something else that should * be available and return a pointer to the new name. Used mostly for emulating * devices like the APS-5. * */ 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 */ /*****************************************************************************/ static void getdevmap(void) { FILE *fp; /* for reading the device fontmap file */ int i = 0; /* number of mapping pairs we've read */ int c; /* for skipping lines */ /* * * Looks for the device font mapping file *fontdir/dev*realdev/fontmaps/devname. * The file, if it exists, should be an ASCII file containing pairs of one or two * character font names per line. The first name is the font troff will be asking * for and the second is the one we'll use. Comments are lines that begin with * a '#' as the first non-white space character on a line. The devfontmap list * ends with a member that has the empty string in the name field. * */ sprintf(temp, "%s/dev%s/fontmaps/%s", fontdir, realdev, devname); if ( devfontmap == NULL && (fp = fopen(temp, "r")) != NULL ) { devfontmap = (Devfontmap *) malloc(10 * sizeof(Devfontmap)); while ( fscanf(fp, "%s", temp) != EOF ) { if ( temp[0] != '#' && strlen(temp) < 3 ) if ( fscanf(fp, "%s", &temp[3]) == 1 && strlen(&temp[3]) < 3 ) { strcpy((devfontmap + i)->name, temp); strcpy((devfontmap + i)->use, &temp[3]); if ( ++i % 10 == 0 ) devfontmap = (Devfontmap *) realloc(devfontmap, (i + 10) * sizeof(Devfontmap)); } /* End if */ while ( (c = getc(fp)) != '\n' && c != EOF ) ; } /* End while */ (devfontmap + i)->name[0] = '\0'; /* end the list we just read */ fclose(fp); } /* End if */ } /* End of getdevmap */ /*****************************************************************************/ char * mapdevfont(char *str) { int i; /* * * Called immediately before loadfont() after an 'x font' command is recognized. * Takes the font name that troff asked for, looks it up in the devfontmap list, * and returns the mapped name to the caller. No mapping is done if the devfontmap * list is empty or font *str isn't found in the list. * */ if ( devfontmap != NULL ) for ( i = 0; (devfontmap + i)->name[0] != '\0'; i++ ) if ( strcmp((devfontmap + i)->name, str) == 0 ) return((devfontmap + i)->use); return(str); } /* End of mapdevfont */ /*****************************************************************************/ void reset(void) { /* * * Resets the variables that keep track of the printer's current position, font, * and size. Typically used after a restore/save pair (eg. when we finish with a * page) to make sure we force the printer back into sync (in terms of the font * and current point) before text is printed. * */ lastx = -(slop + 1); lasty = -1; lastfont = lastsize = -1; } /* End of reset */ /*****************************************************************************/ void resetpos(void) { /* * * Resets the variables that keep track of the printer's current position. Used * when there's a chance we've lost track of the printer's current position or * done something that may have wiped it out, and we want to force dpost to set * the printer's position before printing text or whatever. For example stroke or * fill implicitly do a newpath, and that wipes out the current point, unless the * calls were bracketed by a gsave/grestore pair. * */ lastx = -(slop + 1); lasty = -1; } /* End of resetpos */ /*****************************************************************************/ static void t_init(void) { static int initialized = FALSE; /* only do most things once */ /* * * Called from devcntrl() after an "x init" command is read. Things only work if * we've already seen the "x res" command, and much of the stuff, including the * call to setup, should only be done once. Restricting everything to one call of * setup (ie. the one in the prologue) means all the input files must have been * formatted for the same device. * */ endtext(); /* moved - for cat'ed troff files */ if ( initialized == FALSE ) { /* only do this stuff once per job */ fontinit(); gotspecial = FALSE; widthfac = (float) res /dev.res; slop = pointslop * res / POINTS + .5; rvslop = res * .025; setup(); initialized = TRUE; } /* End if */ hpos = vpos = 0; /* upper left corner */ setsize(t_size(10)); /* start somewhere */ reset(); /* force position and font stuff - later */ } /* End of t_init */ /*****************************************************************************/ static void t_page(int pg) /* troff's current page number */ { static int lastpg = 0; /* last one we started - for ENDPAGE */ /* * * Called whenever we've finished the last page and want to get ready for the * next one. Also used at the end of each input file, so we have to be careful * about what's done. The first time through (up to the redirect(pg) call) output * goes to /dev/null because of the redirect(-1) call made in conv(). * * Adobe now recommends that the showpage operator occur after the page level * restore so it can be easily redefined to have side-effects in the printer's VM. * Although it seems reasonable I haven't implemented it, because it makes other * things, like selectively setting manual feed or choosing an alternate paper * tray, clumsy - at least on a per page basis. * */ if ( tf == stdout ) /* count the last page */ printed++; endtext(); /* print the last line? */ fprintf(tf, "cleartomark\n"); fprintf(tf, "showpage\n"); fprintf(tf, "restore\n"); fprintf(tf, "%s %d %d\n", ENDPAGE, lastpg, printed); redirect(pg); fprintf(tf, "%s %d %d\n", PAGE, pg, printed+1); fprintf(tf, "save\n"); fprintf(tf, "mark\n"); writerequest(printed+1, tf); fprintf(tf, "%d pagesetup\n", printed+1); 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 */ /*****************************************************************************/ static void t_newline(void) { /* * * Just finished the last line. All we do is set the horizontal position to 0, * although even that probably isn't necessary. * */ hpos = 0; } /* End of t_newline */ /*****************************************************************************/ int t_size(int n) /* convert this to an internal size */ { int i; /* loop index */ /* * * Converts a point size into an internal size that can be used as an index into * pstab[]. The internal size is one plus the index of the least upper bound of * n in pstab[], or nsizes if n is larger than all the listed sizes. * */ if ( n <= pstab[0] ) return(1); else if (n >= pstab[nsizes-1]) return(nsizes); for ( i = 0; n > pstab[i]; i++ ) ; return(i+1); } /* End of t_size */ /*****************************************************************************/ static void setsize(int n) /* new internal size */ { /* * * Now using internal size n, where pstab[n-1] is the best available approximation * to the size troff asked for. * */ size = n; } /* End of setsize */ /*****************************************************************************/ static void t_fp(int n, char *s, char *si) /* n - this position */ /* s - now has this font mounted */ /* si - its internal number */ { /* * * Updates nfonts and the array that keeps track of the mounted fonts. Called from * loadfont() after an "x font pos font" command is read, and if pos is larger than * the current value assigned to nfonts we set gotspecial to FALSE to make sure * t_font() loads all the special fonts after the last legitimate font position. * */ fontname[n].name = s; fontname[n].number = atoi(si); if ( n == lastfont ) /* force a call to t_sf() */ lastfont = -1; if ( n > nfonts ) { /* got more positions */ nfonts = n; gotspecial = FALSE; } /* End if */ } /* End of t_fp */ /*****************************************************************************/ int t_font(char *s) /* use font in this position next */ { 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 */ /*****************************************************************************/ static void setfont(int n) /* use the font mounted here */ { /* * * troff wants to use the font that's been mounted in position n. All we do here * is update the variable that keeps track of the current position. PostScript * font changes are handled in t_sf(), and are only generated right before we're * ready to print or draw something. * */ if ( n < 0 || n > NFONT ) error(FATAL, "illegal font %d", n); if ( fontname[n].name == NULL && fontname[n].number == 0) loaddefault(); if ( fontname[n].name == NULL && fontname[n].number == 0) error(FATAL, "font %d not loaded: check 'dpost' input for 'x font %d XXX' before 'f%d'", n, n, n); font = n; } /* End of setfont */ /*****************************************************************************/ void t_sf(void) { int fnum; /* internal font number */ /* * * Called whenever we need to use a new font or size. Only done right before we * print a character. The seenfonts[] array keeps track of the fonts we've used. * Helps manage host resident fonts and the DOCUMENTFONTS comment that's put out * at the end of the job. The array is indexed by internal number. Only works for * fonts that have internal numbers less than or equal to MAXINTERNAL. * */ if ( fontname[font].name == NULL ) return; endtext(); if ( (fnum = fontname[font].number) > MAXINTERNAL || fnum < 0 ) fnum = 0; if ( fnum > 0 && seenfonts[fnum] == 0 && hostfontdir != NULL ) { sprintf(temp, "%s/%s", hostfontdir, fontname[font].name); if ( access(temp, 04) == 0 ) doglobal(temp); } /* End if */ if ( tf == stdout ) { lastfont = font; lastsize = size; if ( seenfonts[fnum] == 0 ) documentfonts(); seenfonts[fnum] = 1; } /* End if */ fprintf(tf, "%d %s f\n", pstab[size-1], fontname[font].name); if ( fontheight != 0 || fontslant != 0 ) fprintf(tf, "%d %d changefont\n", fontslant, (fontheight != 0) ? fontheight : pstab[size-1]); } /* End of t_sf */ /*****************************************************************************/ static void t_charht(int n) /* use this as the character height */ { /* * * Remembers the requested height, from 'x H n'. Forces a call to t_sf(), which * is where the real work is done, by setting lastfont to -1. * */ fontheight = (n == pstab[size-1]) ? 0 : n; lastfont = -1; } /* End of t_charht */ /*****************************************************************************/ static void t_slant(int n) /* slant characters this many degrees */ { /* * * Remembers the requested slant, from 'x X n'. Forces a call to t_sf(), which * is where the real work is done, by setting lastfont to -1. * */ fontslant = n; lastfont = -1; } /* End of t_slant */ /*****************************************************************************/ static void t_reset(int c) /* pause or restart */ { /* * * Found an "x stop" or "x pause" command. Although nothing's done here we could * add code to reset everything so dpost could handle multiple files formatted for * different devices. * */ } /* End of t_reset */ /*****************************************************************************/ static void t_trailer(void) { /* * * Called after we find an "x trailer" in the input file. Forcing out the last * page is done at the end of conv(), but probably belongs here. * */ endtext(); } /* End of t_trailer */ /*****************************************************************************/ void hgoto(int n) /* new horizontal position */ { /* * * Want to be at this absolute horizontal position next. Actual motion commands * are generated in oput(), charlib(), and the drawing routines. * */ hpos = n; } /* End of hgoto */ /*****************************************************************************/ static void hmot(int n) /* move this far horizontally */ { /* * * Handles relative horizontal motion. troff's current positon, as recorded in * in hpos, is changed by n units. Usually called right before we're supposed to * print a character. * */ hpos += n; } /* End of hmot */ /*****************************************************************************/ void vgoto(int n) /* new vertical position */ { /* * * Moves vertically in troff's coordinate system to absolute position n. * */ vpos = n; } /* End of vgoto */ /*****************************************************************************/ static void vmot(int n) /* move this far vertically */ { /* * * Handles relative vertical motion of n units in troff's coordinate system. * */ vpos += n; } /* End of vmot */ /*****************************************************************************/ void xymove(int x, int y) /* this is where we want to be */ { /* * * Makes sure the post-processor and printer agree about the current position. * */ hgoto(x); vgoto(y); fprintf(tf, "%d %d m\n", hpos, vpos); lastx = hpos; lasty = vpos; } /* End of xymove */ /*****************************************************************************/ static void put1s(char *s) /* find and print this character */ { static int i = 0; /* last one we found - usually */ /* * * *s points to the start of a two character string that represents one of troff's * special characters. To print it we first look for *s in the chname[] array using * chtab[i] to find the string representing character i in chname[]. If the lookup * is successful we add 128 to i and ask put1() to finish printing the character. * We remember the index where the last character was found because requests to * print a special character often come in bunches (eg. drawing lines with \(ru). * */ if ( strcmp(s, &chname[chtab[i]]) != 0 ) for ( i = 0; i < nchtab; i++ ) if ( strcmp(&chname[chtab[i]], s) == 0 ) break; if ( i < nchtab ) put1(i + 128); else i = 0; } /* End of put1s */ /*****************************************************************************/ static void put1(int c) /* want to print this character */ { int i; /* character code from fitab */ int j; /* number of fonts we've checked so far */ int k; /* font we're currently looking at */ char *pw; /* font widthtab and */ char *p; /* and codetab where c was found */ int code; /* code used to get c printed */ int ofont; /* font when we started */ /* * * Arranges to have character c printed. If c < 128 it's a simple ASCII character, * otherwise it's a special character. Things done here have to agree with the way * the font tables were built by makedev, and work as follows. First we subtract * 32 from c because the tables don't record the non-graphic ASCII characters. * If fitab[k][c] isn't zero the character is on font k and the value is an index * that can be used to recover width and character code data from the other two * tables. If fitab[k][c] is zero the character isn't defined on font k and we * check the next font, which is found as follows. The current font is the first * one we check, and it's followed by a circular search of all the remaining fonts * that starts with the first special font and skips font position 0. If character * c is found somewhere besides the current font we change to that font and use * fitab[k][c] to locate missing data in the other two tables. The width of the * character can be found at widthtab[k][c] while codetab[k][c] is whatever we * need to tell the printer to have character c printed. lastc records the real * name of the character because it's lost by the time oput() gets called but * charlib() may need it. * * Took all the debugging stuff out because at least this part of the program is * reasonably solid. * */ lastc = c; /* charlib() needs the name not the code */ if ( (c -= 32) <= 0 ) /* probably never happens */ return; k = ofont = font; if ( (i = fitab[k][c] & BMASK) != 0 ) { /* it's on this font */ p = codetab[font]; pw = widthtab[font]; } else if ( smnt > 0 ) { /* on special (we hope) */ for ( k=smnt, j=0; j <= nfonts; j++, k = (k+1) % (nfonts+1) ) { if ( k == 0 ) continue; if ( (i = fitab[k][c] & BMASK) != 0 ) { p = codetab[k]; pw = widthtab[k]; setfont(k); break; } /* End if */ } /* End for */ } /* End else */ if ( i != 0 && (code = p[i] & BMASK) != 0 ) { lastw = widthfac * (((pw[i] & BMASK) * pstab[size-1] + unitwidth/2) / unitwidth); oput(code); } /* End if */ if ( font != ofont ) setfont(ofont); } /* End of put1 */ /*****************************************************************************/ static void oput(int c) /* want to print this character */ { /* * * 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? */ endtext(); 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); lastx += lastw; } /* End of oput */ /*****************************************************************************/ static void starttext(void) { /* * 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 endtext(). * */ 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 */ /*****************************************************************************/ void endtext(void) { int i; /* loop index */ /* * * Generates a call to the PostScript procedure that processes all the text we've * accumulated - provided textcount is positive. * */ if ( textcount > 0 ) { /* started working on some text */ 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 endtext */ /*****************************************************************************/ static void endstring(void) { 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: endtext(); starttext(); break; } /* End switch */ } /* End of endstring */ /*****************************************************************************/ static void endline(void) { /* * * The vertical position has changed. Dump any accumulated text, then adjust * the printer's vertical position. * */ endtext(); if ( encoding == 0 || encoding == MAXENCODING+1 ) fprintf(tf, "%d %d m\n", hpos, vpos); lastx = stringstart = lastend = hpos; lasty = vpos; } /* End of endline */ /*****************************************************************************/ static void addchar(int c) /* next character in current string */ { /* * * 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 */ /*****************************************************************************/ static void addoctal(int c) /* add it as an octal escape */ { /* * * Adds c to the current string as an octal escape \ddd. * */ 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 */ /*****************************************************************************/ static void charlib(int code) /* either 1 or 2 */ { char *name; /* name of the character */ char tname[10]; /* in case it's a single ASCII character */ /* * * 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. * */ endtext(); if ( lastc < 128 ) { /* just a simple ASCII character */ sprintf(tname, "%.3o", lastc); name = tname; } else name = &chname[chtab[lastc - 128]]; if ( downloaded[lastc] == 0 ) { sprintf(temp, "%s/dev%s/charlib/%s", fontdir, realdev, name); if ( access(temp, 04) == 0 && doglobal(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 */ /*****************************************************************************/ int doglobal(char *name) /* copy this to the output - globally */ { int val = FALSE; /* returned to the caller */ /* * * Copies file *name to the output file and brackets it with whatever commands are * needed to have it exported to the global environment. TRUE is returned if we * successfully add file *name to the output file. * */ if ( tf == stdout ) { endtext(); fprintf(tf, "cleartomark restore\n"); fprintf(tf, "%s", BEGINGLOBAL); val = cat(name); fprintf(tf, "%s", ENDGLOBAL); fprintf(tf, "save mark\n"); reset(); } /* End if */ return(val); } /* End of doglobal */ /*****************************************************************************/ static void documentfonts(void) { FILE *fp_in; /* PostScript font name read from here */ FILE *fp_out; /* and added to this file */ /* * * Whenever a new font is used we try to record the appropriate PostScript font * name in *temp_file for the DOCUMENTFONTS comment that's put out in done(). * By default PostScript font names are found in /usr/lib/font/devpost. Fonts * that have a .name file are recorded in *temp_file. The first string in that * file is expected to be that font's (long) PostScript name. * */ if ( temp_file == NULL ) /* generate a temp file name */ if ( (temp_file = tempnam(TEMPDIR, "dpost")) == NULL ) return; sprintf(temp, "%s/dev%s/%s.name", fontdir, realdev, fontname[font].name); if ( (fp_in = fopen(temp, "r")) != NULL ) { if ( (fp_out = fopen(temp_file, "a")) != NULL ) { if ( fscanf(fp_in, "%s", temp) == 1 ) { if ( docfonts++ == 0 ) fprintf(fp_out, "%s", DOCUMENTFONTS); else if ( (docfonts - 1) % 8 == 0 ) fprintf(fp_out, "\n%s", CONTINUECOMMENT); fprintf(fp_out, " %s", temp); } /* End if */ fclose(fp_out); } /* End if */ fclose(fp_in); } /* End if */ } /* End of documentfonts */ /*****************************************************************************/ static void redirect(int pg) /* next page we're printing */ { static FILE *fp_null = NULL; /* if output is turned off */ /* * * 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 */