/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 1994 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */

/*	Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T	*/
/*	  All Rights Reserved  	*/


/*	from OpenSolaris "dpost.c	1.11	05/06/08 SMI"	 SVr4.0 1.2		*/

/*
 * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany
 *
 * Sccsid @(#)dpost.c	1.176 (gritter) 8/19/08
 */

/*
 *
 * dpost - troff post-processor for PostScript printers.
 *
 * A program that translates output generated by the device independent troff
 * into PostScript. Much was borrowed from dimpress and dps (formally dlzw),
 * and even though the code has been changed, credit has to be given to Richard
 * Flood for his early work on the PostScript driver.
 *
 * Among the most interesting new features are color support (see devcntrl() and
 * file color.c) and code to handle complex paths pieced together using any of the
 * standard drawing commands (see devcntrl() and file draw.c). Reverse video mode
 * has also been included as a special case of the color support. Two encoding
 * schemes based on widthshow are also new additions. The safe one is obtained when
 * you set encoding to 2 (eg. using the -e2 option). The slightly faster method
 * is obtained by setting encoding to 3 (eg. using the -e3 option), although it's
 * not recommended. Rounding errors in character widths can accumulate and become
 * quite noticeable by the time you get to the right margin. More often than not
 * you end up getting a ragged right margin.
 *
 * The program handles files formatted for any device, although the best and
 * most efficient output is generated when the font and description files match
 * PostScript's resident fonts. Device emulation is relatively expensive, and
 * can produce output files that are more than twice the size of the input files.
 * In most cases output files will be smaller than input files, perhaps by up to
 * 40 percent, although the results you get depend on what you're doing and the
 * text encoding you're using. You'll get the worst results if you're emulating
 * another device, using special bitmap characters, like the logo, or doing lots
 * of vertical motion or drawing.
 *
 * PostScript fonts don't support all of troff's characters, so some have to
 * be built by special PostScript procedures. Those routines can be found in
 * *fontdir/devpost/charlib, and are only used when we try to print a character
 * that has been assigned a code less than 32. Definitions are only made the
 * first time each character is used. Subsequent requests to print the character
 * only generate a call to the PostScript procedure that's been copied to the
 * output file. For example you'll find a file called sq in directory
 * *fontdir/devpost/charlib. It defines a PostScript procedure called build_sq
 * that's called whenever we need to print a square. Special characters that
 * have been assigned a code of 2 are expected to come in two pieces. The
 * definition part and bitmap part (or whatever). The definition is only made
 * once, but the contents of the character's .map file are copied to the output
 * file each time, immediately after charlib() generates the call to the
 * PostScript procedure (build_?? ) that builds the character. That's typically
 * how logos built from bitmaps would be handled.
 *
 * Several different methods can be used to encode lines of text. What's done
 * depends on the value assigned to encoding. Print time should decrease as
 * encoding increases (up to MAXENCODING). Setting encoding to 0, which should
 * probably be the default, produces output essentially identical to the original
 * version of dpost. It's the slowest but most stable method of encoding lines of
 * text, and won't be bothered by rounding errors in the font width tables that
 * could become noticeable by the time you get to the end of a line. Other schemes
 * seem to work, but aren't well tested and are not guaranteed for all possible
 * jobs. encoding can be changed on the command line using the -e option. Part of
 * the support for different encoding schemes was to move control of all text
 * related output to separate routines. It makes dpost work harder, but changing
 * things is easy. For example adding stuff to support widthshow took less than
 * an hour.
 *
 * I've also added code that handles the DOCUMENTFONTS comment, although it's
 * only produced for those fonts in directory /usr/lib/font/devpost that have an
 * associated .name file. The first string in a .name file should be the (long)
 * PostScript name (eg. Times-Roman in R.name). For now everything else in the
 * .name file is ignored, although that may also change. You'll find .name files
 * for all the supported fonts in the devpost source directory, although they may
 * not be installed in /usr/lib/font/devpost.
 *
 * The PostScript prologue is copied from *prologue before any of the input files
 * are translated. The program expects the following procedures are avaliable:
 *
 *	setup
 *
 *	  mark ... setup -
 *
 *	    Handles special initialization stuff that depends on how the program
 *	    was called. Expects to find a mark followed by key/value pairs on the
 *	    stack. The def operator is applied to each pair up to the mark, then
 *	    the default state is set up. An 'x res' command must preceed the
 *	    'x init' command!
 *
 *	pagesetup
 *
 *	  page pagesetup -
 *
 *	    Called at the start of each page, immediately after the page level
 *	    save, to do special initialization on a per page basis. Right now the
 *	    only argument is the current page number, and actually nothing of any
 *	    importance is currently done.
 *
 *	setdecoding
 *
 *	  num setdecoding -
 *
 *	    Selects the text decoding procedure (ie. what's assigned to PostScript
 *	    procedure t) from the decodingdefs array defined in the prologue. num
 *	    should be the value assigned to variable encoding (in dpost) and will
 *	    remain constant throughout a job, unless special features, like reverse
 *	    video printing, are requested. The text encoding scheme can be set on
 *	    the command line using the -e option. Print time and the size of the
 *	    output file will usually decrease as the value assigned to encoding
 *	    increases.
 *
 *	f
 *
 *	  size font f -
 *
 *	    Selects the size and font to be used for character imaging. Font names
 *	    are defined, in *prologue, so they agree with the one or two character
 *	    names used by troff.
 *
 *	m
 *
 *	  x y m -
 *
 *	    Moves to point (x, y). Normally only used when the vertical position
 *	    changes. Horizontal positioning between words (or letters) is handled
 *	    in procedure t (below).
 *
 *	t
 *
 *	  mark text t mark
 *
 *	    Processes everything on the stack, up to the mark, as a single line
 *	    of text to be printed at a fixed vertical position. What's put out as
 *	    text depends on the encoding scheme. Setting encoding to 0 produces
 *	    output essentially identical to the original version of dpost. In that
 *	    case everything on the stack, up to a mark, is interpreted (from top
 *	    down) as an absolute horizontal position and a string to be printed at
 *	    that point. For example the stack might look like,
 *
 *		mark(this)1000(is)1100(an)1200(example)1300 t
 *
 *	    Procedure t would go through the stack, up to the mark, adjusting the
 *	    horizontal position before printing each string. In other encoding
 *	    schemes, like the one based on widthshow, strings containing several
 *	    space separated words would appear on the stack, and each one would be
 *	    preceeded by a number that's expected to be added to the width of a
 *	    space. For example we might have,
 *
 *		mark(an example)30(this is)40 2 1000 2000 t
 *
 *	    where (1000, 2000) is where the first string starts and 2 is the repeat
 *	    count (ie. number of string and space pairs on the stack).
 *
 *	w
 *
 *	  string x y w -
 *
 *	    Prints a single word starting at position (x, y). Only used in the more
 *	    complicated encoding schemes (eg. the ones based on widthshow).
 *
 *	done
 *
 *	    Makes sure the last page is printed. Only needed when we're printing
 *	    more than one page on each sheet of paper.
 *
 * The PostScript procedures that support troff's drawing commands have been moved
 * out of *prologue and put in a separate file (ie. DRAW as defined in path.h).
 * The procedures are used by the routines in file draw.c, and are copied to the
 * prologue.
 *
 * Many default values, like the magnification and orientation, are defined in 
 * the prologue, which is where they belong. If they're changed (by options), an
 * appropriate definition is made after the prologue is added to the output file.
 * The -P option passes arbitrary PostScript through to the output file. Among
 * other things it can be used to set (or change) values that can't be accessed by
 * other options.
 *
 *
 * output language from troff:
 * all numbers are character strings
 * 
 * sn	size in points
 * fn	font as number from 1-n
 * cx	ascii character x
 * Cxyz	funny char xyz. terminated by white space
 * Hn	go to absolute horizontal position n
 * Vn	go to absolute vertical position n (down is positive)
 * hn	go n units horizontally (relative)
 * vn	ditto vertically
 * nnc	move right nn, then print c (exactly 2 digits!)
 * 		(this wart is an optimization that shrinks output file size
 * 		 about 35% and run-time about 15% while preserving ascii-ness)
 * Dt ...\n	draw operation 't':
 * 	Dl x y		line from here by x,y
 * 	Dc d		circle of diameter d with left side here
 * 	De x y		ellipse of axes x,y with left side here
 *	Da x1 y1 x2 y2	arc counter-clockwise from current point (x, y) to
 *			(x + x1 + x2, y + y1 + y2)
 * 	D~ x y x y ...	wiggly line by x,y then x,y ...
 * nb a	end of line (information only -- no action needed)
 * 	b = space before line, a = after
 * p	new page begins -- set v to 0
 * #...\n	comment
 * x ...\n	device control functions:
 * 	x i	init
 * 	x T s	name of device is s
 * 	x r n h v	resolution is n/inch
 * 		h = min horizontal motion, v = min vert
 * 	x p	pause (can restart)
 * 	x s	stop -- done forever
 * 	x t	generate trailer
 * 	x f n s	font position n contains font s
 * 	x H n	set character height to n
 * 	x S n	set slant to N
 * 
 * 	Subcommands like "i" are often spelled out like "init".
 *
 *
 *
 * To get dpost output conforming to Adobe's structuring conventions (DSC),
 * all output is accumulated in temporary files first. When the document is
 * completed, files that contain global data are output first, followed by
 * regular commands, all surrounded by DSC comments. Speed problems, which
 * were the reason why this was not done by previous versions of dpost, are
 * no longer of concern in 2005 since several hundred pages of text are
 * processed now in less than a second.
 */

#include	<sys/types.h>
#include	<sys/stat.h>
#include	<stdio.h>
#include	<fcntl.h>
#include	<stdlib.h>
#include	<string.h>
#include	<unistd.h>
#include	<signal.h>
#include	<math.h>
#include	<ctype.h>
#include	<time.h>
#include	<limits.h>
#include	<locale.h>
#include	<stdarg.h>

#include	"comments.h"		/* PostScript file structuring comments */
#include	"gen.h"			/* general purpose definitions */
#include	"path.h"		/* for the prologue and a few other files */
#include	"ext.h"			/* external variable definitions */
#include	"dev.h"			/* typesetter and font descriptions */
#include	"dpost.h"		/* a few definitions just used here */
#include	"asciitype.h"
#include	"afm.h"
#include	"fontmap.h"

char			*progname;
static const char	*prologue = DPOST;	/* the basic PostScript prologue */
const char		*colorfile = COLOR;	/* things needed for color support */
const char		*drawfile = DRAW;	/* and drawing */
static const char	*cutmarksfile = CUTMARKS;
static const char	*formfile = FORMFILE;	/* stuff for multiple pages per sheet */
const char		*baselinefile = BASELINE;

static const char	*fontdir = FONTDIR;	/* binary device directories found here */
static char	*hostfontdir = NULL;	/* host resident font directory */

static int	formsperpage = 1;	/* page images on each piece of paper */
static int	copies = 1;		/* and this many copies of each sheet */
int	picflag = ON;		/* enable/disable picture inclusion */


/*
 *
 * encoding selects the encoding scheme used to output lines of text. Change it
 * to something other than 0 at your own risk. The other methods seem to work but
 * aren't well tested and are not guaranteed. Some special features, like reverse
 * video, may temporarily change the encoding scheme and reset it to realencoding
 * when done.
 *
 * Encoding 4 is new as of 9/8/05. It stores only the distances between words and
 * thus saves a bit of output size. It is automatically enabled at high resolutions.
 *
 */


int		encoding = DFLTENCODING;
int		realencoding = DFLTENCODING;
int		maxencoding = MAXENCODING;
static int	eflag;

int		LanguageLevel;	/* PostScript output language level */
static int	Binary;		/* PostScript output contains binary data */

/*
 *
 * seenfonts[] keeps track of the fonts we've used, based on internal numbers. It
 * helps manage host resident fonts and the DOCUMENTFONTS comment, but only works
 * if all fonts have internal numbers less than MAXINTERNAL. *docfonts counts the
 * number of font names we've recorded in *temp_file. If it's positive routine
 * done() adds *temp_file to the output file before quitting.
 *
 */


static char		seenfonts[MAXINTERNAL+1];
static int		docfonts = 0;
static struct afmtab	**afmfonts;
static int		afmcount = 0;

/*
 *
 * devname[] is the device troff used when the job was formatted, while *realdev
 * is combined with *fontdir and used to locate the font and device tables that
 * that control the translation of the input files into PostScript. *realdev can
 * be changed using the -T option, but if you do you may end up getting garbage.
 * The character code field must agree with PostScript's font encoding and font
 * names must be properly mapped into PostScript font names in the prologue.
 *
 */


#define	devname	troff_devname
static char		devname[20] = "";	/* job is formatted for this printer */
static const char		*realdev = DEVNAME;	/* a good description of target printer */


/*
 *
 * Standard things that come from binary font and description files for *realdev.
 * Most are initialized in fontinit() or loadfont().
 *
 */


struct dev	dev;			/* DESC starts this way */
struct Font	**fontbase;		/* FONT files begin this way */
static int	*pstab;			/* list of available sizes */
static int	nsizes = 1;		/* and the number of sizes in that list */
static int	smnt;			/* index of first special font */
int		nchtab;			/* number of special character names */
static int	fsize;			/* max size of a font files in bytes */
static int	unitwidth;		/* set to dev.unitwidth */
char		*chname;		/* special character strings */
short		*chtab;			/* used to locate character names */
unsigned short	**fitab;		/* locates char info on each font */
int		**fontab;		/* character width data for each font */
unsigned short	**codetab;		/* and codes to get characters printed */
char		**kerntab;		/* for makefont() */


/*
 *
 * Special characters missing from standard PostScript fonts are defined by files
 * in directory *fontdir/devpost/charlib. Files have the same names as the troff
 * special character names (for now at least) and each one defines a PostScript
 * procedure that begins with the prefix build_ and ends with the character's
 * name.
 *
 * For example, the routine used to build character \(12, would be build_12.
 * downloaded[] points to an array, allocated in fontinit(), that keeps track of
 * the characters that have already been defined - so we only do it once.
 *
 */


static char	*downloaded;		/* nonzero means it's been downloaded */


/*
 *
 * Variables that keep track of troff's requests. All are set from values in the
 * input files. nfonts is adjusted in t_fp() as new fonts are mounted.
 *
 */


int		nfonts = 0;		/* number of font positions */
int		size = 1;		/* current size - internal value */
#define	FRACTSIZE	-23		/* if size == FRACTSIZE then ... */
static float	fractsize = 0;		/* fractional point size */
static int	font = 0;		/* font position we're using now */
static int	subfont = 0;		/* extra encoding vector */
int		hpos = 0;		/* where troff wants to be - horizontally */
int		vpos = 0;		/* same but vertically */
static float	lastw = 0;		/* width of the last input character */
static int	track = 0;		/* tracking hint from troff */
static int	lasttrack = 0;		/* previous tracking hint */
static int	tracked;		/* records need to flush track */
static int	lastc = 0;		/* and its name (or index) */

int		res;			/* resolution assumed in input file */
static float	widthfac = 1.0;		/* for emulation = res/dev.res */
static float	horscale = 1.0;		/* horizontal font scaling */
static float	lasthorscale = 1.0;	/* last horizontal font scaling */
static int	wordspace = 0;		/* w command was last */


/*
 *
 * Remember some of the same things, but this time for the printer. lastend is only
 * used when we're doing reverse video, and is where the last character on the
 * current line was printed.
 *
 */


static int	lastsize = -1;		/* last internal size we used */
static float	lastfractsize = -1;	/* last fractional size */
static int	lastfont = -1;		/* last font we told printer about */
static int	lastsubfont = -1;	/* last extra encoding vector */
static float	lastx = -1;		/* printer's current position */
static int	lasty = -1;
static int	savey = -1;
int		lastend;		/* where last character on this line was */


/*
 *
 * fontname[] keeps track of the mounted fonts. Filled in (by t_fp()) from data
 * in the binary font files.
 *
 * When font metrics are directly read from AFM files, all characters that
 * are not ASCII are put into the remaining positions in PostScript encoding
 * vectors. Their position in these vectors in recorded in afm->encmap, and
 * characters from troff are translated if necessary.
 *
 */


static struct  {

	struct afmtab	*afm;		/* AFM data, if any */
	char	*name;			/* name of the font loaded here */
	int	number;			/* its internal number */
	float	fontheight;		/* points from x H ... */
	int	fontslant;		/* angle from x S ... */


} fontname[NFONT+1];


/*
 *
 * All the special fonts will be mounted after the last legitimate font position.
 * It helps when we're translating files prepared for devices, like the 202, that
 * have a different set of special fonts. The set of special fonts needed when
 * *realdev's tables are used may not get mounted when we're emulating another
 * device. gotspecial keeps track of whether we've done it yet. seenpage is set
 * to TRUE after we've seen the first page command in the input file. It controls
 * what's done in t_font() and is needed because nfonts is no longer set when the
 * DESC file is read, but rather is updated from "x font" commands in the
 * input files. gotregular ensures that at least one regular font is mounted.
 *
 */


static int		gotspecial = FALSE;
static int		gotregular = FALSE;
static int		seenpage = FALSE;


/*
 *
 * The amount of horizontal positioning error we accept controls both the size
 * of the output file and the appearance of the printed text. It's probably most
 * important when we're emulating other devices, like the APS-5. The error can be
 * set using the -S option. It's converted from points to machine units in t_init()
 * after the resolution is known. rvslop is also set in t_init() and only used to
 * adjust the width of the box that's drawn around text when we're printing in
 * reverse video mode.
 *
 */


static float		pointslop = SLOP;	/* horizontal error in points */
static int		Sflag;			/* unless -S gives explicit slop */
static int		slop;			/* and machine units */
static int		rvslop;			/* to extend box in reverse video mode */


/*
 *
 * Characters are accumulated and saved in PostScript strings that are eventually
 * processed by making a single call to procedure t. textcount counts the number
 * of individual strings collected but not yet processed, and is primarily used to
 * make sure PostScript's stack doesn't get too big. When textcount is positive
 * we've started accumulating strings and need to generate a call to PostScript
 * procedure t to process the text before anything else (like a font change) is
 * done.
 *
 */


static int		textcount = 0;		/* strings accumulated so far */
static int		stringstart = 0;	/* where the next one starts */
static int		laststrstart = INT_MIN;	/* save for optimization */
static int		spacecount = 0;		/* spaces seen so far on current line */
static int		charcount = 0;		/* characters on current line */


/*
 *
 * Things that can be used by text line encoding schemes that need to read and
 * remember an entire line before doing any output. The strings that make up the
 * line can be saved in array strings[] and accessed by fields in line[]. *strptr
 * points to the next free slot in strings[].
 *
 */


static char		strings[STRINGSPACE];
static char		*strptr;
static Line		line[MAXSTACK+3];


/*
 *
 * When we're emulating another device we may want to map font name requests that
 * come in as "x font pos name" commands into some other font name before anything
 * else is done (ie. calling loadfont()). Font names can collide or we may just
 * want to a mapping that depends on the device troff used to format the input
 * files. devfontmap points to a structure that's filled in by getdevmap() if the
 * mapping file /usr/lib/font/dev*realdev/fontmaps/devname exists. mapdevfont()
 * then uses that table to translate font name requests into something else before
 * loadfont() gets called.
 *
 * fontmap[] provides a simple minded translation that maps an unrecognized font
 * name (in loadfont()) into another font name that we know will be available. It
 * doesn't provide the fine control available with *devfontmap, but should be good
 * enough for most jobs. Both structures are only needed when emulating another
 * device using *realdev's font tables.
 *
 */


static Devfontmap	*devfontmap = NULL;	/* device level */
static Fontmap		fontmap[] = FONTMAP;	/* and general mapping tables - emulation */


/*
 *
 * Variables and functions for the pdfmark operator.
 *
 */
static char	*Author;		/* DOCINFO /Author */
static char	*Title;			/* DOCINFO /Title */
static char	*Subject;		/* DOCINFO /Subject */
static char	*Keywords;		/* DOCINFO /Keywords */
static struct Bookmark {
	char	*Title;			/* OUT /Title */
	char	*title;			/* unencoded title */
	int	Count;			/* OUT /Count */
	int	level;			/* used to generate count */
	int	closed;			/* the bookmark is closed initially */
} *Bookmarks;
static size_t	nBookmarks;
static double	pagelength = 792;	/* lenght of page in points */
#define	MAXBOOKMARKLEVEL	20

static void	orderbookmarks(void);

static struct box {
	int	val[4];
	int	flag;
} mediasize, bleedat, trimat, cropat;

/*
 *
 * For the -M option.
 *
 */

static enum {
	M_NONE	= 000,
	M_CUT	= 001,
	M_STAR	= 002,
	M_REG	= 004,
	M_COL	= 010,
	M_ALL	= 077
} Mflag;

static void	setmarks(char *);


/*
 *
 * A few variables that are really only used if we're doing accounting. Designed
 * for our use at Murray Hill and probably won't suit your needs. Changes should
 * be easy and can be made in routine account().
 *
 */


int		printed = 0;		/* charge for this many pages */


/*
 *
 * Output and accounting file definitions. The PostScript output always goes to
 * stdout or /dev/null, while the accounting file can be selected using the -A
 * option.
 *
 */


FILE	*tf = NULL;		/* PostScript output goes here */
FILE	*gf = NULL;		/* global data goes here */
FILE	*rf = NULL;		/* resource data goes here */
FILE	*sf = NULL;		/* supplied resource comments go here */
FILE	*nf = NULL;		/* needed resource comments go here */
FILE	*pf = NULL;		/* elements of _custompagesetup */
static int	sfcount;		/* count of supplied resources */
static int	nfcount;		/* count of needed resources */
static int	ostdout;		/* old standard output */
static FILE	*fp_acct = NULL;	/* accounting stuff written here */


/*
 *
 * Very temporary space that can be used to do things like building up pathnames
 * immediately before opening a file. Contents may not be preserved across calls
 * to subroutines defined in this file, so it probably should only be used in low
 * level subroutines like loadfont() or fontinit() and nowhere else.
 *
 */


static char	temp[4096];

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

static char *linkborderstyle;
static char *ulinkborderstyle;

static void	sethorscale(char *);
static void	t_papersize(char *);
static void	t_cutat(const char *, struct box *, char *);
static void	t_track(char *);
static void	t_strack(void);
static void	t_pdfmark(char *);
static void	t_locale(char *);
static void	t_anchor(char *);
static void	t_link(char *);
static void	t_linkcolor(char *);
static void	t_linkborder(char *);
static void	t_ulink(char *);
static void	t_ulinkcolor(char *);
static void	t_ulinkborder(char *);
static char	*t_linkborderstyle(char *);

static int	mb_cur_max;

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


int 
main(int agc, char *agv[])


{
    const char	template[] = "/var/tmp/dpostXXXXXX";
    char	*tp;
    FILE	*fp;


/*
 *
 * A program that translates troff output into PostScript. All the input files
 * must have been formatted for the same device, which doesn't necessarily have to
 * be *realdev. If there's more than one input file, each begins on a new page.
 *
 */

    setlocale(LC_CTYPE, "");
    mb_cur_max = MB_CUR_MAX;

    ostdout = dup(1);
    if (close(mkstemp(tp = strdup(template))) < 0 ||
    	    freopen(tp, "r+", stdout) == NULL) {
        perror(tp);
        return 2;
    }
    unlink(tp);
    if (close(mkstemp(tp = strdup(template))) < 0 ||
      	    (gf = fopen(tp, "r+")) == NULL) {
        perror(tp);
        return 2;
    }
    unlink(tp);
    if (close(mkstemp(tp = strdup(template))) < 0 ||
	    (rf = fopen(tp, "r+")) == NULL) {
	perror(tp);
	return 2;
    }
    unlink(tp);
    if (close(mkstemp(tp = strdup(template))) < 0 ||
	    (sf = fopen(tp, "r+")) == NULL) {
	perror(tp);
	return 2;
    }
    unlink(tp);
    if (close(mkstemp(tp = strdup(template))) < 0 ||
	    (nf = fopen(tp, "r+")) == NULL) {
	perror(tp);
	return 2;
    }
    unlink(tp);
    if (close(mkstemp(tp = strdup(template))) < 0 ||
	    (pf = fopen(tp, "r+")) == NULL) {
	perror(tp);
	return 2;
    }
    unlink(tp);

    argc = agc;				/* global so everyone can use them */
    argv = agv;

    progname =
    prog_name = argv[0];		/* just for error messages */

    init_signals();			/* sets up interrupt handling */
    options();				/* command line options */
    arguments();			/* translate all the input files */
    done();				/* add trailing comments etc. */

    fp = fdopen(ostdout, "w");
    header(fp);			/* PostScript file structuring comments */

    account();				/* job accounting data */
    return(x_stat);			/* everything probably went OK */

}   /* End of main */

/*****************************************************************************/
static int
putint(int n, FILE *fp)
{
    char	buf[20];
    int	c = 0, i;

/*
 *
 * Print an integer in PostScript binary token representation.
 *
 */
    if (n >= -128 && n <= 127) {
	buf[c++] = 136;
	buf[c++] = n;
    } else if (n >= -32768 && n <= 32767) {
	buf[c++] = 134;
	buf[c++] = (n&0xff00) >> 8;
	buf[c++] = (n&0x00ff);
    } else {
	buf[c++] = 132;
	buf[c++] = (n&0xff000000) >> 24;
	buf[c++] = (n&0x00ff0000) >> 16;
	buf[c++] = (n&0x0000ff00) >> 8;
	buf[c++] = (n&0x000000ff);
    }
    for (i = 0; i < c; i++)
	putc(buf[i]&0377, fp);
    return c;
}

static int
putstring1(const char *sp, int n, FILE *fp)
{
/*
 *
 * Print a string in PostScript binary token representation.
 *
 */
    putc(142, fp);
    putc(n, fp);
    fwrite(sp, 1, n, fp);
    return n + 2;
}

static int
putstring(const char *sp, int n, FILE *fp)
{
    int	c = 0, m;

    do {
	m = n > 250 ? 250 : n;
	c += putstring1(sp, m, fp);
	sp += m;
	n -= m;
    } while (n > 0);
    return c;
}

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


void
init_signals(void)


{


    void	interrupt(int);		/* signal handler */


/*
 *
 * Make sure we handle interrupts.
 *
 */


    if ( signal(SIGINT, interrupt) == SIG_IGN )  {
	signal(SIGINT, SIG_IGN);
	signal(SIGQUIT, SIG_IGN);
	signal(SIGHUP, SIG_IGN);
    } else {
	signal(SIGHUP, interrupt);
	signal(SIGQUIT, interrupt);
    }   /* End else */

    signal(SIGTERM, interrupt);

}   /* End of init_signals */


/*****************************************************************************/
static char *
pdfdate(time_t *tp, char *buf, size_t sz)
{
    struct tm	*tmptr;
    int	tzdiff, tzdiff_hour, tzdiff_min;

    tzdiff = *tp - mktime(gmtime(tp));
    tzdiff_hour = (int)(tzdiff / 60);
    tzdiff_min = tzdiff_hour % 60;
    tzdiff_hour /= 60;
    tmptr = localtime(tp);
    if (tmptr->tm_isdst > 0)
	tzdiff_hour++;
    snprintf(buf, sz, "(D:%04d%02d%02d%02d%02d%02d%+03d'%02d')",
	tmptr->tm_year + 1900,
	tmptr->tm_mon + 1, tmptr->tm_mday,
	tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec,
	tzdiff_hour, tzdiff_min);
    return buf;
}
/*****************************************************************************/

static void
pdfbox(const char *boxname, struct box *bp, FILE *fp, int perpage)
{
    double	llx, lly, urx, ury;

    if (bp->flag == 0)
	return;
    llx = bp->val[0] * 72.0 / res;
    lly = pagelength - ((bp->val[1] + bp->val[3]) * 72.0 / res);
    urx = (bp->val[0] + bp->val[2]) * 72.0 / res;
    ury = pagelength - (bp->val[1] * 72.0 / res);
    fprintf(gf, "/_%s [%g %g %g %g] def\n",
	boxname, llx, lly, urx, ury);
    if (perpage)
	fprintf(fp,
	    "[ {ThisPage} 1 dict dup /%s _%s put /PUT pdfmark\n",
	    boxname, boxname);
    else
	fprintf(gf, "[ /%s _%s /PAGES pdfmark\n", boxname, boxname);
}

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

void
header(FILE *fp)


{

/*
 *
 * Print the DSC header, followed by the data generated so far. This function
 * is now called after all input has been processed.
 *
 */


    struct Bookmark	*bp;
    time_t	now;
    int		n;
    double	x = 0, y = 0;
    char	buf[4096];
    char	crdbuf[40];


    time(&now);
    if (mediasize.flag) {
	x = mediasize.val[2] * 72.0 / res;
	y = mediasize.val[3] * 72.0 / res;
    }
    fprintf(fp, "%s", CONFORMING);
    fprintf(fp, "%s %s\n", CREATOR, creator);
    fprintf(fp, "%s %s", CREATIONDATE, ctime(&now));
    if (LanguageLevel > 1)
    	fprintf(fp, "%%%%LanguageLevel: %d\n", LanguageLevel);
    if (Binary)
	fprintf(fp, "%%%%DocumentData: Binary\n");
    if ( temp_file != NULL )  {
	if ( docfonts > 0 )  {
	    cat(temp_file, fp);
	    putc('\n', fp);
	}   /* End if */
	unlink(temp_file);
    }	/* End if */
    fprintf(fp, "%s %d\n", PAGES, printed);
    if (mediasize.flag & 2)
	fprintf(fp, "%%%%DocumentMedia: x%gy%g %g %g 0 () ()\n", x, y, x, y);

    fflush(nf);
    rewind(nf);
    while ((n = fread(buf, 1, sizeof buf, nf)) > 0)
	fwrite(buf, 1, n, fp);
    fflush(sf);
    rewind(sf);
    while ((n = fread(buf, 1, sizeof buf, sf)) > 0)
	fwrite(buf, 1, n, fp);
    fprintf(fp, "%s", ENDCOMMENTS);

    fprintf(fp, "%s\n", "%%BeginProlog");
    if ( cat(prologue, fp) == FALSE )
	error(FATAL, "can't read %s", prologue);
    fflush(rf);
    rewind(rf);
    while ((n = fread(buf, 1, sizeof buf, rf)) > 0)
	fwrite(buf, 1, n, fp);
    fprintf(fp, "%s", ENDPROLOG);

    fprintf(fp, "%s", BEGINSETUP);
    fprintf(fp, "\
[ /CreationDate %s\n\
  /Creator (%s)\n", pdfdate(&now, crdbuf, sizeof crdbuf), creator);
    if (Author)
	fprintf(fp, "  /Author %s\n", Author);
    if (Title)
	fprintf(fp, "  /Title %s\n", Title);
    if (Subject)
	fprintf(fp, "  /Subject %s\n", Subject);
    if (Keywords)
	fprintf(fp, "  /Keywords %s\n", Keywords);
    fprintf(fp, "/DOCINFO pdfmark\n");
    if (Bookmarks) {
	orderbookmarks();
	for (bp = &Bookmarks[0]; bp < &Bookmarks[nBookmarks]; bp++) {
	    fprintf(fp, "[ /Title %s\n", bp->Title);
	    if (bp->Count)
		fprintf(fp, "  /Count %d\n", bp->closed ?
		    -bp->Count : bp->Count);
		fprintf(fp, "  /Dest /Bookmark$%d\n"
		            "/OUT pdfmark\n",
		    (int)(bp - &Bookmarks[0]));
	}
    }

    fflush(pf);
    rewind(pf);
    fprintf(fp, "/_custompagesetup {\n");
    pdfbox("TrimBox", &trimat, fp, 1);
    pdfbox("BleedBox", &bleedat, fp, 1);
    pdfbox("CropBox", &cropat, fp, 0);
    while ((n = fread(buf, 1, sizeof buf, pf)) > 0)
	fwrite(buf, 1, n, fp);
    fprintf(fp, "} def\n");
    fprintf(fp, "/_marks {\n");
    if (Mflag & M_CUT)
	fprintf(fp, "_cutmarks\n");
    if (Mflag & M_REG)
	fprintf(fp, "_regmarks\n");
    if (Mflag & M_STAR)
	fprintf(fp, "_startargets\n");
    if (Mflag & M_COL)
	fprintf(fp, "_colorbars\n");
    fprintf(fp, "} def\n");

    fflush(gf);
    rewind(gf);
    while ((n = fread(buf, 1, sizeof buf, gf)) > 0)
	fwrite(buf, 1, n, fp);
    if (mediasize.flag) {
	fprintf(fp, "/pagebbox [0 0 %g %g] def\n", x, y);
	fprintf(fp, "userdict /gotpagebbox true put\n");
	if (mediasize.flag & 2)
	    fprintf(fp, "/setpagedevice where {pop "
		"1 dict dup /PageSize [%g %g] put setpagedevice"
		"} if\n", x, y);
    }
    fprintf(fp, "mark\n");

    fflush(stdout);
    rewind(stdout);
    while ((n = fread(buf, 1, sizeof buf, stdout)) > 0)
    	fwrite(buf, 1, n, fp);

    fprintf(fp, "%s", ENDOFFILE);

}   /* End of header */


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


void
options(void)


{

    const char		optnames[] = "a:c:e:m:n:o:p:tw:x:y:A:C:J:F:H:L:M:OP:R:S:T:DI";

    int		ch;			/* name returned by getopt() */


/*
 *
 * Reads and processes the command line options. There are, without a doubt, too
 * many options!
 *
 */


    while ( (ch = getopt(argc, argv, optnames)) != EOF )  {

	switch ( ch )  {

	    case 'a':			/* aspect ratio */
		    fprintf(stdout, "/aspectratio %s def\n", optarg);
		    break;

	    case 'c':			/* number of copies */
		    copies = atoi(optarg);
		    fprintf(stdout, "/#copies %s store\n", optarg);
		    break;

	    case 'e':			/* change the encoding scheme */
		    if ( (encoding = atoi(optarg)) < 0 || encoding > MAXENCODING )
			encoding = DFLTENCODING;
		    else
		        eflag = 1;
		    realencoding = encoding;
		    break;

	    case 'm':			/* magnification */
		    fprintf(stdout, "/magnification %s def\n", optarg);
		    break;

	    case 'n':			/* forms per page */
		    formsperpage = atoi(optarg);
		    fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg);
		    fprintf(stdout, "/formsperpage %s def\n", optarg);
		    break;

	    case 'o':			/* output page list */
		    out_list(optarg);
		    break;

	    case 'p':			/* landscape or portrait mode */
		    if ( *optarg == 'l' )
			fprintf(stdout, "/landscape true def\n");
		    else fprintf(stdout, "/landscape false def\n");
		    break;

	    case 't':			/* just for compatibility */
		    break;

	    case 'w':			/* line width for drawing */
		    fprintf(stdout, "/linewidth %s def\n", optarg);
		    break;

	    case 'x':			/* shift horizontally */
		    fprintf(stdout, "/xoffset %s def\n", optarg);
		    break;

	    case 'y':			/* and vertically on the page */
		    fprintf(stdout, "/yoffset %s def\n", optarg);
		    break;

	    case 'A':			/* force job accounting */
	    case 'J':
		    if ( (fp_acct = fopen(optarg, "a")) == NULL )
			error(FATAL, "can't open accounting file %s", optarg);
		    break;

	    case 'C':			/* copy file to straight to output */
		    if ( cat(optarg, stdout) == FALSE )
			error(FATAL, "can't read %s", optarg);
		    break;

	    case 'F':			/* font table directory */
		    fontdir = optarg;
		    break;

	    case 'H':			/* host resident font directory */
		    hostfontdir = optarg;
		    break;

	    case 'L':			/* PostScript prologue file */
		    setpaths(optarg);
		    break;

	    case 'M':			/* print cut marks */
		    setmarks(optarg);
		    break;

	    case 'O':			/* turn picture inclusion off */
		    picflag = OFF;
		    break;

	    case 'P':			/* PostScript pass through */
		    fprintf(stdout, "%s\n", optarg);
		    break;

	    case 'R':			/* special global or page level request */
		    saverequest(optarg);
		    break;

	    case 'S':			/* horizontal position error */
		    if ( (pointslop = atof(optarg)) < 0 )
			pointslop = 0;
		    Sflag = 1;
		    break;

	    case 'T':			/* target printer */
		    realdev = optarg;
		    break;

	    case 'D':			/* debug flag */
		    debug = ON;
		    tf = stdout;
		    break;

	    case 'I':			/* ignore FATAL errors */
		    ignore = ON;
		    break;

	    case '?':			/* don't know the option */
		    error(FATAL, "");
		    break;

	    default:
		    error(FATAL, "missing case for option %c", ch);
		    break;

	}   /* End switch */
    }	/* End while */

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

    if (Mflag) {
	FILE	*otf = tf;
	tf = stdout;
	doglobal(cutmarksfile);
	tf = otf;
    }

}   /* End of options */


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


void
setpaths (
    char *name			/* string that followed the -L option */
)


{


    char	*path;			/* start of the pathname */


/*
 *
 * Extends the -L option to permit run time modification of pathnames that were
 * fixed or didn't exist in previous versions of dpost. For example, the PostScript
 * drawing procedures have been moved out of *prologue and put in *drawfile. The
 * new syntax can be either -Lfile or -Lname:file. If the "name:" prefix is omitted
 * file will be used as the prologue, otherwise name should be one of "prologue",
 * "font", "draw", "color", or "form" and is used to select the pointer that gets
 * set to string "file".
 *
 */


    for ( path = name; *path; path++ )
	if ( *path == ':' || *path == ' ' )  {
	    while ( *path == ':' || *path == ' ' ) path++;
	    break;
	}   /* End if */

    if ( *path == '\0' )		/* didn't find a "name:" prefix */
	path = name;

    if ( path == name || strncmp(name, "prologue", strlen("prologue")) == 0 )
	prologue = path;
    else if ( strncmp(name, "draw", strlen("draw")) == 0 )
	drawfile = path;
    else if ( strncmp(name, "color", strlen("color")) == 0 )
	colorfile = path;
    else if ( strncmp(name, "form", strlen("form")) == 0 )
	formfile = path;
    else if ( strncmp(name, "baseline", strlen("baseline")) == 0 )
	baselinefile = path;
    else if ( strncmp(name, "cutmarks", strlen("cutmarks")) == 0 )
	cutmarksfile = path;

}   /* End of setpaths */

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

static void
setmarks(char *str)
{
    char	*sp;
    int	c;

    do {
	for (sp = str; *sp && *sp != ':'; sp++);
	c = *sp;
	*sp = 0;
	if (prefix(str, "cutmarks"))
	    Mflag |= M_CUT;
	else if (prefix(str, "registrationmarks"))
	    Mflag |= M_REG;
	else if (prefix(str, "startargets"))
	    Mflag |= M_STAR;
	else if (prefix(str, "colorbars"))
	    Mflag |= M_COL;
	else if (prefix(str, "all"))
	    Mflag |= M_ALL;
	else
	    error(FATAL, "unknown mark: -M %s", str);
	*sp = c;
	str = &sp[1];
    } while (c);
}

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


void
setup(void)


{


/*
 * Handles things that must be done after the options are read but before the
 * input files are processed. Called from t_init() after an "x init" command is
 * read, because we need the resolution before we can generate the call to the
 * setup procedure defined in *prologue. Only allowing one call to setup assumes
 * all the input files have been prepared for the same device.
 *
 */


    writerequest(0, stdout);		/* global requests eg. manual feed */
    fprintf(stdout, "/resolution %d def\n", res);
    fprintf(stdout, "setup\n");
    fprintf(stdout, "%d setdecoding\n", encoding);

    if ( formsperpage > 1 )  {		/* followed by stuff for multiple pages */
	if ( cat(formfile, stdout) == FALSE )
	    error(FATAL, "can't read %s", formfile);
	fprintf(stdout, "%d setupforms\n", formsperpage);
    }	/* End if */

    fprintf(stdout, "%s", ENDSETUP);

}   /* End of setup */


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


void
arguments(void)


{


    FILE	*fp;			/* next input file */


/*
 *
 * Makes sure all the non-option command line arguments are processed. If we get
 * here and there aren't any arguments left, or if '-' is one of the input files
 * we'll translate stdin.
 *
 */


    if ( argc < 1 )
	conv(stdin);
    else
	while ( argc > 0 ) {
	    if ( strcmp(*argv, "-") == 0 )
		fp = stdin;
	    else if ( (fp = fopen(*argv, "r")) == NULL )
		error(FATAL, "can't open %s", *argv);
	    conv(fp);
	    if ( fp != stdin )
		fclose(fp);
	    argc--;
	    argv++;
	}   /* End while */

}   /* End of arguments */


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


void
done(void)


{

/*
 *
 * Finished with all the input files, so mark the end of the pages with a TRAILER
 * comment, make sure the last page prints, and add things like the DOCUMENTFONTS
 * and PAGES comments that can only be determined after all the input files have
 * been read.
 *
 */


    fprintf(stdout, "%s", TRAILER);
    fprintf(stdout, "done\n");

}   /* End of done */


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


void
account(void)


{


/*
 *
 * Writes an accounting record to *fp_acct provided it's not NULL. Accounting is
 * requested using the -A or -J options.
 *
 */

    if ( fp_acct != NULL )
	fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);

}   /* End of account */


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

void
conv(


    register FILE	*fp		/* next input file */
)


{


    register int	c;		/* usually first char in next command */
    int			m, n, n1, m1;	/* when we need to read integers */
    char		str[4096];	/* for special chars and font numbers */
    char		b;


/*
 *
 * Controls the translation of troff's device independent output language into
 * PostScript. The call to t_page() that prints the last page is made when we
 * exit the loop, but probably belongs in t_trailer().
 *
 */


    redirect(-1);			/* only do output after a page command */
    lineno = 1;				/* line in current file */

    while ((c = getc(fp)) != EOF)  {

	switch (c)  {

	    case '\n':			/* just count this line */
		    lineno++;
		    break;

	    case ' ':			/* when input is text */
	    case 0:			/* occasional noise creeps in */
		    break;

	    case '0': case '1': case '2': case '3': case '4':
	    case '5': case '6': case '7': case '8': case '9':
		    /* two motion digits plus a character */
		    hmot((c-'0')*10 + getc(fp)-'0');
		    put1(getc(fp));
		    break;

	    case 'c':			/* single ascii character */
		    put1(getc(fp));
		    break;

	    case 'C':			/* special character */
		    sget(str, sizeof str, fp);
		    put1s(str);
		    break;

	    case 'N':			/* character at position n */
		    fscanf(fp, "%d", &m);
		    endtext();
		    oput(m);
		    endtext();
		    break;

	    case 'D':			/* drawing functions */
		    endtext();
		    getdraw();
		    if ( size != lastsize || (size == FRACTSIZE &&
			    fractsize != lastfractsize) ||
			    horscale != lasthorscale) {
			subfont = 0;
			t_sf(0);
		    }
		    switch ((c=getc(fp))) {
			case 'p':	/* draw a path */
			    while (fscanf(fp, "%d %d", &n, &m) == 2)
				drawline(n, m);
			    lineno++;
			    break;

			case 'P':	/* solid polygon */
			    fprintf(tf, "newpath %d %d neg moveto\n", hpos,
				vpos);
			    while (fscanf(fp, "%d %d", &n, &m) == 2) {
				hpos += n;
				vpos += m;
				fprintf(tf, "%d %d neg lineto\n", hpos, vpos);
			    }
			    fprintf(tf, "closepath fill\n");
			    lineno++;
			    break;

			case 'l':	/* draw a line */
			    fscanf(fp, "%d %d %c", &n, &m, &b);
			    n1 = b;
			    drawline(n, m);
			    break;

			case 'c':	/* circle */
			case 'C':	/* filled circle */
			    fscanf(fp, "%d", &n);
			    drawcirc(n, c);
			    break;

			case 'e':	/* ellipse */
			case 'E':	/* filled ellipse */
			    fscanf(fp, "%d %d", &m, &n);
			    drawellip(m, n, c);
			    break;

			case 'a':	/* counter-clockwise arc */
			case 'A':	/* clockwise arc */
			    fscanf(fp, "%d %d %d %d", &n, &m, &n1, &m1);
			    drawarc(n, m, n1, m1, c);
			    break;

			case 'q':	/* spline without end points */
			    drawspline(fp, 1);
			    lineno++;
			    break;

			case '~':	/* wiggly line */
			    drawspline(fp, 2);
			    lineno++;
			    break;

			case 't':	/* set line width, ignore */
			    fscanf(fp, "%d %d", &m, &n);
			    hgoto(hpos + m);
			    lineno++;
			    break;

			case 'F':	/* color scheme, ignore */
			case 'f':	/* filling color, ignore */
			    fgets(str, sizeof str, fp);
			    lineno++;
			    break;

			default:
			    error(FATAL, "unknown drawing function %c", c);
			    break;
		    }	/* End switch */
		    break;

	    case 's':			/* use this point size */
		    fscanf(fp, "%d", &n);	/* ignore fractional sizes */
		    if (n != FRACTSIZE)
		    	setsize(t_size(n), 0);
		    else {
			float f;
			fscanf(fp, "%f", &f);
			setsize(FRACTSIZE, f);
		    }
		    break;

	    case 'f':			/* use font mounted here */
		    sget(str, sizeof str, fp);
		    setfont(t_font(str));
		    break;

	    case 'H':			/* absolute horizontal motion */
		    fscanf(fp, "%d", &n);
		    hgoto(n);
		    break;

	    case 'h':			/* relative horizontal motion */
		    fscanf(fp, "%d", &n);
		    hmot(n);
		    break;

	    case 'w':			/* word space */
		    wordspace++;
		    break;

	    case 'V':			/* absolute vertical position */
		    fscanf(fp, "%d", &n);
		    vgoto(n);
		    break;

	    case 'v':			/* relative vertical motion */
		    fscanf(fp, "%d", &n);
		    vmot(n);
		    break;

	    case 'p':			/* new page */
		    fscanf(fp, "%d", &n);
		    t_page(n);
		    break;

	    case 'n':			/* end of line */
		    while ( (c = getc(fp)) != '\n'  &&  c != EOF ) ;
		    t_newline();
		    lineno++;
		    break;

	    case '#':			/* comment */
		    while ( (c = getc(fp)) != '\n'  &&  c != EOF ) ;
		    lineno++;
		    break;

	    case 'x':			/* device control function */
		    devcntrl(fp);
		    lineno++;
		    break;

	    default:
		    error(FATAL, "unknown input character %o %c", c, c);
		    done();

	}   /* End switch */

    }	/* End while */

    t_page(-1);				/* print the last page */
    endtext();

}   /* End of conv */


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

void
devcntrl(


    FILE	*fp			/* current input file */
)


{


    char	str[4096], *buf, str1[4096];
    int		c, n, sz;


/*
 *
 * Called from conv() to process the rest of a device control function. There's
 * a whole family of them and they all start with the string "x ", which we've
 * already read. The "x X ..." commands are an extensible (and device dependent)
 * family that we use here for things like picture inclusion. Unrecognized device
 * control commands are ignored.
 *
 */


    buf = malloc(sz = 4096);
    sget(str, sizeof str, fp);		/* get the control function name */

    switch ( str[0] )  {		/* only the first character counts */

	case 'i':			/* initialize */
		t_init();
		break;

	case 'T':			/* device name */
		sget(devname, sizeof devname, fp);
		getdevmap();
		/*
		 * This used to be "strcpy(devname, realdev);" but
		 * it does not work when DESC is a text file because
		 * the fonts are in a different directory.
		 */
		if (dev.afmfonts || (devname[0] == 'p' && devname[1] == 's'))
		    realdev = devname;
		else
		    n_strcpy(devname, realdev, sizeof(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", &n);
		sget(str, sizeof str, fp);
		fgets(buf, sz, 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 */
		c = 0;
		sscanf(buf, "%s %d", str1, &c);
		loadfont(n, mapdevfont(str), str1, 0, c);
		break;

	/* these don't belong here... */
	case 'H':			/* char height */
		fscanf(fp, "%d", &n);
		if (n != FRACTSIZE)
		    t_charht(n, 0);
		else {
		    float	f;
		    fscanf(fp, "%f", &f);
		    t_charht(FRACTSIZE, f);
		}
		break;

	case 'S':			/* slant */
		fscanf(fp, "%d", &n);
		t_slant(n);
		break;

	case 'X':			/* copy through - from troff */
		do
		    c = getc(fp);
		while (spacechar(c));
		n = 0;
		if (c != EOF) do {
		    if (n + 1 < sizeof str)
			str[n++] = c;
		    c = getc(fp);
		} while (c != EOF && !spacechar(c) && c != ':');
		str[n] = 0;
		if (c != ':')
		    ungetc(c, fp);
		n = 0;
		for (;;) {
		    fgets(&buf[n], sz - n, fp);
		    if ((c = getc(fp)) != '+') {
			ungetc(c, fp);
			break;
		    }
		    while (buf[n])
			n++;
		    if (sz - n < 4096)
			buf = realloc(buf, sz += 4096);
		    lineno++;
		}
		if ( strcmp(str, "PI") == 0 || strcmp(str, "PictureInclusion") == 0 )
		    picture(buf);
		else if ( strcmp(str, "InlinePicture") == 0 )
		    inlinepic(fp, buf);
		else if ( strcmp(str, "SupplyFont") == 0 )
		    t_supply(buf);
		else if ( strcmp(str, "PaperSize") == 0 )
		    t_papersize(buf);
		else if ( strcmp(str, "TrimAt") == 0 )
		    t_cutat("Trim size", &trimat, buf);
		else if ( strcmp(str, "BleedAt") == 0 )
		    t_cutat("Bleed size", &bleedat, buf);
		else if ( strcmp(str, "CropAt") == 0 )
		    t_cutat("Crop size", &cropat, buf);
		else if ( strcmp(str, "Track") == 0 )
		    t_track(buf);
		else if ( strcmp(str, "PDFMark") == 0 )
		    t_pdfmark(buf);
		else if ( strcmp(str, "LC_CTYPE") == 0 )
		    t_locale(buf);
		else if ( strcmp(str, "Anchor") == 0 )
		    t_anchor(buf);
		else if ( strcmp(str, "Link") == 0 )
		    t_link(buf);
		else if ( strcmp(str, "SetLinkColor") == 0 )
		    t_linkcolor(buf);
		else if ( strcmp(str, "SetLinkBorder") == 0 )
		    t_linkborder(buf);
		else if ( strcmp(str, "SetBorderStyle") == 0 )
		    linkborderstyle = t_linkborderstyle(buf);
		else if ( strcmp(str, "SetUBorderStyle") == 0 )
		    ulinkborderstyle = t_linkborderstyle(buf);
		else if ( strcmp(str, "ULink") == 0 )
		    t_ulink(buf);
		else if ( strcmp(str, "SetULinkColor") == 0 )
		    t_ulinkcolor(buf);
		else if ( strcmp(str, "SetULinkBorder") == 0 )
		    t_ulinkborder(buf);
		else if ( strcmp(str, "HorScale") == 0 )
		    sethorscale(buf);
		else if ( strcmp(str, "BeginPath") == 0 )
		    beginpath(buf, FALSE);
		else if ( strcmp(str, "DrawPath") == 0 )
		    drawpath(buf, FALSE);
		else if ( strcmp(str, "BeginObject") == 0 )
		    beginpath(buf, TRUE);
		else if ( strcmp(str, "EndObject") == 0 )
		    drawpath(buf, TRUE);
		else if ( strcmp(str, "NewBaseline") == 0 )
		    newbaseline(buf);
		else if ( strcmp(str, "DrawText") == 0 )
		    drawtext(buf);
		else if ( strcmp(str, "SetText") == 0 )
		    settext(buf);
		else if ( strcmp(str, "SetColor") == 0 )  {
		    newcolor(buf);
		    setcolor();
		} else if ( strcmp(str, "Sync") == 0 )  {
		    if (tracked)
			tracked = -1;
		    subfont = 0;
		    t_sf(1);
		    xymove(hpos, vpos);
		} else if ( strcmp(str, "PSSetup") == 0 ) {
		    fprintf(gf, "%s", buf);
		} else if ( strcmp(str, "PS") == 0 || strcmp(str, "PostScript") == 0 )  {
		    endtext();
		    /* xymove(hpos, vpos); ul90-22006 */
		    fprintf(tf, "%s", buf);
		}   /* End else */
		goto done;
    }	/* End switch */

    while ( (c = getc(fp)) != '\n'  &&  c != EOF ) ;
done:
    free(buf);

}   /* End of devcntrl */


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


void
fontinit(void)


{


    char	*descp;			/* for reading the DESC file */
    char	*filebase;		/* the whole thing goes here */
    int		i;			/* loop index */


/*
 *
 * Reads *realdev's DESC file and uses what's there to initialize things like
 * the list of available point sizes. Old versions of the program used *devname's
 * DESC file to initialize nfonts, but that meant we needed to have *devname's
 * binary font files available for emulation. That restriction has been removed
 * and we now set nfonts using the "x font" commands in the input file, so by the
 * time we get here all we really need is *realdev. In fact devcntrl() reads the
 * device name from the "x T ..." command, but almost immediately replaces it with
 * string *realdev so we end up using *realdev's DESC file. Later on (in
 * t_font()) we mount all of *realdev's special fonts after the last legitimate
 * font position, just to be sure device emulation works reasonably well - there's
 * no guarantee *devname's special fonts match what's needed when *realdev's tables
 * are used.
 * 
 */


    snprintf(temp, sizeof temp, "%s/dev%s/FONTMAP", fontdir, devname);
    rdftmap(temp);
    snprintf(temp, sizeof temp, "%s/dev%s/DESC", fontdir, devname);
    if ( (descp = readdesc(temp)) == 0 )
	error(FATAL, "can't open tables for %s", temp);

    memcpy(&dev, descp, sizeof dev);

    nfonts = 0;				/* was dev.nfonts - now set in t_fp() */
    nsizes = dev.nsizes;
    nchtab = dev.nchtab;
    unitwidth = dev.unitwidth;

    filebase = &descp[sizeof dev];

    pstab = (int *) filebase;
    chtab = (short *)(pstab + nsizes + 1);
    chname = (char *) (chtab + nchtab);
    fsize = 3 * 255 + nchtab + 128 - 32 + sizeof(struct Font);

    fitab = calloc(NFONT+1, sizeof *fitab);
    fontab = calloc(NFONT+1, sizeof *fontab);
    codetab = calloc(NFONT+1, sizeof *codetab);
    kerntab = calloc(NFONT+1, sizeof *kerntab);
    fontbase = calloc(NFONT+1, sizeof *fontbase);

    for ( i = 1; i <= NFONT; i++ )  {	/* so loadfont() knows nothing's there */
	fontbase[i] = NULL;
    }	/* End for */

    if ( (downloaded = (char *) calloc(nchtab + 128, sizeof(char))) == NULL )
	error(FATAL, "no memory");

}   /* End of fontinit */


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


void
loadfont (
    int n,			/* load this font position */
    const char *s,		/* with the file for this font */
    char *s1,			/* taken from here - possibly */
    int forcespecial,		/* this is definitively a special font */
    int spec			/* map specification */
)


{


    char	*fpout = NULL;		/* for reading *s file */
    int		fin;			/* for reading *s.afm file */
    int		nw;			/* number of width table entries */
    const char	*p;
    char	*path;


/*
 *
 * Loads font position n with the binary font file for *s provided it's not
 * already there. If *s1 is NULL or points to the empty string we read files from
 * directory *fontdir/dev*devname, otherwise directory *s1 is used. If the first
 * open fails we try to map font *s into one we expect will be available, and then
 * we try again.
 *
 */


    if ( n < 0  ||  n > NFONT )		/* make sure it's a legal position */
	error(FATAL, "illegal fp command %d %s", n, s);

    if ( fontbase[n] != NULL && strcmp(s, fontbase[n]->namefont) == 0 )
	return;

    path = temp;
    if (s1 && strchr(s1, '/') != NULL)
	path = afmdecodepath(s1);
    else if (s1 && strstr(s1, ".afm") != NULL)
	snprintf(temp, sizeof temp, "%s/dev%s/%s", fontdir, devname, s1);
    else if (strchr(s, '/') != NULL) {
	path = afmdecodepath(s);
	if (spec == 0 && s1)
	    spec = atoi(s1);
    } else if (strstr(s, ".afm") != NULL) {
	snprintf(temp, sizeof temp, "%s/dev%s/%s", fontdir, devname, s);
	if (spec == 0 && s1)
	    spec = atoi(s1);
    } else snprintf(temp, sizeof temp, "%s/dev%s/%s.afm", fontdir, devname, s);

    if ( (fin = open(path, O_RDONLY)) >= 0 )  {
	struct afmtab	*a;
	struct stat	st;
	char	*contents;
	int	i;
	if ((p = strrchr(s, '/')) == NULL)
	    p = s;
	else
	    p++;
	if (p[0] == 'S' && (p[1] == '\0' || (digitchar(p[1]&0377) &&
		p[2] == '\0') || p[2] == '.'))
	    forcespecial = 1;
	for (i = 0; i < afmcount; i++)
	    if (afmfonts[i] && strcmp(afmfonts[i]->path, path) == 0 &&
		    afmfonts[i]->spec == spec) {
		a = afmfonts[i];
		close(fin);
		goto have;
	    }
	if ((a = calloc(1, sizeof *a)) == NULL ||
		fstat(fin, &st) < 0 ||
		(contents = malloc(st.st_size+1)) == NULL ||
		read(fin, contents, st.st_size) != st.st_size) {
	    free(a);
	    close(fin);
	    goto fail;
	}
	close(fin);
	a->path = strdup(path);
	if (path != temp)
	    free(path);
	a->file = s;
	a->spec = spec;
	if (afmget(a, contents, st.st_size) < 0) {
	    free(a);
	    free(contents);
	    goto fail;
	}
	free(contents);
	afmfonts = realloc(afmfonts, (afmcount+1) * sizeof *afmfonts);
	afmfonts[afmcount] = a;
	snprintf(a->Font.intname, sizeof a->Font.intname,
	    "%d", dev.nfonts + ++afmcount);
	if (forcespecial)
	    a->Font.specfont = 1;
have:   fontbase[n] = &a->Font;
	fontab[n] = a->fontab;
	codetab[n] = a->codetab;
	fitab[n] = a->fitab;
    	t_fp(n, a->fontname, fontbase[n]->intname, a);
	goto done;
    }
    if (strchr(s, '/') != NULL)
	goto fail;
    if ( s1 == NULL || s1[0] == '\0' )
	snprintf(temp, sizeof temp, "%s/dev%s/%s", fontdir, devname, s);
    else snprintf(temp, sizeof temp, "%s/%s", s1, s);

    if ( access(temp, R_OK) < 0 ) 
        snprintf(temp, sizeof temp, "%s/dev%s/%s",
	    fontdir, devname, mapfont(s));
    if ((fpout = readfont(temp, &dev, 0)) == NULL)
    fail:   error(FATAL, "can't open font table %s", temp);

    if ( fontbase[n] != NULL )		/* something's already there */
	free(fontbase[n]);		/* so release the memory first */

    fontbase[n] = (struct Font *)fpout;

    p = (char *) fontbase[n] + sizeof(struct Font);
    nw = fontbase[n]->nwfont & BMASK;
    makefont(n, p, NULL, p + 2 * nw, p + 3 * nw, nw);

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

done:
    if ( smnt == 0 && (fontbase[n]->specfont == 1 || forcespecial) )
	smnt = n;
    if (fontbase[n]->specfont == 1 || forcespecial)
	gotregular = TRUE;

    if ( debug == ON )
	fontprint(n);

}   /* End of loadfont */


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


void
loadspecial(void)


{


    char	*p;			/* for next binary font file */
    int		nw;			/* width entries in next font */
    int		i;			/* loop index */


/*
 *
 * Loads all the special fonts after the last legal font position. Mostly used
 * for device emulation, but we'll do it no matter what. Needed because there's
 * no consistency in special fonts across different devices, and relying on having
 * them mounted in the input file doesn't guarantee the whole collection will be
 * there. The special fonts are determined and mounted using the copy of the
 * DESC file that's been read into memory. Initially had this stuff at the
 * end of fontinit(), but we now don't know nfonts until much later.
 *
 */

    if ( gotregular == FALSE )
	loadfont(++nfonts, ((struct Font *)(&chname[dev.lchname]))->namefont,
		NULL, 0, 0);

    if ( gotspecial == FALSE )
	for ( i = 1, p = chname + dev.lchname; i <= dev.nfonts; i++ )  {
	    nw = *p & BMASK;
	    if ( ((struct Font *) p)->specfont == 1 )
		loadfont(++nfonts, ((struct Font *)p)->namefont, NULL, 1, 0);
	    p += 3 * nw + dev.nchtab + 128 - 32 + sizeof(struct Font);
	}   /* End for */

    gotregular = TRUE;
    gotspecial = TRUE;

}   /* End of loadspecial */


/*****************************************************************************/
static const char *defaultFonts[] =
	{ "R", "I", "B", "BI", "CW", "H", "HB", "HX", "S1", "S", NULL };

void
loaddefault(void)
{
  int i;

  for (i = 0; defaultFonts[i] != NULL ; i++)
    loadfont(++nfonts, defaultFonts[i], NULL, defaultFonts[i][0] == 'S', 0);
}


void
fontprint (
    int i			/* font's index in fontbase[] */
)


{


    int		j, n;
    char	*p;


/*
 *
 * Debugging routine that dumps data about the font mounted in position i.
 *
 */


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

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

    fprintf(tf, "base=0%lo, nchars=%d, spec=%d, name=%s, widtab=0%lo, fitab=0%lo\n",
	    (long)p, n, fontbase[i]->specfont, fontbase[i]->namefont, (long)fontab[i], (long)fitab[i]);

    fprintf(tf, "widths:\n");
    for ( j = 0; j <= n; j++ )  {
	fprintf(tf, " %2d", fontab[i][j]);
	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]);
	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]);
	if ( j % 20 == 19 ) putc('\n', tf);
    }	/* End for */

    putc('\n', tf);

}   /* End of fontprint */


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


const char *
mapfont (
    const char *name			/* troff wanted this font */
)


{


    int		i;			/* loop index */


/*
 *
 * If loadfont() can't find font *name we map it into something else that should
 * be available and return a pointer to the new name. Used mostly for emulating
 * devices like the APS-5.
 *
 */


    name = mapft(name);
    for ( i = 0; fontmap[i].name != NULL; i++ )
	if ( strcmp(name, fontmap[i].name) == 0 )
	    return(fontmap[i].use);

    switch ( *++name )  {
	case 'I':
		return("I");

	case 'B':
		return("B");

	case 'X':
		return("BI");

	default:
		return("R");
    }	/* End switch */

}   /* End of mapfont */


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


void
getdevmap(void)


{


    FILE	*fp;			/* for reading the device fontmap file */
    int		i = 0;			/* number of mapping pairs we've read */
    int		c;			/* for skipping lines */


/*
 *
 * Looks for the device font mapping file *fontdir/dev*realdev/fontmaps/devname.
 * The file, if it exists, should be an ASCII file containing pairs of one or two
 * character font names per line. The first name is the font troff will be asking
 * for and the second is the one we'll use. Comments are lines that begin with
 * a '#' as the first non-white space character on a line. The devfontmap list
 * ends with a member that has the empty string in the name field.
 *
 */


    snprintf(temp, sizeof temp, "%s/dev%s/fontmaps/%s",
		    fontdir, realdev, devname);

    if ( devfontmap == NULL && (fp = fopen(temp, "r")) != NULL )  {
	devfontmap = (Devfontmap *) malloc(10 * sizeof(Devfontmap));

	while ( sget(temp, sizeof temp, fp) == 1 ) {
	    if ( temp[0] != '#' && strlen(temp) < 3 )
		if ( sget(&temp[3], sizeof temp - 3, fp) == 1 &&
				strlen(&temp[3]) < 3 )  {
		    n_strcpy((devfontmap + i)->name, temp,
		        sizeof(devfontmap->name));
		    n_strcpy((devfontmap + i)->use, &temp[3],
		        sizeof(devfontmap->use));
		    if ( ++i % 10 == 0 )
			devfontmap = (Devfontmap *) realloc(devfontmap, (i + 10) * sizeof(Devfontmap));
		}   /* End if */
	    while ( (c = getc(fp)) != '\n' && c != EOF ) ;
	}   /* End while */

	(devfontmap + i)->name[0] = '\0';	/* end the list we just read */
	fclose(fp);
    }	/* End if */

}   /* End of getdevmap */


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


char *
mapdevfont(char *str)


{


    int		i;


/*
 *
 * Called immediately before loadfont() after an 'x font' command is recognized.
 * Takes the font name that troff asked for, looks it up in the devfontmap list,
 * and returns the mapped name to the caller. No mapping is done if the devfontmap
 * list is empty or font *str isn't found in the list.
 *
 */


    if ( devfontmap != NULL )
	for ( i = 0; (devfontmap + i)->name[0] != '\0'; i++ )
	    if ( strcmp((devfontmap + i)->name, str) == 0 )
		return((devfontmap + i)->use);

    return(str);

}   /* End of mapdevfont */


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


void
reset(void)


{


/*
 *
 * Resets the variables that keep track of the printer's current position, font,
 * and size. Typically used after a restore/save pair (eg. when we finish with a
 * page) to make sure we force the printer back into sync (in terms of the font
 * and current point) before text is printed.
 *
 */


    lastx = -(slop + 1);
    savey = lasty = -1;
    lastfont = lastsubfont = lastsize = -1;
    if (tracked)
	tracked = -1;

}   /* End of reset */


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


void
resetpos(void)


{


/*
 *
 * Resets the variables that keep track of the printer's current position. Used
 * when there's a chance we've lost track of the printer's current position or
 * done something that may have wiped it out, and we want to force dpost to set
 * the printer's position before printing text or whatever. For example stroke or
 * fill implicitly do a newpath, and that wipes out the current point, unless the
 * calls were bracketed by a gsave/grestore pair. 
 *
 */


    lastx = -(slop + 1);
    savey = lasty = -1;

}   /* End of resetpos */


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


void
t_init(void)


{


    static int	initialized = FALSE;	/* only do most things once */


/*
 *
 * Called from devcntrl() after an "x init" command is read. Things only work if
 * we've already seen the "x res" command, and much of the stuff, including the
 * call to setup, should only be done once. Restricting everything to one call of
 * setup (ie. the one in the prologue) means all the input files must have been
 * formatted for the same device.
 *
 */


    endtext();				/* moved  - for cat'ed troff files */

    if ( initialized == FALSE )  {	/* only do this stuff once per job */
	fontinit();
	gotspecial = FALSE;
	gotregular = FALSE;
	widthfac = (float) res /dev.res;
	if (dev.afmfonts) {
	    if (Sflag == 0)
		pointslop = 0;
	}
	if (eflag == 0)
	    realencoding = encoding = dev.encoding;
	if (encoding == 5) {
	    LanguageLevel = max(LanguageLevel, 2);
	    Binary++;
	}
	slop = pointslop * res / POINTS + .5;
	rvslop = res * .025;
	setup();
	initialized = TRUE;
    }	/* End if */

    hpos = vpos = 0;			/* upper left corner */
    setsize(t_size(10), 0);		/* start somewhere */
    reset();				/* force position and font stuff - later */

}   /* End of t_init */


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

void
needresource(const char *s, ...)
{
	va_list	ap;

	if (nfcount++ == 0)
	    fprintf(nf, "%%%%DocumentNeededResources: ");
	else
	    fprintf(nf, "%%%%+ ");
	va_start(ap, s);
	vfprintf(nf, s, ap);
	va_end(ap);
	putc('\n', nf);
}


static struct supplylist {
	struct supplylist	*next;
	char	*font;
	char	*file;
	char	*type;
	int	done;
} *supplylist;

void
t_supply(char *fnt)		/* supply a font */
{
    struct supplylist	*sp;
    char	*np, *file, *type = NULL, c;

    while (*fnt == ' ' || *fnt == '\t')
	fnt++;
    for (np = fnt; *np && *np != ' ' && *np != '\t' && *np != '\n'; np++);
    if (*np == '\0' || *np == '\n')
	return;
    *np = '\0';
    file = &np[1];
    while (*file == ' ' || *file == '\t')
	file++;
    for (np = file; *np && *np != ' ' && *np != '\t' && *np != '\n'; np++);
    c = *np;
    *np = '\0';
    if (c != '\0' && c != '\n') {
	type = &np[1];
	while (*type == ' ' || *type == '\t')
	    type++;
	for (np = type; *np && *np != ' ' &&
	    	*np != '\t' && *np != '\n'; np++);
	*np = '\0';
    }
    for (sp = supplylist; sp; sp = sp->next)
	if (strcmp(sp->font, fnt) == 0)
	    return;
    sp = calloc(1, sizeof *sp);
    sp->font = strdup(fnt);
    sp->file = afmdecodepath(file);
    sp->type = type && *type ? strdup(type) : NULL;
    sp->next = supplylist;
    supplylist = sp;
}

static unsigned long
ple32(const char *cp)
{
    return (unsigned long)(cp[0]&0377) +
	((unsigned long)(cp[1]&0377) << 8) +
	((unsigned long)(cp[2]&0377) << 16) +
	((unsigned long)(cp[3]&0377) << 24);
}

static const char ps_adobe_font_[] = "%!PS-AdobeFont-";
static const char ps_truetypefont[] = "%!PS-TrueTypeFont";
static const char hex[] = "0123456789abcdef";

static void
supplypfb(char *fnt, char *path, FILE *fp)
{
    char	buf[30];
    size_t	i, n, length;
    int		c = EOF, type = 0, lastch = EOF;

    if (fread(buf, 1, 6, fp) != 6)
	error(FATAL, "no data in %s", path);
    if ((buf[0]&0377) != 0200 || (type = buf[1]) != 1)
	error(FATAL, "invalid header in %s", path);
    length = ple32(&buf[2]);
    n = 0;
    while (ps_adobe_font_[n] && --length > 0 && (c = getc(fp)) != EOF) {
	if (c != ps_adobe_font_[n++])
	    error(FATAL, "file %s does not start with \"%s\"",
		path, ps_adobe_font_);
    }
    while (--length > 0 && (c = getc(fp)) != EOF && c != '\r' && c != '\n');
    if (c != '\n') {
    	if ((c = getc(fp)) != '\n')
	   ungetc(c, fp);
    	else
	   length--;
    }
    if (sfcount++ == 0)
        fprintf(sf, "%%%%DocumentSuppliedResources: font %s\n", fnt);
    else
        fprintf(sf, "%%%%+ font %s\n", fnt);
    fprintf(rf, "%%%%BeginResource: font %s\n", fnt);
    for (;;) {
    	switch (type) {
    	case 1:
	    	while (length > 0 && (c = getc(fp)) != EOF) {
		    length--;
		    switch (c) {
		    case '\r':
    			    if ((c = getc(fp)) != '\n')
	    		        ungetc(c, fp);
    			    else
	    		        length--;
			    putc('\n', rf);
			    lastch = '\n';
			    break;
		    case 0:
		   	    continue;
		    default:
			    putc(c, rf);
			    lastch = c;
		    }
	    	}
	    	if (c == EOF)
		    error(FATAL, "short text data in %s", path);
	    	break;
    	case 2:
	    	while (length) {
	    	    n = length > sizeof buf ? sizeof buf : length;
	    	    if (fread(buf, 1, n, fp) != n)
		    	error(FATAL, "short binary data in %s", path);
	    	    for (i = 0; i < n; i++) {
			putc(hex[(buf[i]&0360)>>4], rf);
			putc(hex[buf[i]&017], rf);
		    }
	    	    putc('\n', rf);
		    lastch = '\n';
		    length -= n;
	    	}
	    	break;
    	case 3:
		if (lastch != '\n')
		    putc('\n', rf);
    		fprintf(rf, "%%%%EndResource\n");
		fclose(fp);
		return;
	default:
	        error(FATAL, "invalid header type %d in %s", path, type);
    	}
        if ((n = fread(buf, 1, 6, fp)) != 6 && (buf[1] != 3 || n < 2))
	    error(FATAL, "missing header in %s", path);
        if ((buf[0]&0377) != 0200)
	    error(FATAL, "invalid header in %s", path);
	if ((type = buf[1]) != 3)
            length = ple32(&buf[2]);
    }
}

static void
supplyotf(char *fnt, char *path, FILE *fp)
{
    static int	cffcount;
    struct stat	st;
    char	*contents;
    size_t	sz, offset, length, i;
    int	fsType;
    const char StartData[] = " StartData ";

    if (fstat(fileno(fp), &st) < 0)
	error(FATAL, "cannot stat %s", path);
    sz = st.st_size;
    contents = malloc(sz);
    if (fread(contents, 1, sz, fp) != sz)
	error(FATAL, "cannot read %s", path);
    fclose(fp);
    if ((fsType = otfcff(path, contents, sz, &offset, &length)) < 0) {
	free(contents);
	return;
    }
    /*
     * Adobe Technical Note #5176, "The Compact Font Format
     * Specification", Version 1.0, 12/4/2003, p. 53 proposes
     * a weird syntax for CFF DSC comments ("ProcSet" etc.);
     * Adobe Distiller 7 complains about it with DSC warnings
     * enabled. What follows is an attempt to fix this.
     */
    if (cffcount++ == 0) {
	fprintf(rf, "%%%%IncludeResource: procset FontSetInit 0 0\n");
	needresource("procset FontSetInit 0 0");
    }
    if (sfcount++ == 0)
        fprintf(sf, "%%%%DocumentSuppliedResources: font %s\n", fnt);
    else
        fprintf(sf, "%%%%+ font %s\n", fnt);
    fprintf(rf, "%%%%BeginResource: font %s\n", fnt);
    fprintf(rf, "/FontSetInit /ProcSet findresource begin\n");
    if (encoding == 5) {
	fprintf(rf, "%%%%BeginData: %ld Binary Bytes\n",
		(long)(length + 13 + strlen(fnt) + 12));
	fprintf(rf, "/%s %12ld StartData ", fnt, (long)length);
	fwrite(&contents[offset], 1, length, rf);
	fprintf(rf, "\n%%%%EndData\n");
    } else {
	fprintf(rf, "/%s %ld ", fnt, (long)length);
	fprintf(rf, "currentfile /ASCIIHexDecode filter cvx exec\n");
	for (i = 0; StartData[i]; i++) {
	    putc(hex[(StartData[i]&0360)>>4], rf);
	    putc(hex[StartData[i]&017], rf);
	}
	putc('\n', rf);
	for (i = offset; i < offset+length; i++) {
	    putc(hex[(contents[i]&0360)>>4], rf);
	    putc(hex[contents[i]&017], rf);
	    if (i > offset && (i - offset + 1) % 34 == 0)
		putc('\n', rf);
	}
	fprintf(rf, ">\n");
    }
    fprintf(rf, "%%%%EndResource\n");
    free(contents);
    LanguageLevel = max(LanguageLevel, 3);
}

static void
supplyttf(char *fnt, char *path, FILE *fp)
{
    struct stat	st;
    char	*contents;
    size_t	sz;

    if (fstat(fileno(fp), &st) < 0)
	error(FATAL, "cannot stat %s", path);
    sz = st.st_size;
    contents = malloc(sz);
    if (fread(contents, 1, sz, fp) != sz)
	error(FATAL, "cannot read %s", path);
    fclose(fp);
    if (sfcount++ == 0)
	fprintf(sf, "%%%%DocumentSuppliedResources: font %s\n", fnt);
    else
	fprintf(sf, "%%%%+ font %s\n", fnt);
    fprintf(rf, "%%%%BeginResource: font %s\n", fnt);
    otft42(fnt, path, contents, sz, rf);
    fprintf(rf, "%%%%EndResource\n");
    free(contents);
    LanguageLevel = max(LanguageLevel, 2);
}

static void
supply1(char *fnt, char *file, char *type)
{
    FILE *fp;
    char linebuf[4096], c;

    if (strchr(file, '/') == 0) {
    	snprintf(temp, sizeof temp, "%s/dev%s/%s.%s",
		fontdir, devname, file, type);
	file = temp;
    }
    if ((fp = fopen(file, "r")) == NULL)
	error(FATAL, "can't open %s", file);
    if (type == NULL) {
	c = getc(fp);
	ungetc(c, fp);
	type = c == '\200' ? "pfb" : c == 'O' ? "otf" :
		c == 0 || c == 't' ? "ttf" : "anything";
    }
    if (strcmp(type, "pfb") == 0) {
	supplypfb(fnt, file, fp);
	return;
    }
    if (strcmp(type, "otf") == 0) {
	supplyotf(fnt, file, fp);
	return;
    }
    if (strcmp(type, "ttf") == 0) {
	supplyttf(fnt, file, fp);
	return;
    }
    if (fgets(linebuf, sizeof linebuf, fp) == NULL)
        error(FATAL, "missing data in %s", file);
    if (strncmp(linebuf, ps_adobe_font_, strlen(ps_adobe_font_)) &&
	    strncmp(linebuf, ps_truetypefont, strlen(ps_truetypefont)))
	error(FATAL, "file %s does not start with \"%s\" or \"%s\"",
			    file, ps_adobe_font_, ps_truetypefont);
    if (sfcount++ == 0)
        fprintf(sf, "%%%%DocumentSuppliedResources: font %s\n", fnt);
    else
        fprintf(sf, "%%%%+ font %s\n", fnt);
    fprintf(rf, "%%%%BeginResource: font %s\n", fnt);
    while (fgets(linebuf, sizeof linebuf, fp) != NULL)
	fputs(linebuf, rf);
    fclose(fp);
    fprintf(rf, "%%%%EndResource\n");
}

static void
t_dosupply(const char *fnt)
{
    struct supplylist	*sp;

    for (sp = supplylist; sp; sp = sp->next)
	if (strcmp(sp->font, fnt) == 0) {
	    if (sp->done == 0) {
		supply1(sp->font, sp->file, sp->type);
		sp->done = 1;
	    }
	    return;
	}
    needresource("font %s", fnt);
}

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

static void
boxcmp(const char *name, struct box *bp, int a, int b, int c, int d)
{
    if (bp->flag && (a != bp->val[0] || b != bp->val[1] ||
	    c != bp->val[2] || d != bp->val[3]))
	error(NON_FATAL, "%s has changed, using new values", name);
}

static void
t_papersize(char *buf)
{
    int	x, y, setmedia = 0;

    if (sscanf(buf, "%d %d %d", &x, &y, &setmedia) < 2)
	return;
    boxcmp("Media size", &mediasize, 0, 0, x, y);
    mediasize.val[2] = x;
    mediasize.val[3] = y;
    mediasize.flag |= 1;
    if (setmedia)
	mediasize.flag |= 2;
    pagelength = y * 72.0 / res;
}

static void
t_cutat(const char *name, struct box *bp, char *buf)
{
    int	c[4], i;

    if (sscanf(buf, "%d %d %d %d", &c[0], &c[1], &c[2], &c[3]) < 4)
	return;
    boxcmp(name, bp, c[0], c[1], c[2], c[3]);
    for (i = 0; i < 4; i++)
	bp->val[i] = c[i];
    bp->flag |= 1;
}

/*****************************************************************************/
void
t_page (
    int pg			/* troff's current page number */
)


{


    static int	lastpg = 0;		/* last one we started - for ENDPAGE */


/*
 *
 * Called whenever we've finished the last page and want to get ready for the
 * next one. Also used at the end of each input file, so we have to be careful
 * about what's done. The first time through (up to the redirect(pg) call) output
 * goes to /dev/null because of the redirect(-1) call made in conv().
 *
 * Adobe now recommends that the showpage operator occur after the page level
 * restore so it can be easily redefined to have side-effects in the printer's VM.
 * Although it seems reasonable I haven't implemented it, because it makes other
 * things, like selectively setting manual feed or choosing an alternate paper
 * tray, clumsy - at least on a per page basis. 
 *
 */


    if ( tf == stdout )			/* count the last page */
	printed++;

    endtext();				/* print the last line? */

    fprintf(tf, "_marks\n");
    fprintf(tf, "cleartomark\n");
    fprintf(tf, "showpage\n");
    fprintf(tf, "restore\n");
    fprintf(tf, "%s %d %d\n", ENDPAGE, lastpg, printed);

    redirect(pg);

    fprintf(tf, "%s %d %d\n", PAGE, pg, printed+1);
    fprintf(tf, "save\n");
    fprintf(tf, "mark\n");
    writerequest(printed+1, tf);
    fprintf(tf, "%d pagesetup\n", printed+1);
    setcolor();

    lastpg = pg;			/* for the next ENDPAGE comment */
    hpos = vpos = 0;			/* get ready for the next page */
    reset();				/* force position and font stuff - later */

    seenpage = TRUE;

}   /* End of t_page */


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


void
t_newline(void)


{


/*
 *
 * Just finished the last line. All we do is set the horizontal position to 0,
 * although even that probably isn't necessary.
 *
 */


    hpos = 0;

}   /* End of t_newline */


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


int 
t_size (
    int n			/* convert this to an internal size */
)


{


    int		i;			/* loop index */


/*
 *
 * Converts a point size into an internal size that can be used as an index into
 * pstab[]. The internal size is one plus the index of the least upper bound of
 * n in pstab[], or nsizes if n is larger than all the listed sizes.
 *
 */


    if ( n <= pstab[0] )
	return(1);
    else if (n >= pstab[nsizes-1])
	return(nsizes);

    for ( i = 0; n > pstab[i]; i++ ) ;

    return(i+1);

}   /* End of t_size */


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


void
setsize (
    int n, float f			/* new internal size */
)


{


/*
 *
 * Now using internal size n, where pstab[n-1] is the best available approximation
 * to the size troff asked for.
 *
 */


    size = n;
    fractsize = f;
    lasthorscale = horscale = 1.0;

}   /* End of setsize */


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


void
t_fp (
    int n,			/* this position */
    char *s,			/* now has this font mounted */
    char *si,			/* its internal number */
    void *a
)


{


/*
 *
 * Updates nfonts and the array that keeps track of the mounted fonts. Called from
 * loadfont() after an "x font pos font" command is read, and if pos is larger than
 * the current value assigned to nfonts we set gotspecial to FALSE to make sure
 * t_font() loads all the special fonts after the last legitimate font position.
 *
 */


    fontname[n].name = s;
    fontname[n].number = atoi(si);
    fontname[n].afm = a;

    if ( n == lastfont )		/* force a call to t_sf() */
	lastfont = lastsubfont = -1;

    if ( n > nfonts )  {		/* got more positions */
	nfonts = n;
	gotspecial = FALSE;
	gotregular = FALSE;
    }	/* End if */

}   /* End of t_fp */


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


int 
t_font (
    char *s			/* use font in this position next */
)


{


    int		n;


/*
 *
 * Converts the string *s into an integer and checks to make sure it's a legal
 * font position. Also arranges to mount all the special fonts after the last
 * legitimate font (by calling loadspecial()), provided it hasn't already been
 * done.
 *
 */


    n = atoi(s);

    if ( seenpage == TRUE )  {
	if ( n < 0  ||  n > nfonts )
	    error(FATAL, "illegal font position %d", n);

	if ( gotspecial == FALSE || gotregular == FALSE )
	    loadspecial();
    }	/* End if */

    if (tracked)
        tracked = -1;
    track = 0;

    return(n);

}   /* End of t_font */

/*****************************************************************************/
static void
sethorscale(char *buf)
{
    horscale = atof(buf);
}

/*****************************************************************************/
static void
t_track(char *buf)
{
    int	t;

/*
 * Handling of track kerning. troff provides this parameter as a hint
 * only. dpost can use it in combination with the PostScript "ashow"
 * operator. When the variable "track" is not zero, the printer is
 * advised to perform tracking by the given amount. This relieves us
 * of the need to adjust the character position explicitly after each
 * character and thus greatly reduces the size of the output.
 *
 * Currently this is done in encodings 0, 4, and 5 only.
 */

    if (sscanf(buf, "%d", &t) != 1)
	t = 0;
    if (t != lasttrack) {
	tracked = -1;
    } else if (t && tracked != -1)
	tracked = 1;
    track = t;
}

static void
t_strack(void)
{
    endtext();
    fprintf(tf, "%d T\n", track);
    if (tf == stdout) {
	tracked = track != 0;
	lasttrack = track;
    }
}

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


void
setfont (
    int n			/* use the font mounted here */
)


{


/*
 *
 * troff wants to use the font that's been mounted in position n. All we do here
 * is update the variable that keeps track of the current position. PostScript
 * font changes are handled in t_sf(), and are only generated right before we're
 * ready to print or draw something.
 *
 */


    if ( n < 0 || n > NFONT )
	error(FATAL, "illegal font %d", n);
    if ( fontname[n].name == NULL && fontname[n].number == 0)
	loaddefault();
    if ( fontname[n].name == NULL && fontname[n].number == 0)
	error(FATAL,
  "font %d not loaded: check 'dpost' input for 'x font %d XXX' before 'f%d'",
		n, n, n);

    font = n;
    subfont = 0;
    lasthorscale = horscale = 1.0;

}   /* End of setfont */


/*****************************************************************************/
static void
endvec(struct afmtab *a, int n)
{
    fprintf(gf, "] def\n");
    fprintf(gf, "\
/%s findfont\n\
dup length dict begin\n\
  {1 index /FID ne {def} {pop pop} ifelse} forall\n\
  /Encoding Encoding-@%s@%d def\n\
  currentdict\n\
end\n",
	a->fontname, a->Font.intname, n);
    if (a->spec & SPEC_S) {
	fprintf(gf, "/%s-tmp-@%s", a->fontname, a->Font.intname);
	if (n) fprintf(gf, "@%d", n);
	fprintf(gf, " exch definefont pop\n");
	fprintf(gf, "_Sdefsadj\n");
	fprintf(gf, "/%s-tmp-@%s", a->fontname, a->Font.intname);
	if (n) fprintf(gf, "@%d", n);
	fprintf(gf, " /%s-@%s", a->fontname, a->Font.intname);
	if (n) fprintf(gf, "@%d", n);
	fprintf(gf, " Sdefs cf\n");
	fprintf(gf, "/%s-tmp-@%s", a->fontname, a->Font.intname);
	if (n) fprintf(gf, "@%d", n);
	fprintf(gf, " undefinefont\n");
    } else if (a->spec & SPEC_S1) {
	fprintf(gf, "/%s-tmp-@%s", a->fontname, a->Font.intname);
	if (n) fprintf(gf, "@%d", n);
	fprintf(gf, " exch definefont pop\n");
	fprintf(gf, "/%s-tmp-@%s", a->fontname, a->Font.intname);
	if (n) fprintf(gf, "@%d", n);
	fprintf(gf, " /%s-@%s", a->fontname, a->Font.intname);
	if (n) fprintf(gf, "@%d", n);
	fprintf(gf, " S1defs cf\n");
	fprintf(gf, "/%s-tmp-@%s", a->fontname, a->Font.intname);
	if (n) fprintf(gf, "@%d", n);
	fprintf(gf, " undefinefont\n");
    } else if (n)
	fprintf(gf, "/%s-@%s@%d exch definefont pop\n",
	    a->fontname, a->Font.intname, n);
    else
	fprintf(gf, "/%s-@%s exch definefont pop\n",
	    a->fontname, a->Font.intname);
    fprintf(gf, "/@%s", a->Font.intname);
    if (n)
	fprintf(gf, "@%d", n);
    fprintf(gf, " /%s-@%s", a->fontname, a->Font.intname);
    if (n)
	fprintf(gf, "@%d", n);
    fprintf(gf, " def\n");
    fprintf(gf, "/&%s", a->Font.intname);
    if (n)
	fprintf(gf, "@%d", n);
    fprintf(gf, " {@%s", a->Font.intname);
    if (n)
	fprintf(gf, "@%d", n);
    fprintf(gf, " F} bind def\n");
}

static void
printencsep(int *colp)
{
    if (*colp >= 60) {
	putc('\n', gf);
	*colp = 0;
    } else {
	putc(' ', gf);
	(*colp)++;
    }
}

static int *
printencvector(struct afmtab *a)
{
    int	i, j, k, n, col = 0, s;
    int	*encmap = NULL;

    fprintf(gf, "/Encoding-@%s@0 [\n", a->Font.intname);
    col = 0;
    /*
     * First, write excess entries into the positions from 1 to 31
     * for later squeezing of characters >= 0400.
     */
    s = 128 - 32;
    encmap = calloc(256 + nchtab + a->nchars, sizeof *encmap);
    col += fprintf(gf, "/.notdef");
    printencsep(&col);
    for (j = 1; j < 32; j++) {
	while (s < a->nchars + 128 - 32 + nchtab &&
		((k = a->fitab[s]) == 0 ||
		 a->nametab[k] == NULL))
	    s++;
	if (s < a->nchars + 128 - 32 + nchtab &&
		(k = a->fitab[s]) != 0 &&
		k < a->nchars &&
		a->nametab[k] != NULL) {
	    encmap[s - 128 + 32] = j;
	    col += fprintenc(gf, a->nametab[k]);
	    printencsep(&col);
	    s++;
	} else {
	    col += fprintf(gf, "/.notdef");
	    printencsep(&col);
	}
    }
    col += fprintf(gf, "/space");
    printencsep(&col);
    for (i = 1; i < a->nchars + 128 - 32 + nchtab && i < 256 - 32; i++) {
	if (i < 128 - 32 && (k = a->fitab[i]) != 0 && k < a->nchars &&
		a->nametab[k] != NULL) {
	    col += fprintenc(gf, a->nametab[k]);
	    printencsep(&col);
	} else {
	    while (s < a->nchars + 128 - 32 + nchtab &&
		((k = a->fitab[s]) == 0 ||
	 	a->nametab[k] == NULL))
	    s++;
	    if (s < a->nchars + 128 - 32 + nchtab &&
			(k = a->fitab[s]) != 0 &&
			k < a->nchars &&
			a->nametab[k] != NULL) {
		encmap[s - 128 + 32] = i + 32;
		col += fprintenc(gf, a->nametab[k]);
		printencsep(&col);
		s++;
	    } else {
		col += fprintf(gf, "/.notdef");
		printencsep(&col);
	    }
	}
    }
    endvec(a, 0);
    n = 1;
    while (s < a->nchars + 128 - 32 + nchtab) {
	fprintf(gf, "/Encoding-@%s@%d [\n", a->Font.intname, n);
	col = 0;
	for (i = 0; i < 256; i++) {
	    while (s < a->nchars + 128 - 32 + nchtab &&
		    ((k = a->fitab[s]) == 0 ||
		     a->nametab[k] == NULL))
		s++;
	    if (s < a->nchars + 128 - 32 + nchtab &&
		    (k = a->fitab[s]) != 0 &&
		    k < a->nchars &&
		    a->nametab[k] != NULL) {
	        encmap[s - 128 + 32] = i | n << 8;
	        col += fprintenc(gf, a->nametab[k]);
	        printencsep(&col);
	        s++;
	    } else {
	        col += fprintf(gf, "/.notdef");
	        printencsep(&col);
	    }
	}
	endvec(a, n++);
    }
    return encmap;
}
/*****************************************************************************/


void
t_sf(int forceflush)


{


    int		fnum;			/* internal font number */
    int		cmd;			/* command to execute */


/*
 *
 * Called whenever we need to use a new font or size. Only done right before we
 * print a character. The seenfonts[] array keeps track of the fonts we've used.
 * Helps manage host resident fonts and the DOCUMENTFONTS comment that's put out
 * at the end of the job. The array is indexed by internal number. Only works for
 * fonts that have internal numbers less than or equal to MAXINTERNAL.
 *
 */


    if ( fontname[font].name == NULL )
	return;

    endtext();

    if ( (fnum = fontname[font].number) > MAXINTERNAL || fnum < 0 )
	fnum = 0;

    if ( fnum > 0 && seenfonts[fnum] == 0 && hostfontdir != NULL )  {
	snprintf(temp, sizeof temp, "%s/%s", hostfontdir, fontname[font].name);
	if ( access(temp, 04) == 0 )
	    doglobal(temp);
    }	/* End if */

    cmd = 'f';
    if (forceflush == 0) {
	if (font == lastfont && subfont == lastsubfont)
	    cmd = 's';
	else if (size == lastsize && fractsize == lastfractsize)
	    cmd = 'F';
    }
    if (horscale != 1.0)
	    cmd = 'h';
    if ( tf == stdout )  {
	lastfont = font;
	lastsubfont = subfont;
	lastsize = size;
	lastfractsize = fractsize;
	lasthorscale = horscale;
	if ( seenfonts[fnum] == 0 ) {
	    documentfonts();
	}
	if (fontname[font].afm && fontname[font].afm->encmap == NULL)
	    fontname[font].afm->encmap = printencvector(fontname[font].afm);
	seenfonts[fnum] = 1;
    }	/* End if */

    if (cmd == 'f' || cmd == 's' || cmd == 'h') {
        if (size != FRACTSIZE)
            fprintf(tf, "%d ", pstab[size-1]);
        else
	    fprintf(tf, "%g ", (double)fractsize);
    }
    if (fontname[font].afm && cmd == 'F') {
        if (subfont)
    	    fprintf(tf, "&%s@%d\n", fontname[font].afm->Font.intname, subfont);
        else
    	    fprintf(tf, "&%s\n", fontname[font].afm->Font.intname);
	cmd = 0;
    } else if (cmd == 'f' || cmd == 'F' || cmd == 'h') {
        if (fontname[font].afm && subfont)
    	    fprintf(tf, "@%s@%d ", fontname[font].afm->Font.intname, subfont);
        else if (fontname[font].afm)
    	    fprintf(tf, "@%s ", fontname[font].afm->Font.intname);
        else
    	    fprintf(tf, "%s ", fontname[font].name);
    }
    if (cmd == 'h')
	fprintf(tf, "%g ", horscale);
    if (cmd)
	fprintf(tf, "%c\n", cmd);

    if ( fontname[font].fontheight != 0 || fontname[font].fontslant != 0 ) {
	if (size != FRACTSIZE)
	    fprintf(tf, "%d %g changefont\n", fontname[font].fontslant, (fontname[font].fontheight != 0) ? (double)fontname[font].fontheight : pstab[size-1]);
	else
	    fprintf(tf, "%d %g changefont\n", fontname[font].fontslant, (fontname[font].fontheight != 0) ? (double)fontname[font].fontheight : (double)fractsize);
    }

    if (tracked < 0 || (tracked > 0 && forceflush))
	t_strack();

}   /* End of t_sf */


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


void
t_charht (
    int n, float f		/* use this as the character height */
)


{


/*
 *
 * Remembers the requested height, from 'x H n'. Forces a call to t_sf(), which
 * is where the real work is done, by setting lastfont to -1.
 *
 */

    if (n == FRACTSIZE)
        fontname[font].fontheight = f;
    else
    	fontname[font].fontheight = (n == pstab[size-1]) ? 0 : n;
    lastfont = lastsubfont = -1;

}   /* End of t_charht */


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


void
t_slant (
    int n			/* slant characters this many degrees */
)


{


/*
 *
 * Remembers the requested slant, from 'x X n'. Forces a call to t_sf(), which
 * is where the real work is done, by setting lastfont to -1.
 *
 */

    fontname[font].fontslant = n;
    lastfont = lastsubfont = -1;

}   /* End of t_slant */

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


void
t_reset (
    int c __unused	/* pause or restart */
)


{


/*
 *
 * Found an "x stop" or "x pause" command. Although nothing's done here we could
 * add code to reset everything so dpost could handle multiple files formatted for
 * different devices.
 *
 */


}   /* End of t_reset */


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


void
t_trailer(void)


{


/*
 *
 * Called after we find an "x trailer" in the input file. Forcing out the last
 * page is done at the end of conv(), but probably belongs here.
 *
 */


    endtext();

}   /* End of t_trailer */


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


void
hgoto (
    int n			/* new horizontal position */
)


{


/*
 *
 * Want to be at this absolute horizontal position next. Actual motion commands
 * are generated in oput(), charlib(), and the drawing routines.
 *
 */


    hpos = n;

}   /* End of hgoto */


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


void
hmot (
    int n			/* move this far horizontally */
)


{


/*
 *
 * Handles relative horizontal motion. troff's current positon, as recorded in
 * in hpos, is changed by n units. Usually called right before we're supposed to
 * print a character.
 *
 */


    hpos += n;

}   /* End of hmot */


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


void
vgoto (
    int n			/* new vertical position */
)


{


/*
 *
 * Moves vertically in troff's coordinate system to absolute position n.
 *
 */


    vpos = n;

}   /* End of vgoto */


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


void
vmot (
    int n			/* move this far vertically */
)


{


/*
 *
 * Handles relative vertical motion of n units in troff's coordinate system.
 *
 */


    vpos += n;

}   /* End of vmot */


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


void
xymove (
    int x,
    int y			/* this is where we want to be */
)


{


/*
 *
 * Makes sure the post-processor and printer agree about the current position.
 *
 */


    hgoto(x);
    vgoto(y);

    fprintf(tf, "%d %d m\n", hpos, vpos);

    lastx = hpos;
    savey = lasty = vpos;

}   /* End of xymove */


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


void
put1s (
    register char *s		/* find and print this character */
)


{


    static int		i = 0;		/* last one we found - usually */


/*
 *
 * *s points to the start of a two character string that represents one of troff's
 * special characters. To print it we first look for *s in the chname[] array using
 * chtab[i] to find the string representing character i in chname[]. If the lookup
 * is successful we add 128 to i and ask put1() to finish printing the character.
 * We remember the index where the last character was found because requests to
 * print a special character often come in bunches (eg. drawing lines with \(ru).
 *
 */


    if (s[0] == 'P' && s[1] == 'S' && s[2] != 0) {	/* PostScript name */ 
         int	m;
	 struct namecache	*np;
	 struct afmtab	*a;
	 if ((a = fontname[font].afm) != NULL &&
		 (np = afmnamelook(a, &s[2])) != NULL &&
		 ((m = np->fival[0]) != NOCODE ||
		  (m = np->fival[1]) != NOCODE)) {
	     put1(m+32);
	     return;
	 }
    }
    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 */


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


void
put1 (
    register int c		/* want to print this character */
)


{


    register int	i = 0;		/* character code from fitab */
    register int	j;		/* number of fonts we've checked so far */
    register int	k;		/* font we're currently looking at */
    int			*pw = NULL;	/* font widthtab and */
    unsigned short	*p = NULL;	/* and codetab where c was found */
    int			code;		/* code used to get c printed */
    int			ofont;		/* font when we started */


/*
 *
 * Arranges to have character c printed. If c < 128 it's a simple ASCII character,
 * otherwise it's a special character. Things done here have to agree with the way
 * the font tables were built by makedev, and work as follows. First we subtract
 * 32 from c because the tables don't record the non-graphic ASCII characters.
 * If fitab[k][c] isn't zero the character is on font k and the value is an index
 * that can be used to recover width and character code data from the other two
 * tables. If fitab[k][c] is zero the character isn't defined on font k and we
 * check the next font, which is found as follows. The current font is the first
 * one we check, and it's followed by a circular search of all the remaining fonts
 * that starts with the first special font and skips font position 0. If character
 * c is found somewhere besides the current font we change to that font and use
 * fitab[k][c] to locate missing data in the other two tables. The width of the
 * character can be found at fontab[k][c] while codetab[k][c] is whatever we
 * need to tell the printer to have character c printed. lastc records the real
 * name of the character because it's lost by the time oput() gets called but
 * charlib() may need it.
 *
 * Took all the debugging stuff out because at least this part of the program is
 * reasonably solid.
 *
 */


    lastc = c;				/* charlib() needs the name not the code */
    if ( (c -= 32) <= 0 )		/* probably never happens */
	return;

    k = ofont = font;

    if ( fitab[k] && (i = fitab[k][c]) != 0 )  {	/* it's on this font */
	p = codetab[font];
	pw = fontab[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 ( fitab[k] && (i = fitab[k][c]) != 0 )  {
		p = codetab[k];
		pw = fontab[k];
		setfont(k);
		break;
	    }	/* End if */
	}   /* End for */
    }	/* End else */

    lastw = 0;
    if ( i != 0 && (code = p[i]) != 0 )  {
	if (size != FRACTSIZE)
	    lastw = horscale * widthfac * ((pw[i] * pstab[size-1] + unitwidth/2) / unitwidth);
	else
	    lastw = horscale * widthfac * (int)((pw[i] * fractsize + unitwidth/2) / unitwidth);
	if (widthfac == 1)	/* ignore fractional parts since troff */
	    lastw = (int)lastw;	/* does the same */
	if (track && encoding != MAXENCODING+2)
	    lastw += track;
	if (code == NOCODE && fontname[k].afm)
	    code = c + 32;
	oput(code);
    }	/* End if */

    if ( font != ofont )
	setfont(ofont);

}   /* End of put1 */


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


static void
oprep(int stext)
{
    if ( textcount > MAXSTACK )		/* don't put too much on the stack? */
	endtext();

    if ( font != lastfont || size != lastsize || subfont != lastsubfont ||
	    (size == FRACTSIZE && fractsize != lastfractsize) ||
	    horscale != lasthorscale) {
	t_sf(0);
    }
    if (tracked < 0)
	t_strack();

    if ( vpos != lasty )
	endline();

    if (stext) {
        starttext();

        if ( fabsf(hpos - lastx) > slop )
	    endstring();
    }
    wordspace = 0;
}


void
oput (
    int c			/* want to print this character */
)


{


/*
 *
 * Arranges to print the character whose code is c in the current font. All the
 * actual positioning is done here, in charlib(), or in the drawing routines.
 *
 */

    if ( asciichar(c) && printchar(c) )
	switch ( c )  {
	    case '(':
	    case ')':
	    case '\\':
		    if (encoding != 5)
		    	addchar('\\');

	    default:
		    addchar(c);
	}   /* End switch */
    else if ( c > 040 )
	addoctal(c);
    else charlib(c);

    lastx += lastw;

}   /* End of oput */


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


void
starttext(void)


{


/*
 * Called whenever we want to be sure we're ready to start collecting characters
 * for the next call to PostScript procedure t (ie. the one that prints them). If
 * textcount is positive we've already started, so there's nothing to do. The more
 * complicated encoding schemes save text strings in the strings[] array and need
 * detailed information about the strings when they're written to the output file
 * in endtext().
 *
 */


    if ( textcount < 1 )  {
	switch ( encoding )  {
	    case 0:
	    case 1:
	    case 4:
		putc('(', tf);
		charcount = 1;
		break;

	    case 5:
		strptr = strings;
		break;

	    case 2:
	    case 3:
		strptr = strings;
		spacecount = 0;
		line[1].str = strptr;
		line[1].dx = 0;
		line[1].spaces = 0;
		line[1].start = hpos;
		line[1].width = 0;
		charcount = 0;
		break;

	    case MAXENCODING+1:			/* reverse video */
		if ( lastend == -1 )
		    lastend = hpos;
		putc('(', tf);
		charcount = 1;
		break;

	    case MAXENCODING+2:			/* follow a funny baseline */
		putc('(', tf);
		charcount = 1;
		break;
	}   /* End switch */
	textcount = 1;
	lastx = stringstart = hpos;
	laststrstart = INT_MIN;
    }	/* End if */

}   /* End of starttext */


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


void
endtext(void)


{

    char	buf[STRINGSPACE+100];
    int		i;			/* loop index */
    int		n, m;


/*
 *
 * Generates a call to the PostScript procedure that processes all the text we've
 * accumulated - provided textcount is positive.
 *
 */

    if ( textcount > 0 )  {		/* started working on some text */
	switch ( encoding )  {
	    case 0:
		fprintf(tf, ")%d t\n", stringstart);
		break;

	    case 4:
		if (laststrstart != INT_MIN)
		    fprintf(tf, ")%d %d t\n",
			stringstart - laststrstart, stringstart);
		else
		    fprintf(tf, ")%d t\n", stringstart);
		break;

	    case 5:
		putstring(strings, strptr - strings, tf);
		strptr = strings;
		if (laststrstart != INT_MIN)
		    putint(stringstart - laststrstart, tf);
		putint(stringstart, tf);
		putc('t', tf);
		putc('\n', tf);
		break;


	    case 1:
		fprintf(tf, ")%d %d t\n", stringstart, lasty);
		break;

	    case 2:
		*strptr = '\0';
		line[textcount].width = lastx - line[textcount].start;
		if ( spacecount != 0 || textcount != 1 )  {
		    n = 0;
		    for ( i = textcount; i > 0; i-- ) {
			m = snprintf(buf, sizeof buf, "(%s)%d %d",
			    line[i].str, line[i].spaces, line[i].width);
			if (i < textcount && n + m >= 80) {
			    putc('\n', tf);
			    n = 0;
			}
			fputs(buf, tf);
			n += m;
		    }
		    if (lasty != savey)
		        fprintf(tf, " %d %d %d t\n", textcount, stringstart, lasty);
		    else
		        fprintf(tf, " %d %d u\n", textcount, stringstart);
		} else {
		    if (lasty != savey)
			fprintf(tf, "(%s)%d %d w\n", line[1].str, stringstart, lasty);
		    else
			fprintf(tf, "(%s)%d v\n", line[1].str, stringstart);
		}
		savey = lasty;
		break;

	    case 3:
		*strptr = '\0';
		if ( spacecount != 0 || textcount != 1 )  {
		    n = 0;
		    for ( i = textcount; i > 0; i-- ) {
			m = snprintf(buf, sizeof buf, "(%s)%d",
			    line[i].str, line[i].dx);
			if (i < textcount && n + m >= 80) {
			    putc('\n', tf);
			    n = 0;
			}
			fputs(buf, tf);
			n += m;
		    }
		    if (lasty != savey)
		        fprintf(tf, " %d %d %d t\n", textcount, stringstart, lasty);
		    else
		        fprintf(tf, " %d %d u\n", textcount, stringstart);
		} else {
		    if (lasty != savey)
			fprintf(tf, "(%s)%d %d w\n", line[1].str, stringstart, lasty);
		    else
			fprintf(tf, "(%s)%d v\n", line[1].str, stringstart);
		}
		savey = lasty;
		break;

	    case MAXENCODING+1:
		fprintf(tf, ")%d ", stringstart);
		fprintf(tf, "%d %d drawrvbox ", lastend - rvslop, (int)(lastx + .5) + rvslop);
		fprintf(tf, "t\n"/*, stringstart*/);
		lastend = (lastx + .5) + 2 * rvslop;
		break;

	    case MAXENCODING+2:
		fprintf(tf, ")%d %d t\n", stringstart, lasty);
		break;
	}   /* End switch */
	charcount = 0;
    }	/* End if */

    textcount = 0;

}   /* End of endtext */


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


void
endstring(void)


{


    int		dx;


/*
 *
 * Horizontal positions are out of sync. End the last open string, adjust the
 * printer's position, and start a new string. Assumes we've already started
 * accumulating text.
 *
 */


    switch ( encoding )  {
	case 4:
	    if (laststrstart != INT_MIN)
	        charcount += fprintf(tf, ")%d", stringstart - laststrstart);
	    else {
		putc(')', tf);
		charcount++;
	    }
	    laststrstart = stringstart;
	    goto nx;

	case 5:
	    putstring(strings, strptr - strings, tf);
	    strptr = strings;
	    charcount++;
	    if (laststrstart != INT_MIN)
		charcount += putint(stringstart - laststrstart, tf);
	    laststrstart = stringstart;
	    textcount++;
	    lastx = stringstart = hpos;
	    break;

	case 0:
	case 1:
	    charcount += fprintf(tf, ")%d", stringstart);
         nx:
	    if (charcount >= 60) {
		putc('\n', tf);
		charcount = 0;
	    }
	    putc('(', tf);
	    charcount++;
	    textcount++;
	    lastx = stringstart = hpos;
	    break;

	case 2:
	case 3:
	    if (!wordspace) {
		endtext();
		starttext();
		break;
	    }
	    dx = hpos - lastx;
	    if ( spacecount++ == 0 )
		line[textcount].dx = dx;
	    if ( line[textcount].dx != dx )  {
		*strptr++ = '\0';
		line[textcount].width = lastx - line[textcount].start;
		line[++textcount].str = strptr;
		*strptr++ = ' ';
		line[textcount].dx = dx;
		line[textcount].start = lastx;
		line[textcount].width = 0;
		line[textcount].spaces = 1;
		charcount = 1;
	    } else {
		*strptr++ = ' ';
		line[textcount].spaces++;
		charcount++;
	    }	/* End else */
	    lastx += dx;
	    break;

	case MAXENCODING+1:
	    charcount += fprintf(tf, ")%d", stringstart);
	    if (charcount >= 60) {
		putc('\n', tf);
		charcount = 0;
	    }
	    putc('(', tf);
	    charcount++;
	    textcount++;
	    lastx = stringstart = hpos;
	    break;

	case MAXENCODING+2:
	    endtext();
	    starttext();
	    break;

    }	/* End switch */

}   /* End of endstring */


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


void
endline(void)


{


/*
 *
 * The vertical position has changed. Dump any accumulated text, then adjust
 * the printer's vertical position.
 *
 */


    endtext();

    if ( encoding == 0 || encoding == 4 || encoding == MAXENCODING+1 ) {
	fprintf(tf, "%d %d m\n", hpos, vpos);
	savey = vpos;
    } else if (encoding == 5) {
	putint(hpos, tf);
	putint(vpos, tf);
	putc('m', tf);
	putc('\n', tf);
    }

    lastx = stringstart = lastend = hpos;
    lasty = vpos;

}   /* End of endline */


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


void
addchar (
    int c			/* next character in current string */
)


{


/*
 *
 * Does whatever is needed to add character c to the current string.
 *
 */

    static int	lastc;

    subfont = 0;
    if (lastc != '\\')
    	oprep(1);
    lastc = c;
    switch ( encoding )  {
	case 0:
	case 1:
	case 4:
	    putc(c, tf);
	    if (charcount++ >= 72 && c != '\\') {
		putc('\\', tf);
		putc('\n', tf);
		charcount = 0;
	    }
	    break;

	case 5:
	    *strptr++ = c;
	    break;

	case 2:
	case 3:
	    *strptr++ = c;
	    if (charcount++ >= 72 && c != '\\') {
		*strptr++ = '\\';
		*strptr++ = '\n';
		charcount = 0;
	    }
	    break;

	case MAXENCODING+1:
	case MAXENCODING+2:
	    putc(c, tf);
	    if (charcount++ >= 72 && c != '\\') {
		putc('\\', tf);
		putc('\n', tf);
		charcount = 0;
	    }
	    break;
    }	/* End switch */

}   /* End of addchar */


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


void
addoctal (
    int c			/* add it as an octal escape */
)


{

    int	n;


/*
 *
 * Adds c to the current string as an octal escape \ddd.
 *
 *
 * If c is not a byte, try to squeeze it into the control area.
 */


    oprep(0);
    if (c >= 128 && fontname[font].afm && fontname[font].afm->encmap) {
	c = fontname[font].afm->encmap[c - 128];
	subfont = c >> 8;
	c &= 0377;
    } else
	subfont = 0;
    oprep(1);
    switch ( encoding )  {
	case 0:
	case 1:
	case 4:
	    charcount += fprintf(tf, "\\%03o", c);
	    if (charcount >= 72) {
		putc('\\', tf);
		putc('\n', tf);
		charcount = 0;
	    }
	    break;

	case 5:
	    *strptr++ = c;
	    break;

	case 2:
	case 3:
	    snprintf(strptr, sizeof strings - (strptr - strings), "\\%03o", c);
	    n = strlen(strptr);
	    strptr += n;
	    charcount += n;
	    if (charcount >= 72) {
		*strptr++ = '\\';
		*strptr++ = '\n';
		charcount = 0;
	    }
	    break;

	case MAXENCODING+1:
	case MAXENCODING+2:
	    charcount += fprintf(tf, "\\%03o", c);
	    if (charcount >= 72) {
		putc('\\', tf);
		putc('\n', tf);
		charcount = 0;
	    }
	    break;
    }	/* End switch */

}   /* End of addoctal */


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


void
charlib (
    int code			/* either 1 or 2 */
)


{


    char	*name;			/* name of the character */
    char	tname[10];		/* in case it's a single ASCII character */
    const char	*filename;              /* real file name */


/*
 *
 * Called from oput() for characters having codes less than 040. Special files
 * that define PostScript procedures for certain characters can be found in
 * directory *fontdir/devpost/charlib. If there's a file that has the same name as
 * the character we're trying to print it's copied to the output file, otherwise
 * nothing, except some positioning, is done.
 *
 * All character definitions are only made once. Subsequent requests to print the
 * character generate a call to a procedure that begins with the prefix build_ and
 * ends with the character's name. Special characters that are assigned codes
 * other than 1 are assumed to have additional data files that should be copied
 * to the output file immediately after the build_ call. Those data files should
 * end in the suffix .map, and usually will be a hex representation of a bitmap.
 *
 */


    subfont = 0;
    oprep(1);
    endtext();

    if ( lastc < 128 )  {		/* just a simple ASCII character */
	snprintf(tname, sizeof tname, "%.3o", lastc);
	name = tname;
    } else name = &chname[chtab[lastc - 128]];

    /*
     * This is just a kludge of course.  But since only one file exists which
     * needs a name mapping, no more general method had been implemented.
     */

    if (*name == 'L' && name[1] == 'H' && !name[2])
    	filename = "LH_uc";
    else
    	filename = name;

    if ( downloaded[lastc] == 0 )  {
	snprintf(temp, sizeof temp, "%s/dev%s/charlib/%s",
	    fontdir, realdev, filename);
	if ( access(temp, 04) == 0 && doglobal(temp) == TRUE )  {
	    downloaded[lastc] = 1;
	    t_sf(0);
	}   /* End if */
    }	/* End if */

    if ( downloaded[lastc] == 1 )  {
	xymove(hpos, vpos);
	fprintf(tf, "%d build_%s\n", (int) lastw, name);
	if ( code != 1 )  {		/* get the bitmap or whatever */
	    snprintf(temp, sizeof temp, "%s/dev%s/charlib/%s.map",
		    fontdir, realdev, name);
	    if ( access(temp, 04) == 0 && tf == stdout )
		cat(temp, tf);
	}   /* End if */
	fprintf(tf, "%d %d m\n", stringstart = hpos + lastw, vpos);
	savey = vpos;
    }	/* End if */

}   /* End of charlib */


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


int 
doglobal (
    const char *name			/* copy this to the output - globally */
)


{


    int		val = FALSE;		/* returned to the caller */


/*
 *
 * Copies file *name to the output file and brackets it with whatever commands are
 * needed to have it exported to the global environment. TRUE is returned if we
 * successfully add file *name to the output file.
 *
 * Actually, all files included this way are procsets, so they go into
 * the resource section of the PostScript output and not in the global
 * setup file.
 *
 */


    if ( tf == stdout )  {
	val = cat(name, rf);
	reset();
    }	/* End if */

    return(val);

}   /* End of doglobal */


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

void
documentfont(const char *name)
{
    static FILE	*fp_out;		/* PostScript name added to this file */
    static int	pos;
    static struct fn {
	struct fn	*next;
	const char	*name;
    } *fn;
    struct fn *ft;
    int	n;

    if ( temp_file == NULL )		/* generate a temp file name */
	if ( (temp_file = tempname("dpost")) == NULL )
	    return;

    if ( fp_out == NULL && (fp_out = fopen(temp_file, "w")) == NULL )
	return;
    for (ft = fn; ft; ft = ft->next)
	if (strcmp(name, ft->name) == 0)
	    return;
    ft = calloc(1, sizeof *ft);
    ft->name = strdup(name);
    ft->next = fn;
    fn = ft;
    if ( docfonts++ == 0 )
	pos += fprintf(fp_out, "%s", DOCUMENTFONTS);
    else {
	n = strlen(name);
	if (pos + n >= 80) {
	    fprintf(fp_out, "\n%s", CONTINUECOMMENT);
	    pos = 0;
	}
    }
    pos += fprintf(fp_out, " %s", name);
    fflush(fp_out);
    t_dosupply(name);
}


void
documentfonts(void)


{


    FILE	*fp_in = NULL;		/* PostScript font name read from here */


/*
 *
 * Whenever a new font is used we try to record the appropriate PostScript font
 * name in *temp_file for the DOCUMENTFONTS comment that's put out in done().
 * By default PostScript font names are found in /usr/lib/font/devpost. Fonts
 * that have a .name file are recorded in *temp_file. The first string in that
 * file is expected to be that font's (long) PostScript name.
 *
 */


    snprintf(temp, sizeof temp, "%s/dev%s/%s.name",
		    fontdir, realdev, fontname[font].name);

    if (fontname[font].afm == NULL && (fp_in = fopen(temp, "r")) != NULL )  {
	if ( sget(temp, sizeof temp, fp_in) == 1 )  {
	    documentfont(temp);
	}   /* End if */
	if (fp_in != NULL)
	    fclose(fp_in);
    } else if (fontname[font].afm != NULL){
	documentfont(fontname[font].afm->fontname);
    }	/* End if */

}   /* End of documentfonts */


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


void
redirect (
    int pg			/* next page we're printing */
)


{


    static FILE	*fp_null = NULL;	/* if output is turned off */


/*
 *
 * If we're not supposed to print page pg, tf will be directed to /dev/null,
 * otherwise output goes to stdout.
 *
 */


    if ( pg >= 0 && in_olist(pg) == ON )
	tf = stdout;
    else if ( (tf = fp_null) == NULL )
	tf = fp_null = fopen("/dev/null", "w");

}   /* End of redirect */


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

static char *
mbs2pdf(char *mp)
{
    char	*ustr, *tp;
    int	c, i, sz;
#ifdef	EUC
    int	n = 0, w;
    wchar_t	wc;
#endif

    for (tp = mp; *tp && (*tp&~0177) == 0 && *tp&~037; tp++);
    if (*tp == 0) {
	ustr = malloc(sz = 16);
	*ustr = '(';
	c = i = 1;
	while (*mp) {
	    switch (*mp) {
	        case '(':
	        case ')':
	        case '\\':
			ustr[i++] = '\\';
			c++;
			/*FALLTHRU*/
	        default:
			ustr[i++] = *mp++;
			c++;
	    }
	    if (i + 4 >= sz)
		ustr = realloc(ustr, sz += 16);
	    if (c >= 60) {
		ustr[i++] = '\\';
		ustr[i++] = '\n';
		c = 0;
	    }
	}
	ustr[i++] = ')';
	ustr[i++] = 0;
	return ustr;
    }
#ifdef	EUC
    ustr = malloc(sz = 16);
    c = i = snprintf(ustr, sz, "<FEFF");
    while (mp += n, *mp) {
	if ((n = mbtowc(&wc, mp, mb_cur_max)) <= 0) {
	    error(NON_FATAL,
		"illegal byte sequence %d in PDFMark operand",
		*mp&0377);
	    n = 1;
	    continue;
	}
	if (wc < 0 || wc > 0xFFFF) {
	    error(NON_FATAL, "only BMP values allowed for PDFMark");
	    continue;
	}
	if (i + 8 >= sz)
	    ustr = realloc(ustr, sz += 16);
	w = snprintf(&ustr[i], sz - i * sizeof(*ustr), "%04X", (int)wc);
	i += w;
	c += w;
	if (c >= 60) {
	    ustr[i++] = '\n';
	    c = 0;
	}
    }
    ustr[i++] = '>';
    ustr[i] = 0;
    return ustr;
#else	/* !EUC */
    error(NON_FATAL,
	"this instance of dpost only supports ASCII with PDFMark");
    return NULL;
#endif	/* !EUC */
}

static void
t_pdfmark(char *buf)
{
    char	*bp, *tp;
    int	n;

    while (spacechar(*buf&0377))
	buf++;
    for (bp = buf; *bp && !spacechar(*bp&0377); bp++);
    *bp++ = '\0';
    while (spacechar(*bp&0377))
	bp++;
    for (tp = bp; *tp; tp++)
	if (*tp == '\n') {
	    *tp = '\0';
	    break;
	}
    if (strcmp(buf, "Author") == 0)
	Author = mbs2pdf(bp);
    else if (strcmp(buf, "Title") == 0)
	Title = mbs2pdf(bp);
    else if (strcmp(buf, "Subject") == 0)
	Subject = mbs2pdf(bp);
    else if (strcmp(buf, "Keywords") == 0)
	Keywords = mbs2pdf(bp);
    else if (strcmp(buf, "Bookmark") == 0 ||
	    strcmp(buf, "BookmarkClosed") == 0) {
	n = strtol(bp, &bp, 10);
	while (spacechar(*bp&0377))
	    bp++;
	if (n < 0 || n > MAXBOOKMARKLEVEL) {
	    error(NON_FATAL, "invalid PDFMark Bookmark level %d,"
	        "maximum is %d\n",
		n, MAXBOOKMARKLEVEL);
	    return;
	}
	Bookmarks = realloc(Bookmarks, ++nBookmarks*sizeof *Bookmarks);
	Bookmarks[nBookmarks-1].level = n;
	Bookmarks[nBookmarks-1].Title = mbs2pdf(bp);
	Bookmarks[nBookmarks-1].title = strdup(bp);
	Bookmarks[nBookmarks-1].Count = 0;
	Bookmarks[nBookmarks-1].closed =
	    strcmp(buf, "BookmarkClosed") == 0;
	endtext();
	fprintf(tf, "[ /Dest /Bookmark$%ld\n"
		    "  /View [/XYZ -4 %g 0]\n"
		    "/DEST pdfmark\n",
	    (long)nBookmarks - 1,
	    pagelength - (lasty >= 0 ? vpos * 72.0 / res : -4));
    } else
	error(NON_FATAL, "unknown PDFMark attribute %s", buf);
}

static void
orderbookmarks(void)
{

    int	counts[MAXBOOKMARKLEVEL+1];
    int	refs[MAXBOOKMARKLEVEL+1];
    size_t	i, j, k, t;
    int	lvl = 0;

/*
 * Generate the Count parameter from the given levels.
 */

    memset(&counts, 0, sizeof counts);
    for (i = 0; i <= MAXBOOKMARKLEVEL; i++)
	refs[i] = -1;
    for (i = 0; i <= nBookmarks; i++) {
	k = i < nBookmarks ? Bookmarks[i].level : 0;
	if (i == nBookmarks || k <= lvl) {
	    for (j = k+1; j <= MAXBOOKMARKLEVEL; j++) {
		t = j - 1;
		if (refs[t] >= 0) {
		    Bookmarks[refs[t]].Count += counts[j];
		    refs[t] = -1;
		}
		counts[j] = 0;
	    }
	}
	if (k > 0 && refs[k-1] < 0) {
	    while (k > 0 && refs[k-1] < 0)
		k--;
	    error(NON_FATAL, "PDFMark Bookmark \"%s\" at level %d "
	        "has no direct parent, "
		"using level %d\n",
		Bookmarks[i].title, Bookmarks[i].level, k);
	}
	counts[k]++;
	refs[k] = i;
	lvl = k;
    }
}

static void
t_locale(char *lp)
{
    static char	*savlp;

    if (savlp && strcmp(lp, savlp) == 0)
	return;
    free(savlp);
    savlp = malloc(strlen(lp) + 1);
    sscanf(lp, "%s", savlp);
    setlocale(LC_CTYPE, savlp);
    mb_cur_max = MB_CUR_MAX;
}

static void
pref(const char *lp, FILE *fp)
{
    int	c;

    while ((c = *lp++ & 0377) != 0 && c != '\n') {
	if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') ||
		(c >= 'A' && c <= 'Z'))
	    putc(c, fp);
	else
	    fprintf(fp, "$%2x", c);
    }
}

static void
pref_uri(const char *lp, FILE *fp)
{
    int	c;

    /* Don't do any escaping here to avoid double-escaping. */
    while ((c = *lp++ & 0377) != 0 && c != '\n') {
	    putc(c, fp);
    }
}

static void
t_anchor(char *lp)
{
    int	v;

    v = strtol(lp, &lp, 10);
    if ((lp = strchr(lp, ' ')) != NULL) {
	lp++;
	endtext();
	fprintf(tf, "[ /Dest /Anchor$");
	pref(lp, tf);
	fprintf(tf, "\n"
		    "  /View [/XYZ -4 %g 0]\n"
		    "/DEST pdfmark\n",
	    pagelength - (v >= 0 ? v * 72.0 / res : -4));
    }
}

static char linkcolor[60] = "0 0 1";
static char linkborder[60] = "0 0 1";

static char *
t_linkborderstyle(char *arg) {
	char c, *s;
	s = arg;
	for (s = arg; (c = *s) && c != '\n'; s++);
	*s = 0;
	return strdup(arg);
}

static void
t_link(char *lp)
{
    int	llx, lly, urx, ury;

    llx = strtol(lp, &lp, 10);
    if (*lp) {
	while (*lp == ',')
	    lp++;
	lly = strtol(lp, &lp, 10);
	if (*lp) {
	    while (*lp == ',')
		lp++;
	    urx = strtol(lp, &lp, 10);
	    if (*lp) {
		while (*lp == ',')
		    lp++;
		ury = strtol(lp, &lp, 10);
		if ((lp = strchr(lp, ' ')) != NULL) {
		    lp++;
		    endtext();
		    fprintf(tf, "[ /Dest /Anchor$");
		    pref(lp, tf);
		    fprintf(tf, "\n"
			"/Rect [%d %d %d %d]\n"
			"/Color [%s]\n",
			llx, -lly, urx, -ury, linkcolor);
		if (linkborderstyle)
			fprintf(tf, "/BS << %s >>\n", linkborderstyle);
		else
			fprintf(tf, "/Border [%s]\n", linkborder);
		fprintf(tf,
		    "/Subtype /Link\n"
		    "/ANN pdfmark\n");
		}
	    }
	}
    }
}

static void
t_linkcolor(char *lp)
{
    float	r, g, b;

    r = strtod(lp, &lp);
    g = strtod(lp, &lp);
    b = strtod(lp, &lp);
    snprintf(linkcolor, sizeof linkcolor, "%g %g %g", r, g, b);
}

static void
t_linkborder(char *lp)
{
    float	bx, by, c;

    bx = strtod(lp, &lp);
    by = strtod(lp, &lp);
    c = strtod(lp, &lp);
    snprintf(linkborder, sizeof linkborder, "%g %g %g", bx, by, c);
    free(linkborderstyle);
    linkborderstyle = NULL;
}

static char ulinkcolor[60] = "0 0 1";
static char ulinkborder[60] = "0 0 1";

static void
t_ulink(char *lp)
{
    int	llx, lly, urx, ury;

    llx = strtol(lp, &lp, 10);
    if (*lp) {
	while (*lp == ',')
	    lp++;
	lly = strtol(lp, &lp, 10);
	if (*lp) {
	    while (*lp == ',')
		lp++;
	    urx = strtol(lp, &lp, 10);
	    if (*lp) {
		while (*lp == ',')
		    lp++;
		ury = strtol(lp, &lp, 10);
		if ((lp = strchr(lp, ' ')) != NULL) {
		    lp++;
		    endtext();
		    fprintf(tf, "[ /Rect [%d %d %d %d]\n"
			    "/Color [%s]\n",
			    llx, -lly, urx, -ury, ulinkcolor);
		if (ulinkborderstyle)
			fprintf(tf, "/BS << %s >>\n", ulinkborderstyle);
		else
			fprintf(tf, "/Border [%s]\n", ulinkborder);
		    fprintf(tf, "/Action << /Subtype /URI\n"
			    "/URI (");
		    pref_uri(lp, tf);
		    fprintf(tf, ") >>\n"
			    "/Subtype /Link\n"
			    "/ANN pdfmark\n");
		}
	    }
	}
    }
}

static void
t_ulinkcolor(char *lp)
{
    float	r, g, b;

    r = strtod(lp, &lp);
    g = strtod(lp, &lp);
    b = strtod(lp, &lp);
    snprintf(ulinkcolor, sizeof ulinkcolor, "%g %g %g", r, g, b);
}

static void
t_ulinkborder(char *lp)
{
    float	bx, by, c;

    bx = strtod(lp, &lp);
    by = strtod(lp, &lp);
    c = strtod(lp, &lp);
    snprintf(ulinkborder, sizeof ulinkborder, "%g %g %g", bx, by, c);
    free(ulinkborderstyle);
    ulinkborderstyle = NULL;
}
