/* * * dimpress - cleaned up and modified code borrowed from di10. * * * troff post-processor for Imagen printers. Output is written in * version 2.0 Impress, although it does still work for earlier versions. * Raster files in either the old format or in Imagen's Rst format can * be used for character generation. Drawing functions are handled by * the routines in file impdraw.c. I rewrote most of those routines so they * use the graphics primitives avaliable in version 1.9 (and later) Impress. * If you're printer is still running older systems you'll probably need * to get the original version of draw.c and use it instead. * * All the code should work properly for any printer that accepts Impress. * By default I've got things set up for the 8/300, but you can use the * -T option to select a different printer. If you choose a new printer * name not supported under the -T option you'll probably want to add code * to do the proper initialization. There really are only six variables * that need to be changed for different printers. They include realdev, * penwidth, pres, xoff, yoff, and possibly bitdir. The printer variables * are defined in dimpress.h (PR_INIT) and that's what's used to initialize * the prdata[] array. If you make changes just be sure that the list ends * with an entry that has its prname field set to NULL. * * * 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 x y r arc counter-clockwise by x,y of radius r * 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 for ever * 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 <signal.h> #include <math.h> #include <ctype.h> #include <time.h> #include "gen.h" /* general purpose definitions */ #include "ext.h" /* external variable definitions */ #include "init.h" /* just used for BITDIR definition */ #include "rast.h" /* raster file definitions */ #include "dev.h" /* typesetter and font descriptions */ #include "impcodes.h" /* Impress 2.0 opcode definitions */ #include "dimpress.h" /* defs just used by this program */ #include "spectab.h" /* * * A few names we'll need when we're printing files. devname[] is what troff * thought was going to be the output device while realdev[] is the name * of the printer we're really going to be using. There may be occasions when * we want to send the output to a given printer (*realdev) but we want to * use the raster tables that were built for a different one (*rastdev). If * rastdev isn't NULL we'll assume that this is the name of the device's * raster files that we want to use. In otherwords if *rastdev hasn't been * set by an option *realdev will be used in combination with *bitdir to * define *rastdir. * */ char devname[20] = ""; /* troff's target printer */ char *realdev = NULL; /* name of the printer we're really using */ char *rastdev = NULL; /* use raster tables made for this guy */ /* * * There's a bunch of stuff we'll want to know about the raster files we're * planning on using for this job. The most important, obviously is where * they're located. The routines that access the raster files all assume * they're in directory *rastdir, so it better be set up right before we * try to print anything. I've decided to have this program build up the * *rastdir string from *bitdir and either *rastdev or *realdev. All this * stuff will be done after the options are read. One of the reasons I'm * doing things this way is because we could want to do something like * print a troff file generated for the APS-5 on an Imprint-10 using the * raster files built for an 8/300. While that may sound rediculous the * raster tables can use up a lot of disk space and we may not want to * waste the space keeping complete sets of raster files for two or three * different devices. Anyway we should be able to do something reasonable * no matter what devices and raster files are requested. * * *bitdir is initialized to BITDIR (file init.h) and can be changed using * the -B option. The raster files for a particular device, say the 8/300 * which is being called i300, will be found in directory *bitdir/rasti300. * It's defined in file glob.c because resident font routines need the * directory. * * We'll also need to know the format of the raster files we're using before * we can have any characters printed. rastformat keeps track of which * style is being used. It's initialized in routine rastsetup() using * info in file *rastdir/RASTDATA - if it exists. Right now if rastsetup() * can't read the RASTDATA file it assumes that the raster files are the * old versetec format and have a resolution of 240 dots per inch. I've * done things this way just to make sure we can use the post-processor * with the old raster files without having to add an appropriate RASTDATA * file to the directory. Eventually it may not be a bad idea to just * quit if we can't read the file. * */ int rastformat; /* * * Although I originally had ideas about not using a RASTERLIST file, I now * think it's probably a pretty good idea to use one. We could get around the * missing size problems by linking raster files for unavailble sizes to ones * we really have. That approach would cost more in downloading glyphs, but * more we'd probably be able to place the glyphs a little better because * we'd be able to set the character advance separately. Anyway it's really * not all that clear which approach is better. I've allowed for use of a * RASTERLIST file (routine rastlist()) but I've also decided that if it's * not there we'll continue on with the program assuming we have every size * that jobs ask for. If the job asks for a non-existent file it will die * in routine getrastdata(). * * The following data structures are used to keep track of any RASTLIST * data we may have read. If maxrast is zero then there isn't any data * and we'll assume, when we do the lookups, that all fonts can be printed * in any size. * */ int maxrast = 0; /* number of RASTERLIST font entries */ Sizedata sizedata[MAXFONTS]; /* available raster size data */ /* * * Standard things needed after we've read troff's font and device tables. * The binary files are all built by makedev and will be looked for in the * target printer's dev directory located in *fontdir. Actually that's not * quite right. There are mapping problems, both for fonts and characters, * that will exist if we try and use font files for a device that don't * exactly map into the raster files that are being used. For example if * we used the character code field in the normal APS-5 font tables when * we print jobs generated for the APS-5 we'd be guaranteed to get garbage. * To get around that problem I have the post-processor first look in * *rastdir for the dev directory. If it's not there then we look in *fontdir. * */ struct dev dev; /* DESC.out starts this way */ struct Font *fontbase[NFONT+1]; /* starts each FONT.out file */ short *pstab; /* typesetter knows these sizes */ int nsizes = 1; /* sizes in *pstab list */ int nfonts; /* number of font positions */ int smnt; /* index of first special font */ int nchtab; /* number of special character names */ 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]; /* widtab would be a better name */ char *codetab[NFONT+1]; /* codes to get characters printed */ /* * * Variables that we use to keep track of troff's requests. All these * guys are set from values in the input files. * */ int size = 1; /* current size - internal value */ int font = 1; /* font position we're using now */ int hpos = 0; /* troff's current horizontal position */ int vpos = 0; /* same but vertically */ int lastw = 0; /* width of the last input character */ int lastc = 0; /* and its name (or index) */ /* * * We'll also want to keep track of some of the same things, but for the * target printer. * */ int lastsize = -1; /* last internal size we used */ int lastfont = -1; /* last font we told printer about */ int lastx = -1; /* printer's current position */ int lasty = -1; /* * * The following structure is used to keep track of the fonts that are * loaded in various positions. Both fields are filled in by routine t_fp() * using values in the binary font files. * */ struct { char *name; /* name of the font loaded here */ int number; /* its internal number */ } fontname[NFONT+1]; /* * * A few variables that are really just used for drawing things. Although * they're declared here most are only really used in draw.c. The pen * width and the step sizes can be changed by options. The step sizes DX * and DY control how far we move horizontally or vertically between * adjacent dots when drawing curves. They'll have no effect if we're * using Impress graphics commands for all graphics. Decreasing DX and DY * improves pictures but increases the size and complexity of the output * files. Because Impress complies and prints pages on the fly you may * not be able to draw fairly simple figures if the step sizes are too * small. * */ int drawdot = '.'; /* draw with this character */ int drawsize = 1; /* shrink size by this factor */ int penwidth = 1; /* use this as the pen diameter */ int DX = 6; /* horiz step when using drawdot */ int DY = 6; /* same but for vertical step */ /* * * We may want to shift things around on the output page a little. In * particular moving everything right on the output pages is normal because * the left 3/8 inch or so of each page on an Imprint-10 isn't visible. * All this stuff could be done more efficiently by defining a new * coordinate system to match the requested offsets rather than doing * the calculations each time. I've left things this way for now because * that's the way we originally did stuff in di10. * * The xoff and yoff values are in inches while the xoffset and yoffset * numbers are pixels. We'll set the pixel values after we're sure about * the target printer's resolution. xoff and yoff can be changed by options. * xfac and yfac are scaling factors used to convert coordinates in the * input files to appropiate values in the target printers coordinate * system. Their values are set in t_init() because we can only do it * properly after we've gotten the printer's resolution (from options) * and read the "x res" command in each input file. * */ float xoff; /* shift right by this many inches */ float yoff; /* and down by this much */ int xoffset; /* pixel values = xoff * pres */ int yoffset; /* = yoff * pres */ float xfac = 1.0; /* scaling factor for x */ float yfac = 1.0; /* scaling factor for y */ /* * * A few miscellaneous variable declarations. I've added landscape mode * to the post-processor. It's requested using the -l option. Might also * be reasonable to add a new device control command to request landscape * mode, although I haven't done it yet. None of this stuff will work with * printer resident fonts. * */ int mode = PORTRAIT; /* landscape or portrait mode */ int angle = ROT_0; /* ROT_270 for LNADSCAPE mode */ int output = 0; /* do we do output at all? */ int pageno = -1; /* output page number */ int copies = 1; /* ask IPR for this many */ float aspect = 1; /* default aspect ratio */ int cancenter = TRUE; /* try to improve char placement? */ int center = 0; /* for alphanumeric characters only */ /* * * We'll really need to deal with three possibly different resolutions * when we're printing files. The first is the resolution used by troff in * preparing the input file. Next is the resolution of the target printer. * Imprint-10s are 240 and 8/300s are 300 dots per inch. Finially we'll need * or at least want to know the resolution of the raster files we're using * to print the file. Obviously we'll get the best results when all three * numbers agree, but no matter what we should be able to do something * reasonable with any set of numbers. * */ int res; /* resolution assumed in input file */ int pres; /* resolution of the target printer */ int rres; /* raster file resolution */ /* * * The data about the different printers supported by the program is * stored in prdata[]. It's initialized using PR_INIT which is defined * in dimpress.h. The fields in structure Prdata are used to define the * printer name, resolution, x and y offsets (in inches), and the string * that should be used for *bitdir. * */ Prdata prdata[] = PR_INIT; /* printer data */ int pr_num = PR_NUM; /* printer we're using - index in prdata[] */ /* * * Many of the printer dependent variables can be set by several different * options. Most times we'll want to use the -T option, but there may be * occasions when we want to override one or more of the default values. * These variables keep track of whether we've tried to set values by other * options. * */ int setpenwidth = FALSE; /* changed by the -P option */ int setxoff = FALSE; /* -x option */ int setyoff = FALSE; /* -y option */ int setpres = FALSE; /* -r option */ /* * * A few variables that are really only used if we're doing accounting. * Much of the accounting stuff has been designed for our own use at * MHCC and may not suit your needs. * */ char *acctfile = NULL; /* append accounting record to this file */ char *jacctfile = NULL; /* just a page count for this job */ int acctpages = 0; /* charge for this many pages */ char *username = NULL; /* guy whose running this program */ /* * * If we're emulating another device we'll want to use the following array to * help map font names available on that device into PostScript fonts. It's * initialized using FONTMAP (file dpost.h) and may be changed in routine * getfontmap(). In particular if there's a file that matches the device name * in directory *fontdir/devpost/fontmaps, getfontmap() will use it instead of * the default - haven't implemented it and probably never will. * */ Fontmap fontmap[100] = FONTMAP; /* just for device emulation */ /* * * Output and accounting FILE definitions. If you invoke this program with * the -t option everything goes to stdout, otherwise it will be written * to *tempfile and then passed along to Imagen's spooler using IPR. * */ char *tempfile = NULL; /* output file name if no -t option used */ FILE *tf = NULL; /* and Impress output goes here */ FILE *fp_acct = stderr; /* accounting stuff written here */ extern int maxdots; /* defined and used in draw.c */ /*****************************************************************************/ main(agc, agv) int agc; char *agv[]; { /* * * Post-processor that's used to translate troff's device independent * output language into Impress (version 2.0 right now). A call to Imagen's * spooling package, through IPR, is made in print_file() provided * the output hasn't gone to stdout. Using the -t option or initializing * tf to be stdout forces output to stdout. If there are any processing * errors x_stat will be non-zero and done() will remove *tempfile before * quitting. * */ argc = agc; /* global so everyone can use them */ argv = agv; prog_name = argv[0]; /* just used for error messages */ init_signals(); /* sets up interrupt handling */ options(); /* command line options */ initialize(); /* must be done after options */ rastsetup(); /* set all the raster file stuff up */ rastlist(); /* get availble font and size list */ resfonts(tf); /* setup for any resident fonts */ acct_file(); /* open accounting file - maybe */ arguments(); /* translate all the input files */ t_wrapup(); /* mark the end of job for Impress */ print_file(); /* print the file we just built */ done(); /* everything probably went OK */ } /* End of main */ /*****************************************************************************/ init_signals() { int done(); /* handles them if we're catching sigs */ /* * * Makes sure we handle any interrupts properly. It wasn't done right * in di10. * */ if ( signal(SIGINT, done) == SIG_IGN ) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGHUP, SIG_IGN); } else { signal(SIGHUP, done); signal(SIGQUIT, done); } /* End else */ } /* End of init_signals */ /*****************************************************************************/ options() { int ch; /* option name returned from getopt */ char *names = "u:c:tx:y:r:a:o:p:LP:n:d:F:f:B:f:R:T:AJ:"; extern char *optarg; /* option argument set by getopt() */ extern int optind; /* * * Processes the command line options. The original stuff stuff done in * di10 has been changed a little. I've also used getopt() to scan the * options. * * The most confusing options are -B and -R. The *bitdir string is the * directory where we expect to find the raster file directories. They all * start with "rast" and end with a device name. I've got things set up so * that they're in /usr/lib/raster, but di10 looked for directory rasti10 * in *fontdir (/usr/lib/font/devi10). The -B option changes the value * of *bitdir. If you're running DWB and want this program to replace di10 * call it using the option "-B /usr/lib/font/devi10". The -R option * sets the value of string *rastdev. If that string isn't NULL then * "/rast*rastdev" will be appended to the end of *bitdir to locate the * raster file directory that we really want to use. By default rastdev is * NULL so *realdev will be used in its place. * * The -c option doesn't work right now. If you want to implement it you'll * probably just need to change the IPR system() call that's made in * print_file(). * */ while ( (ch = getopt(argc, argv, names)) != EOF ) { switch ( ch ) { case 'u': username = optarg; break; case 'c': copies = atoi(optarg); break; case 't': tf = stdout; break; case 'x': xoff = atof(optarg); setxoff = TRUE; break; case 'y': yoff = atof(optarg); setyoff = TRUE; break; case 'r': pres = atoi(optarg); setpres = TRUE; break; case 'a': aspect = atof(optarg); break; case 'o': out_list(optarg); break; case 'p': /* pixels of resolution */ if ( (DX = DY = atoi(optarg)) <= 0 ) DX = DY = 1; break; case 'L': /* landscape mode */ mode = LANDSCAPE; break; case 'P': /* pen diameter for drawing */ if ( (penwidth = atoi(optarg)) > 20 ) penwidth = 20; else if ( penwidth < 1 ) penwidth = 1; setpenwidth = TRUE; break; case 'n': /* a limit for drawing routines */ if ( (maxdots = atoi(optarg)) <= 0 ) maxdots = 32000; break; case 'd': if ( (debug = atoi(optarg)) == 0 ) debug = 1; tf = stdout; break; case 'F': /* font table directory */ fontdir = optarg; break; case 'B': /* rast* directories found here */ bitdir = optarg; break; case 'f': /* use this resident font file */ resfile = optarg; break; case 'R': /* use this guys raster files */ rastdev = optarg; break; case 'T': /* target printer */ for ( pr_num = 0; prdata[pr_num].prname != NULL; pr_num++ ) if ( strcmp(optarg, prdata[pr_num].prname) == 0 ) break; if ( prdata[pr_num].prname == NULL ) error(FATAL, "don't know printer %s", optarg); break; /* These options were added for the MHCC */ case 'A': x_stat |= DO_ACCT; break; case 'J': jacctfile = optarg; x_stat |= DO_ACCT; break; case '?': error(FATAL, ""); break; default: error(FATAL, "don't know option %s", argv[1]); break; } } argc -= optind; /* get ready for non-options args */ argv += optind; } /* End of options */ /*****************************************************************************/ initialize() { char *mktemp(); #ifdef V9 char *getlogin(); #endif /* * * Much of this stuff can only be done properly after the command line * options have been processed, so make sure it's called (from main()) * after options(). * */ #ifdef V9 if (username == NULL && (username = getlogin()) == NULL ) #endif #ifdef SYSV if ( (username = cuserid((char *) 0)) == NULL ) #endif username = "???"; if (tf != stdout) { tempfile = mktemp("/tmp/dimpXXXXX"); if ((tf = fopen(tempfile, "w")) == NULL) error(FATAL, "can't open temporary file %s", tempfile); } /* End if */ if ( realdev == NULL ) realdev = prdata[pr_num].prname; if ( setpres == FALSE ) pres = prdata[pr_num].pres; if ( setxoff == FALSE ) xoff = prdata[pr_num].xoff; if ( setyoff == FALSE ) yoff = prdata[pr_num].yoff; if ( setpenwidth == FALSE ) penwidth = prdata[pr_num].penwidth; if ( bitdir == NULL ) bitdir = prdata[pr_num].bitdir; if ( resfile == NULL ) resfile = prdata[pr_num].prname; res = rres = pres; /* just to be safe */ xoffset = pres * xoff; /* set the pixel offsets */ yoffset = pres * yoff; } /* End of initialize */ /*****************************************************************************/ rastsetup() { static char rastname[100]; /* rastdir string saved here */ char temp[100]; /* for pathnames and reads from *fp */ FILE *fp; /* RASTDATA and RASTLIST files */ int ch; /* just used to skip lines in *fp */ /* * * We'll need to know a bunch of stuff about the raster files before we can * actually use them. First of all we need to figure out where they are. * Strings *bitdir and either *rastdev or *realdev will be used to initialize * *rastdir. Once we've got rastname set up we'll need to determine the * format of the raster files and their resolution. All that stuff should * be in file *rastdir/RASTDATA. If the file's not there or we can't read * it we'll assume the files are written in the old format at a resolution * of 240 dots per inch. I've done things this way because RASTDATA is new * and won't be part of the of raster file directory unless you add it. * This way nothing has to be done to the old raster file directory. * */ sprintf(rastname, "%s/rast%s", bitdir, (rastdev == NULL) ? realdev : rastdev); rastdir = rastname; sprintf(temp, "%s/%s", rastdir, RASTDATA); if ( (fp = fopen(temp, "r")) == NULL ) { rastformat = OLD_FORMAT; rres = 240; return; } /* End if */ while ( fscanf(fp, "%s", temp) != EOF ) if ( strcmp(temp, "format") == 0 ) { fscanf(fp, "%s", temp); if ( strcmp(temp, "old") == 0 ) rastformat = OLD_FORMAT; else rastformat = RST_FORMAT; } else if ( strcmp(temp, "resolution") == 0 ) fscanf(fp, "%d", &rres); else while ( (ch = getc(fp)) != '\n' && ch != EOF ) ; fclose(fp); } /* End of rastsetup */ /*****************************************************************************/ rastlist() { int n; /* next size for current font */ int i; /* next size goes here */ char name[100]; /* pathname for RASTERLIST file */ FILE *fp; /* * * If we're not using the old format raster files we'll want to read the * list of available fonts and sizes in *rastdir. The file that's got all * the info is called RASTLIST (defined in init.h). The format is exactly * the same as Brian's original file. Under the old format all this stuff * is done in routine initfontdata(). * * For now if we can't open RASTERLIST and were not using the old format * raster files we'll just return to the caller. That really just means * we'll assume we can handle any font + size request that may come our * way. If that's not the case we better make up a complete RASTERLIST file. * */ if ( rastformat == OLD_FORMAT ) /* don't bother doing it here */ return; sprintf(name, "%s/%s", rastdir, RASTLIST); if ( (fp = fopen(name, "r")) == NULL ) return; /* name should be no more than L_FNAME characters */ while ( fscanf(fp, "%10s", sizedata[maxrast].name) != EOF ) { i = 0; while ( fscanf(fp, "%d", &n) != EOF && n < 100 ) sizedata[maxrast].sizes[i++] = n; sizedata[maxrast].sizes[i] = 999; if ( ++maxrast >= MAXFONTS ) error(FATAL, "too many fonts in %s", name); } /* End while */ fclose(fp); } /* End of rastlist */ /*****************************************************************************/ arguments() { FILE *fp; /* next input file */ /* * * All the rest of the command line options are input files we want to * translate. If we get here and there are no more arguments, or if '-' is * in the list of file names, we'll process 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); fclose(fp); argc--; argv++; } } /* End of arguments */ /*****************************************************************************/ print_file() { char buf[BUFSIZ]; /* IPR command line built up here */ /* * * Finished with all the input files and everything the printer needs to * know is in FILE *tf. We'll close the file because we're all done with * it, and then call IPR if we haven't been writing to stdout. * */ fclose(tf); /* everything's in the file */ if ( tf != stdout || debug ) { sprintf(buf, "%s -Limpress -r -o '-Downer \"'\"%s\"'\"'\", jobheader on, pagecollation on, pagereversal on\" %s 0</dev/null 1>/dev/null 2>&1 &", IPR, username, tempfile); if ( debug ) fprintf(stderr, "executing %s\n", buf); else system(buf); } /* End if */ } /* End of print_file */ /*****************************************************************************/ acct_file() { /* * * If we want to do accounting *acctfile will be the name of the file where * we put the accounting data. If for some reason we can't open the file * we'll just quit. I've called the routine from main() before anything is * written to the output file. * */ if ( acctfile != NULL && *acctfile != '\0' ) { if ( (fp_acct = fopen(acctfile, "a")) == NULL ) { fp_acct = stderr; x_stat |= NO_ACCTFILE; error(FATAL, "can't open accounting file"); exit(x_stat); } if ( tf != stdout ) x_stat |= DO_ACCT; } } /* End of acct_file */ /*****************************************************************************/ account() { FILE *f; /* just the job's page count */ /* * * Writes an accounting record, usually to *fp_acct, for the current job. * All this stuff is set up for our MHCC printers. In some cases we'll * know most everything about the job (called with -J option) and all * we'll do is write a page count to *jacctfile. In other cases we'll want * a more complete record and all the stuff will go to *fp_acct. You'll * undoubtedly want to change this stuff to suit your own needs. * */ if ( x_stat & DO_ACCT ) { if ( jacctfile == NULL || *jacctfile == '\0' ) { fprintf(fp_acct, " user = %-10s", username); fprintf(fp_acct, " paper = %-10d", acctpages * copies); x_stat &= ~DO_ACCT; fprintf(fp_acct, " exit_status = 0%-6o", x_stat); fprintf(fp_acct, " type = t "); if ( tf == stdout ) fprintf(fp_acct, " ??"); fprintf(fp_acct, "\n"); } else { if ( (f = fopen(jacctfile, "a")) != NULL ) { fprintf(f, "%d\n", acctpages); fclose(f); } x_stat &= ~DO_ACCT; } /* End else */ } } /* End of account */ /*****************************************************************************/ done() { /* * * Finished with everything so we want to make sure accounting is handled * properly and the right exit status is returned to the caller. We'll also * delete the temporary file if x_stat != 0. * */ account(); if ( tf != stdout && x_stat != 0 ) unlink(tempfile); exit(x_stat); } /* End of done */ /*****************************************************************************/ conv(fp) register FILE *fp; { register int c, k; int m, n, i, n1, m1; char str[100]; /* * * Controls the translation of troff's device independent output language * to Impress. Some of the commands, like slant and height, are no-ops * on Imagen's bitmap printers even though routines are called to do the * processing. * */ lineno = 1; /* line in current file */ x_stat |= FILE_STARTED; /* we'll clear it when done with *fp */ 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); oput(m); break; case 'D': /* drawing function */ lineno++; switch ((c=getc(fp))) { case 'p': /* draw a path */ while (fscanf(fp, "%d %d", &n, &m) == 2) drawline(n, m); 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': /* arc */ fscanf(fp, "%d %d %d %d", &n, &m, &n1, &m1); drawarc(n, m, n1, m1); break; case 'q': /* spline */ drawspline(fp, 1); break; case '~': /* wiggly line */ drawspline(fp, 2); break; default: error(FATAL, "unknown drawing function %c", c); break; } 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 */ /* * * The simple scan I've commented out didn't handle negative numbers right * and believe it or not we did get jobs that asked for negative absolute * positions. Even though negative absolute coordinates probably are a * mistake it makes more sense to handle the numbers properly. * while ((c = getc(fp)) == ' ') ; k = 0; do { k = 10 * k + c - '0'; } while (isdigit(c = getc(fp))); ungetc(c, fp); hgoto(k); * */ fscanf(fp, "%d", &n); hgoto(n); break; case 'h': /* relative horizontal motion */ /* * * Again the same potential problem with negative numbers exists here. In * fact it makes a lot more sense to expect negative relative motions even * though I never did see them. Anyway just to be safe I've made the same * change - take it out if you want. * while ((c = getc(fp)) == ' ') ; k = 0; do { k = 10 * k + c - '0'; } while (isdigit(c = getc(fp))); ungetc(c, fp); hmot(k); * */ 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); break; default: error(!FATAL, "unknown input character %o %c", c, c); done(); } } x_stat &= ~FILE_STARTED; } /* End of conv */ /*****************************************************************************/ devcntrl(fp) FILE *fp; /* current input file */ { char str[20], str1[50], buf[50]; int c, n, x, y; /* * * Called from conv() to process the rest of a device control function. * There's a whole family of them and they all begin with the string * "x ". The rest of the command consists of the function name followed * by zero or more arguments that depend on the particular command. We've * already read the "x" from the input file, that's why the routine was * called. The whole rest of the input line is assumed to be part of the * device control command. * */ fscanf(fp, "%s", str); /* get the control function name */ switch ( str[0] ) { /* only the first character counts now */ case 'i': /* initialize */ fileinit(); t_init(0); break; case 'T': /* device name */ fscanf(fp, "%s", devname); 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, 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 'I': case 'B': x = hpos * xfac + xoffset + .5; y = vpos * yfac + yoffset + .5; /* force 32x32 alignment, imagen 3.2 software does this ** anyway, but this should fix others */ putc(ASETAV, tf); putint(y&~0x1f, tf); putc(ASETAH, tf); putint(x&~0x1f, tf); if (str[0] == 'I') { fscanf(fp, "%d %s", &n, buf); iglyphpage(tf, buf, n, x&0x1f, y&0x1f); } else if (str[0] == 'B') { fscanf(fp, "%d %s", &n, buf); glyphpage(tf, buf, n, x&0x1f, y&0x1f); } /* position back where it belongs */ putc(ASETAV, tf); putint(y, tf); lasty = y; putc(ASETAH, tf); putint(x, tf); lastx = x; break; case 'X': fscanf(fp, "%s", str); /* line style */ break; } while ( (c = getc(fp)) != '\n' && c != EOF ) ; lineno++; } /* End of devcntrl */ /*****************************************************************************/ fileinit() { int i, fin; char *filebase; char temp[100]; /* * * Called from t_init(), which in turn is called from devcntrl(), whenever we get * an "x init" command. There are a few lines of code here that set things up for * emulating another device. * */ 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 = dev.nfonts; if ( strcmp(devname, realdev) != 0 ) { /* device emulation */ close(fin); strcpy(devname, realdev); 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)); } /* End if */ nsizes = dev.nsizes; nchtab = dev.nchtab; if ( (filebase = malloc(dev.filesize)) == NULL ) error(FATAL, "no memory for description file"); read(fin, filebase, dev.filesize); /* all at once */ pstab = (short *) filebase; chtab = pstab + nsizes + 1; chname = (char *) (chtab + dev.nchtab); for ( i = 1; i <= nfonts; i++ ) { fontbase[i] = NULL; widthtab[i] = codetab[i] = fitab[i] = NULL; } /* End for */ close(fin); } /* End of fileinit */ /*****************************************************************************/ fontprint(i) int i; /* font's index in fontbase[] */ { int j, n; char *p; /* * * Just a debugging routine that dumps most of the important information about * the font that's mounted in position i. * */ fprintf(tf, "font %d:\n", i); p = (char *) fontbase[i]; n = fontbase[i]->nwfont & BMASK; /* name should be no more than L_FNAME characters long */ fprintf(tf, "base=0%o, nchars=%d, spec=%d, name=%.10s, 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 */ /*****************************************************************************/ loadfont(n, s, s1) int n; char *s, *s1; { char temp[80]; int fin, nw; /* * * Called to to load font info for font *s on position n using the binary tables * located in directory *s1 (if it's not NULL or the empty string). * */ if ( n < 0 || n > NFONT ) /* make sure it's a legal position */ error(FATAL, "illegal fp command %d %s", n, s); /* * * If the font's already loaded on position n there's nothing to do. * */ if ( fontbase[n] != NULL && strncmp(s, fontbase[n]->namefont, L_FNAME) == 0 ) return; /* * * If *s1 isn't NULL or the NULL string that's what we'll use for the font * directory, otherwise use *fontdir. * */ if ( s1 == NULL || s1[0] == '\0' ) sprintf(temp, "%s/dev%s/%.10s.out", fontdir, devname, s); else sprintf(temp, "%s/%.10s.out", s1, s); if ( (fin = open(temp, 0)) < 0 ) { sprintf(temp, "%s/dev%s/%.10s.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(3*255 + dev.nchtab + (128-32) + sizeof(struct Font)); if ( fontbase[n] == NULL ) error(FATAL, "Out of space in loadfont %s", s); read(fin, fontbase[n], 3*255 + nchtab+128-32 + sizeof(struct Font)); 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 */ /*****************************************************************************/ char *mapfont(name) char *name; /* name of the font troff wants */ { int i; /* loop index for fontmap[] */ /* * * Only called from loadfont() when we haven't been able to open the font file * that troff asked for. We take the font name and map it into an appropriate * substitute. If the initial lookup fails we do a simple mapping that should * be good enough most of the time. * * This stuff should only be needed when we're emulating another printer like * the APS-5. * */ for ( i = 0; fontmap[i].name != NULL && i < L_FNAME; i++ ) if ( strncmp(name, fontmap[i].name, L_FNAME) == 0 ) return(fontmap[i].use); switch ( *++name ) { case 'I': case 'B': return(name); case 'X': return("BI"); default: return("R"); } /* End switch */ } /* End of mapfont */ /*****************************************************************************/ convint(c) unsigned char c; { /* * * Converts a character to a proper 2's complement integer. Really just used * on 3b's when we're reading raster tables in the old format. There's * really no need to make this a separate routine - at least I don't * think there is. Should just redefine CONVINT() macro so it does this * stuff. * */ return((c & 0200) ? ((256 - c) * -1) : c); } /* End of convint */ /*****************************************************************************/ t_init(reinit) int reinit; { int i; FILE *tmpfile(); /* * * Called to initialize the printer and associated variables. reinit will * be zero when the "x init" command was found in the output file, while * it will be non-zero when the routine is called to set things up for a * new page. * */ if (! reinit) { for (i = 0; i < nchtab; i++) if (strcmp(&chname[chtab[i]], "l.") == 0) break; if (i < nchtab) { drawdot = i + 128; drawsize = 1; } else { drawdot = '.'; drawsize = 2; /* half size */ } xfac = (float) pres / res * aspect; yfac = (float) pres / res; putc(ASETPEN, tf); putc(penwidth, tf); if ( mode == LANDSCAPE ) { putc(ASETHV, tf); putc(0107, tf); angle = ROT_270; } /* End if */ } hpos = vpos = 0; setsize(t_size(10)); /* start somewhere */ } /* End of t_init */ /*****************************************************************************/ t_page(pg) { register int i, j, n; register unsigned char *p; /* * * Get the printer and everything else ready for a new page. If the -o * option has been used only selected pages will be printed. If output * is ON when this routine is called we'll write the endpage Impress * command to the output file. If we're supposed to be doing output for * page pg we'll write the APAGE command out, and that will cause the * horizontal and vertical positions (in Impress) to be set to zero. hpos * and vpos are initialized in routine t_init(), although I really don't * see any reason why we couldn't just skip the call and do it here. * */ if ( debug == ON ) fprintf(stderr, "t_page %d, output=%d\n", pg, output); pageno = pg; if ( output == ON ) /* doing output for last page */ putc(AENDP, tf); if ( (output = in_olist(pg)) == ON ) { /* print this page */ putc(APAGE, tf); acctpages++; } lastx = lasty = -1; t_init(1); /* setup for this page */ } /* End of t_page */ /*****************************************************************************/ t_newline() { /* * * Read an "n a b" command in the input file and are ready to start a new * line. There really isn't much else this routine can do besides set the * horizontal position to zero. Anyway troff takes care of any needed * positioning before it starts printing text. * */ hpos = 0; } /* End of t_newline */ /*****************************************************************************/ t_size(n) int n; /* convert this point size */ { int i; /* * * Converts a point size into an internal size that can be used as an * index into the pstab[] array. Actually the internal size is defined as * 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 */ /*****************************************************************************/ t_charht(n) int n; /* use this as the character height */ { /* * * Although it can be done on some typesetters, like the APS-5, it's ignored * on Imagen's bitmap printers like the Imprint-10 and the 8/300. It would * be absurd to keep any extra raster files around just so we could implement * this command, and right now there's no Impress command to do it to * downloaded glyphs. * */ } /* End of t_charht */ /*****************************************************************************/ t_slant(n) int n; /* slant characters this many degrees */ { /* * * As with troff's height command, there's no simple way to slant characters * on Imagen's bitmap printers and it's really not worth any effort to try * and make it work. * */ } /* End of t_slant */ /*****************************************************************************/ t_font(s) char *s; { int n; /* * * Just converts the string *s into an integer and checks to make sure the * number is a legal font position. If it's not we'll print an error message * and then quit. di10 handled bad requests a little differently. Instead * of treating mistakes as FATAL errors it would return 1 as the font * number. * */ if ( (n = atoi(s)) < 0 || n > nfonts ) error(FATAL, "illegal font position %d", n); return(n); } /* End of t_font */ /*****************************************************************************/ t_reset(c) int c; /* pause or restart */ { /* * * Called from devcntrl() when we've found an "x stop" or "x pause" command. * There's really nothing we need to do for Imagen's bitmap printers, and * leaving the final cleanup (ie. AEOF code etc.) to someone else means * we should be able to cat a bunch of troff output files together and * have them all printed properly. * */ } /* End of t_reset */ /*****************************************************************************/ t_wrapup() { /* * * We're finished with all the input files and all that's left to do is * end the last page we were working on and then mark the end of the Impress * file. * */ putc(AENDP, tf); putc(AEOF, tf); } /* End of t_wrapup */ /*****************************************************************************/ t_trailer() { /* * * Called from devcntrl() when an "x trailer" command is found. There's * nothing we need to do here for Imagen's printers. * */ } /* End of t_trailer */ /*****************************************************************************/ hgoto(n) int n; /* where we want to be */ { /* * * Want to be at this absolute horizontal position next - usually will * get these motion commands right before printing a character. * */ hpos = n; } /* End of hgoto */ /*****************************************************************************/ hmot(n) int n; /* move this far from where we are */ { /* * * Handles relative horizontal motion. troff's current positon as recorded * in hpos is changed by n units. * */ hpos += n; } /* End of hmot */ /*****************************************************************************/ vgoto(n) int n; /* new vertical position */ { /* * * Moves vertically in troff's coordinate system to absolute position n. * */ vpos = n; } /* End of vgoto */ /*****************************************************************************/ vmot(n) int n; /* move this far vertically */ { /* * * Handles relative vertical motion of n units in troff's coordinate system. * */ vpos += n; } /* End of vmot */ /*****************************************************************************/ put1s(s) register char *s; { static int i = 0; /* last one we found - usually */ /* * * Does whatever is necessary to have special character *s printed. If we're * doing output on this page we'll look the character up using chname[] and * chtab[]. The first array contains all the special character names * separated by '\0' that are mentioned in the typesetter's DESC file, while * chname[i] points to the start of character i in chname[]. Both arrays * come from the DESC.out file for printer *devname which is found in * *fontdir (wherever that might be). * */ if ( output == OFF ) return; if ( debug ) printf("%s", s); 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 */ /*****************************************************************************/ put1(c) register int c; /* print this character */ { char *pw; register char *p; register int i, j, k; int ofont, code; /* * * Arranges to have character c printed. If c < 128 it's a simple ASCII character, * otherwise it's a special character. We subtract 32 from c because non-graphic * ASCII characters aren't included in the tables. * */ lastc = c; /* charlib() may need the real name */ c -= 32; if ( c <= 0 ) { if ( debug == ON ) fprintf(tf, "non-exist 0%o\n", lastc); return; } /* End if */ k = ofont = font; i = fitab[font][c] & BMASK; if ( i != 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 || k > nfonts ) { if ( debug == ON ) fprintf(tf, "not found 0%o\n", lastc); if ( font != ofont ) setfont(ofont); return; } /* End if */ lastw = ((pw[i] & BMASK) * pstab[size-1] + dev.unitwidth/2) / dev.unitwidth; if ( debug == ON ) { if ( isprint(lastc) ) fprintf(tf, "%c %d\n", lastc, code); else fprintf(tf, "%03o %d\n", lastc, code); } else oput(code); if ( font != ofont ) setfont(ofont); } /* End of put1 */ /*****************************************************************************/ setsize(n) int n; /* new internal size */ { /* * * We want to use internal size n for now, where n is an index into *pstab * where we can find the closest available approximation to the requested * point size. * */ size = n; } /* End of setsize */ /*****************************************************************************/ t_fp(n, s, si) int n; /* font position to update */ char *s; /* it now contains this font */ char *si; /* and this is its internal number */ { /* * * Called when we've loaded font *s in position n. The font position info * is recorded in fontname[]. * */ fontname[n].name = s; fontname[n].number = atoi(si); if ( n == lastfont ) lastfont = -1; /* force getfontdata() call */ } /* End of t_fp */ /*****************************************************************************/ setfont(n) int n; /* this will be the current font */ { /* * * Job now wants to use the font loaded in position n. The variable font * keeps track of which one we're currently using. If the requested positon * is out of range we'll just quit. * */ if ( output == OFF ) return; if (n < 0 || n > NFONT) error(FATAL, "illegal font %d", n); font = n; } /* End of setfont */ /*****************************************************************************/ oput(c) int c; /* want to print this character */ { int x, y; /* current scaled position */ int member; /* glyph number for c in current family */ /* * * Arranges to print the character whose code is c in the current font. * The old and new raster file formats are supported. * */ if ( output == OFF ) return; if ( rastformat == OLD_FORMAT ) { xychar(c, fontname[font].name, pstab[size-1], lastw, tf); return; } /* End if */ if ( font != lastfont || size != lastsize ) { if ( isresident(fontname[font].name) == FALSE ) getrastdata(fontname[font].name, mapsize(fontname[font].name, pstab[size-1])); else getresdata(fontname[font].name, mapsize(fontname[font].name, pstab[size-1]), tf); lastfont = font; lastsize = size; } /* End if */ member = download(c, lastw, angle, tf); x = hpos * xfac + xoffset + .5; y = vpos * yfac + yoffset + .5; if ( y != lasty ) { putc(ASETAV, tf); putint(y, tf); lasty = y; } /* End if */ if ( ABS(x - lastx) > SLOP ) { putc(ASETAH, tf); putint(x, tf); lastx = x + fam->advance[member]; } else lastx += fam->advance[member]; putc(member, tf); } /* End of oput */ /*****************************************************************************/ mapsize(name, ps) char *name; /* name of the font */ int ps; /* point size troff wants to use */ { int i, j; /* * Uses the data that's been filled in from RASTLIST to map point size * ps font font *name into the best size available in our raster tables. * If no RASTLIST file was used rastsize will be zero and we'll just * assume that any size that's aske for is available. */ if ( maxrast == 0 ) /* don't have any size data */ return(ps); for ( i = 0; i < maxrast; i++ ) if ( strncmp(name, sizedata[i].name, L_FNAME) == 0 ) break; if ( i >= maxrast ) /* didn't find the font */ error(FATAL, "missing raster list data for font %s", name); for ( j = 1, ps = (ps * pres) / rres; ps >= sizedata[i].sizes[j]; j++ ) ; return(sizedata[i].sizes[--j]); } /* End of mapsize */ /*****************************************************************************/