/*
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred."
 *
 */
/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"$Header: dimpress.c 1.2 89/10/10 $@(#)roff:troff.d/devi10/dimpress.c	2.12"
char *xxxvers = "@(#)roff:troff.d/devi10/dimpress.c	2.12";

/*
 *
 * 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 (exactly 2 digits!), then print c
 * 		(this wart is an optimization that shrinks output file size
 * 		 about 35% and run-time about 15% while preserving ascii-ness)
 * w	paddable word space - no action needed
 * nb a	end of line (information only -- no action needed)
 * 	b = space before line, a = after
 * pn	begin page n
 * #...\n	comment
 * 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 u v	arc counter-clockwise from here to u,v from center
 * 				with center x,y from here
 * 	D~ x y x y ...	wiggly line by x,y then x,y ...
 * 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 character 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.
 *
 * 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.
 *
 */


char		*bitdir = NULL;		/* rast* directories found here */
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 */

struct  {

	char	name[4];		/* name of the font */
	int	sizes[20];		/* available point sizes for this font */

} rastdata[MAXRAST];


/*
 *
 * 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 */


/*
 *
 * 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 = 3;			/* horiz step when using drawdot */
int		DY = 3;			/* 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.
 *
 */


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 */


/*
 *
 * 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 */
	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 = "c:tx:y:a:o:p:P:n:d:r:F:f:B:R:T:AJ:#";
	int		i, sharpsign;	/*for debugging options. MRW*/

	extern char	*optarg;	/* option argument set by getopt() */
	extern int	optind;
	extern int	nolist;
	extern int	olist[];


/*
 *
 * 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 by default). The -B option changes the value
 * of *bitdir. If you're running DWB and want this program to replace di10
 * make sure you call it using with the option "-B /usr/lib/font". 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 '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 '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 */
		case 'f':
			bitdir = 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;

		case '#':			/*MRW debug option*/
			sharpsign = 1;
			break;

		default:
			error(FATAL, "don't know option %s", argv[1]);
			break;
		}
	}

	argc -= optind;			/* get ready for non-options args */
	argv += optind;

	if (sharpsign == 1) {
		fprintf(stderr, "copies = %d, DX, DY = %d, %d, x_stat = %o\n",
			copies, DX, DY, x_stat);
		fprintf(stderr, "xoff=%g, yoff=%g, setxoff=%d, setyoff=%d\n",
			xoff, yoff, setxoff, setyoff);
		fprintf(stderr, "aspect = %g, penwidth = %d, maxdots = %d\n",
			aspect, penwidth, maxdots);
		fprintf(stderr, "fontdir = %s, bitdir = %s, rastdev = %s\n",
			fontdir, bitdir, rastdev);
		fprintf(stderr, "debug = %d, pr_num = %d\n",
			debug, pr_num);
		for (i=0; i<nolist; i+=2)
			fprintf (stderr, "olist[%d] is %d; olist[%d] is %d.\n",
				i, olist[i], i+1, olist[i+1]);
		if (tf != stdout)
			fprintf(stderr, "tf is %x\n", tf);
	}
}   /* End of options */


/*****************************************************************************/


initialize()


{


	char	*mktemp();


/*
 *
 * 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().
 *
 */


	if ( (username = cuserid((char *) 0)) == NULL )
		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;

	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;

	maxrast = 0;
	while ( fscanf(fp, "%s", rastdata[maxrast].name) != EOF )  {
		i = 0;
		while ( fscanf(fp, "%d", &n) != EOF  &&  n < 100 )
			rastdata[maxrast].sizes[i++] = n;
		rastdata[maxrast].sizes[i] = 999;
		if ( ++maxrast >= MAXRAST )
			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], buf[300];


/*
 *
 * 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 'D':		/* drawing function */
			fgets(buf, sizeof(buf), fp);
			lineno++;
			switch (buf[0]) {
			case 'l':	/* draw a line */
				sscanf(buf+1, "%d %d", &n, &m);
				drawline(n, m);
				break;

			case 'c':	/* circle */
				sscanf(buf+1, "%d", &n);
				drawcirc(n);
				break;

			case 'e':	/* ellipse */
				sscanf(buf+1, "%d %d", &m, &n);
				drawellip(m, n);
				break;

			case 'a':	/* arc */
				sscanf(buf+1, "%d %d %d %d", &n, &m, &n1, &m1);
				drawarc(n, m, n1, m1);
				break;

			case '~':	/* wiggly line */
				drawwig(buf+1);
				break;

			default:
				error(FATAL, "unknown drawing function %s", buf);
				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;


/*
 *
 * Called form 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;
	}

	while ( (c = getc(fp)) != '\n'  &&  c != EOF ) ;

	lineno++;

}   /* End of devcntrl */


/*****************************************************************************/


fileinit()


{


	int	i, fin, nw;
	char	*malloc(), *filebase, *p;
	char	temp[60];

/*
 *
 * Called from devcntrl() when we get an "x init" command. There's really
 * no reason this routine should be called more than once for each input
 * file.
 *
 * The font and description files are looked for in two separate places.
 * The normal troff font directory is the last place we look. Although
 * ideally troff and the post-processor should read the same font and
 * device tables I've often found it useful to have them read related
 * but slightly different binary files.
 *
 * It wasn't all that clear where we should put the extra font files.
 * One choice and probably a pretty good convention would be to make
 * dev directories in the target printer's normal font directory. For
 * example if the input files were prepared for the APS-5 and we wanted
 * to print them on an 8/300 then we'd first look for a directory called
 * *fontdir/devaps/devi300. If it existed we'd use the binary files that
 * we found there, but if it didn't exist we'd use the normal APS-5
 * tables in *fontdir/devaps. That seemed like a good convention but
 * we need two different sets of tables so we can access the right
 * characters in the raster files we're using. In otherwords the extra
 * font directories are really related to the raster files that are
 * being used, rather than the device that we're using to print the
 * files. Adopting that convention would mean that we really could only
 * use one set of raster files. To avoid that restriction (not really
 * sure why) I decided to hide the extra font files in *rastdir/dev*.
 *
 */


	sprintf(temp, "%s/dev%s/DESC.out", rastdir, devname);
	if ((fin = open(temp, 0)) < 0)  {
		sprintf(temp, "%s/dev%s/DESC.out", fontdir, devname);
		if ((fin = open(temp, 0)) < 0)
			error(FATAL, "can't open tables for %s", temp);
	} else fontdir = rastdir;

	read(fin, &dev, sizeof(struct dev));

	nfonts = dev.nfonts;
	nsizes = dev.nsizes;
	nchtab = dev.nchtab;

	if ( dev.spare2 <= 0  ||  dev.spare2 > nfonts )
		dev.spare2 = nfonts;

	if ((filebase = malloc(dev.filesize)) == NULL)	/* enough room for whole file */
		error(FATAL, "not enough space for %s", temp);
	read(fin, filebase, dev.filesize);	/* all at once */

	pstab = (short *) filebase;
	chtab = pstab + nsizes + 1;
	chname = (char *) (chtab + dev.nchtab);

	p = chname + dev.lchname;		/* default fonts start here */

	for (i = 1; i <= nfonts; i++) {
		fontbase[i] = (struct Font *) p;
		nw = *p & BMASK;		/* width count comes first */

		if ( i <= dev.spare2 )  {	/* it's a normal font */
			fontbase[i] = NULL;
			widthtab[i] = codetab[i] = fitab[i] = NULL;
			p += 3 * nw + dev.nchtab + 128 - 32 + sizeof(struct Font);
			continue;
		}   /* End if */

		p += sizeof(struct Font);	/* font structure written first */
		widthtab[i] = p;		/* then the width table */
		codetab[i] = p + 2 * nw;	/* skip kern to get code table */
		fitab[i] = p + 3 * nw;		/* font's index table */

		p += 3 * nw + dev.nchtab + 128 - 32;	/* next font starts here */

		t_fp(i, fontbase[i]->namefont, fontbase[i]->intname);

		if ( debug ) fontprint(i);
	}	/* End for */

	close(fin);

}   /* End of fontinit */


/*****************************************************************************/


fontprint(i)


	int	i;			/* font's index in fontbase[] */


{


	int	j, k, n;
	char	*p;


/*
 *
 * Just a debugging routine that dumps most of the improtant information
 * about the font that's mounted in position i. Stuff probably should have
 * been written to stderr instead.
 *
 */


	printf("font %d:\n", i);

	p = (char *) fontbase[i];
	n = fontbase[i]->nwfont & BMASK;

	printf("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]);

	printf("widths:\n");
	for (j=0; j <= n; j++) {
		printf(" %2d", widthtab[i][j] & BMASK);
		if (j % 20 == 19) printf("\n");
	}

	printf("\ncodetab:\n");
	for (j=0; j <= n; j++) {
		printf(" %2d", codetab[i][j] & BMASK);
		if (j % 20 == 19) printf("\n");
	}

	printf("\nfitab:\n");
	for (j=0; j <= dev.nchtab + 128-32; j++) {
		printf(" %2d", fitab[i][j] & BMASK);
		if (j % 20 == 19) printf("\n");
	}

	printf("\n");

}   /* End of fontprint */


/*****************************************************************************/


loadfont(n, s, s1)


	int	n;
	char	*s, *s1;


{


	char	temp[60];
	int	fin, nw, norig;


/*
 *
 * 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 && strcmp(s, fontbase[n]->namefont) == 0)
		return;

/*
 *
 * If *s1 isn't NULL or the NULL string that's what we'll use for the
 * font directory, otherwise use *fontdir. I don't think anyone ever uses
 * this feature.
 *
 */

	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) {
		error(!FATAL, "can't open font table %s", temp);
		return;
	}

	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 > 1) fontprint(n);

}   /* End of loadfont */


/*****************************************************************************/


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;

#ifdef	IMPDRAW
		putc(ASETPEN, tf);
		putc(penwidth, tf);
#endif
	}

	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, w;
	int		num, l;


/*
 *
 * Arranges to have character c printed. If c < 128 it's a simple ASCII
 * character, otherwise it represents one of troff's special characters.
 * Since we omit non-graphic ASCII characters we subtract 32 from c before
 * we do any lookups.
 *
 */


	if ( output == OFF )
		return;

	if ( cancenter == TRUE )
		center = isascii(c) && isalnum(c);

	c -= 32;
	if (c <= 0) {
		if (debug) printf("non-exist 0%o\n", c+32);
		return;
	}
	k = ofont = font;
	i = fitab[font][c] & BMASK;

/*
 *
 * The next few lines were added to handle the lookups on the extra
 * default fonts.
 *
 */

	num = fontname[font].number;
	l = num & BMASK;
	while ( i == 0 && l <= nfonts && fontname[l].number == num )
		if ( (i = fitab[l][c]) != 0 )  {
			if ( debug > 2 )  {
				fprintf(stderr, "found 0%o on font %d\n", c, l);
			}
			if ( strcmp(fontname[font].name, fontname[l].name) == 0 )  {
				buildchar(c, l);
				return;
			}    /* End if */
			setfont(l);
		} else l++;

	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;
			}
		}
	}
	if (i == 0 || (code = p[i] & BMASK) == 0 || k > nfonts) {
		if (debug) printf("not found 0%o\n", c+32);
		if ( font != ofont ) setfont(ofont);
		return;
	}
	lastw = pw[i] & BMASK;
	lastw = (lastw * pstab[size-1] + dev.unitwidth/2) / dev.unitwidth;
	if (debug) {
		if (isprint(c+32))
			printf("%c %d\n", c+32, code);
		else
			printf("%03o %d\n", c+32, code);
	} else
		oput(code);
	if (font != ofont)
		setfont(ofont);

}   /* End of put1 */


/*****************************************************************************/


buildchar(c, f)


	int	c;			/* build this character up */
	int	f;			/* it's on this font - internal num */


{


	char	ch[10];			/* character string for c goes here */
	int	ohpos, ovpos;		/* position right now */
	int	cwidth;			/* c's width from font f */
	int	pos;			/* c's position on this font */
	int	i;			/* for loop index */


/*
 *
 * Character c on font f isn't in any of the raster files so we'll try and
 * build it up out of characters that are available using commands in file
 * spectab.h. All this stuff is kind of dumb and I didn't do things all
 * that well, but it seems to work for know so I'll leave it be.
 *
 */


	ohpos = hpos;			/* save the current position */
	ovpos = vpos;
	pos = fitab[f][c] & BMASK;	/* c's position on font f */
	cwidth = widthtab[f][pos] & BMASK;
	cwidth = (cwidth * pstab[size-1] + dev.unitwidth/2) / dev.unitwidth;

	fontname[f].number++;		/* prevent recursion for now */
	c += 32;

	if ( c < 128 )  {		/* was a single ASCII character */
		ch[0] = c;		/* so build up the ch[] string */
		ch[1] = '\0';
	} else strcpy(ch, &chname[chtab[c-128]]);

	for ( i = 0; spectab[i] != 0; i += 2 )
		if ( strcmp(spectab[i], ch) == 0 )  {
			dostring(spectab[i+1], cwidth);
			break;
		}    /* End if */

	if ( hpos != ohpos )  hgoto(ohpos);	/* back to original position */
	if ( vpos != ovpos )  vgoto(ovpos);

	lastw = cwidth;
	fontname[f].number--;

}    /* End of buildchar */


/*****************************************************************************/


dostring(str, cwidth)


	char	*str;			/* control string for current char */
	int	cwidth;			/* char's width from font tables */


{


	int	hstep, vstep;		/* move this far from current pos */
	char	c;			/* character that is printed next */


/*
 *
 * Reads commands from spectab.h and uses them to build character ch. Again
 * things could have been done better but I'm not going to change it right
 * now.
 *
 */


	if ( debug > 2 )
		fprintf(stderr, "cwidth = %d\n", cwidth);

	while ( c = *str++ )  {
		if ( debug > 2 )
			fprintf(stderr, "string = %s\n", str);
		if ( c == '(' )  {	/* have a relative motion */
			hstep = cwidth * (atoi(str) / 100.0);
			while ( *str++ != ',' ) ;
			vstep = cwidth * (atoi(str) / 100.0);
			while ( *str++ != ')' ) ;
			if ( hstep != 0 )  hmot(hstep);
			if ( vstep != 0 )  vmot(vstep);
			c = *str++;
		}    /* End if */
		if ( debug > 2 )  {
			fprintf(stderr, "char = %c (0%0), ", *str, *str);
			fprintf(stderr, "hstep = %d, vstep = %d, ", hstep, vstep);
			fprintf(stderr, "hpos = %d, vpos = %d\n", hpos, vpos);
		}    /* End if */
		if ( c != '\0' )  put1(c);
	}    /* End while */

}    /* End of dostring */


/*****************************************************************************/


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 )  {
		getrastdata(fontname[font].name, mapsize(fontname[font].name, pstab[size-1]));
		lastfont = font;
		lastsize = size;
	}   /* End if */

	member = download(c, lastw, 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 ( strcmp(name, rastdata[i].name) == 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 >= rastdata[i].sizes[j]; j++ ) ;

	return(rastdata[i].sizes[--j]);

}	/* End of mapsize */


/*****************************************************************************/


