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