V10/cmd/post.src/dpost/dpost.c
/*
*
* 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 changed, credit has to be given to Richard
* Flood for his early work on the PostScript driver.
*
* The big change is in the font table routines. The old binary format and
* makedev are gone. dpost and troff now read ASCII tables, and both skip
* unrecognized entries in the ASCII tables. That means problems, like where
* to put the real name of the PostScript font, have disappeared. The added
* flexibility means some overhead translating the ASCII tables, but the
* overhead isn't too bad.
*
* dpost can also now calculate a reasonably tight BoundingBox, which helps
* picture inclusion. The calculations, by default, are disabled. Couldn't
* justify the overhead for a comment, particularly one that's only needed
* occasionally. Use the -B option to get the comment.
*
* Output produced by dpost is still nonconforming. Definitions made in pages
* and exported to the job's global environment are the primary problem. It's
* an efficient approach, but means pages are not independent. Violations are
* bracketed by %%BeginGlobal and %%EndGlobal comments and can be pulled into
* the prologue by utility programs (like postreverse) that recognize the new
* comments.
*
* 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. Emulation is relatively expensive, and
* can produce output files that are more than twice the size of the input
* files.
*
* 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 3 (or higher)
* is not normally recommended. It's fast and produces very compact output,
* but rounding errors in troff's width tables can accumulate and lead to a
* ragged right margin. encoding can be changed on the command line using the
* -e option.
*
* PostScript fonts don't support all of troff's characters. Some are built
* by special PostScript procedures in directory *fontdir/devpost/charlib.
* The charlib approach is not meant to replace user defined fonts. It was
* a quick implementation designed to handle characters that aren't used
* often - charlib should not be overused! The charlib lookup is triggered
* when a character in a font table is assigned a code less than 32.
*
* Most defaults are set in the prologue, but can be changed by options. The
* -P option passes arbitrary PostScript into the setup section of the output
* file. It can be used to set (or change) values that can't be accessed by
* other options. For example,
*
* dpost -P'/useclippath false def' file > file.ps
*
* defines useclippath to be false. Everything passed through using the -P
* (-C to copy a file) options become part of the job's global environment.
* Definitions override defaults in the prologue.
*
* dpost expects to find the following procedures in the prologue:
*
* setup
*
* mark ... setup -
*
* Initialization procedure mainly responsible for setting up an
* appropriate coordinate system.
*
* pagesetup
*
* page pagesetup -
*
* Called at the start of each page, immediately after the page
* level save. Argument is the current page number.
*
* setdecoding
*
* num setdecoding -
*
* Select the decoding procedure used to print text strings encoded
* by dpost. num is whatever has been assigned to encoding.
*
* f
*
* size font f -
*
* Set the font and size used for character imaging. The font name
* argument is (normally) the name troff used. Mapping to the real
* PostScript font name is made using the fontname field in the
* ASCII width tables.
*
* m
*
* x y m -
*
* Move to point (x, y). Not used for positioning words in text
* strings.
*
* t
*
* mark text t mark
*
* Everything on the stack (up to the mark) is treated as a line
* of text to be decoded and printed. What's on the stack depends
* on encoding.
*
* w
*
* string x y w -
*
* Print a single word starting at position (x, y). Only used in
* the more complicated encoding schemes, like the ones based on
* widthshow.
*
* done
*
* Make sure the last page prints. Always called, but only needed
* when printing more than one page on each sheet of paper.
*
* 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" /* structuring comments */
#include "gen.h" /* general purpose definitions */
#include "path.h" /* prologue and a few other files */
#include "ext.h" /* external variable declarations */
#include "font.h" /* font table definitions */
#include "dpost.h" /* a few definitions just used here */
#include "motion.h" /* positioning macros */
char *prologue = DPOST; /* the PostScript prologue */
char *colorfile = COLOR; /* color support */
char *drawfile = DRAW; /* drawing routines */
char *formfile = FORMFILE; /* multiple pages on each sheet */
char *baselinefile = BASELINE; /* for text along curved baseline */
char *fontdir = FONTDIR; /* font table directories */
char *hostfontdir = NULL; /* host resident font directory */
char *realdev = DEVNAME; /* use these width tables */
char devname[20] = ""; /* job formatted for this device */
Fontmap fontmap[] = FONTMAP; /* font translation table - emulation */
char *useencoding = NULL; /* text font encoding - from -E option */
int copies = 1; /* copies of each sheet */
int printed = 0; /* pages processed and printed */
int formsperpage = 1; /* pages on each sheet of paper */
int picflag = ON; /* enable/disable picture inclusion */
int encoding = DFLTENCODING; /* how text is translated to PostScript */
int realencoding = DFLTENCODING; /* where we started */
int maxencoding = MAXENCODING; /* max that users can select */
int landscape = FALSE; /* for BoundingBox calculations only */
double magnification = 1.0;
double xoffset = 0.0;
double yoffset = 0.0;
int smnt; /* special fonts start here */
int devres; /* device resolution */
int unitwidth; /* and unitwidth - from DESC file */
char downloaded[MAXCH+32+ALPHABET]; /* status of charlib characters */
int nfonts = 0; /* number of font positions */
int size = 10; /* current point size */
int font = 0; /* and font position */
int hpos = 0; /* where troff wants to be */
int vpos = 0;
float lastw = 0; /* width of the last input character */
int lastc = 0; /* its name (or index) - for charlib() */
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/devres */
int lastsize = -1; /* for tracking printer's current size */
int lastfont = -1; /* current font */
float lastx = -1; /* and current position */
int lasty = -1;
int lastend; /* where last character on this line was */
int seenpage = FALSE; /* expect fonts are now all mounted */
int gotspecial = FALSE; /* append special fonts - emulation */
float pointslop = SLOP; /* horizontal error in points */
int slop; /* and machine units */
int rvslop; /* to extend box in reverse video mode */
int textcount = 0; /* strings accumulated so far */
int stringstart = 0; /* where the next one starts */
int spacecount = 0; /* spaces in current string */
Line line[MAXSTACK+3]; /* data about words accumulated so far */
char strings[STRINGSPACE]; /* strings temporarily saved here */
char *strptr; /* next free slot in strings[] */
FILE *tf = NULL; /* most output goes here */
FILE *fp_acct = NULL; /* accounting file */
char *optnames = "a:c:e:m:n:o:p:tw:x:y:A:BC:E:J:F:H:L:OP:R:S:T:DI";
extern int gotcolor; /* read *colorfile when TRUE */
extern Font fonts[]; /* data about every font we see */
extern Font *mount[]; /* troff mounts fonts here */
/*****************************************************************************/
main(agc, agv)
int agc;
char *agv[];
{
/*
*
* Translates output from troff into PostScript. Input files must be formatted
* for the same device. Each input file begins on a new page.
*
*/
argc = agc; /* global so everyone can use them */
argv = agv;
prog_name = argv[0]; /* for error messages */
init_signals(); /* interrupt handling */
header(); /* structuring comments */
options(); /* command line options */
arguments(); /* translate the input files */
done(); /* add trailing comments etc. */
account(); /* job accounting data */
exit(x_stat);
} /* End of main */
/*****************************************************************************/
init_signals()
{
/*
*
* 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 */
/*****************************************************************************/
header()
{
int ch;
int old_optind = optind;
/*
*
* Scan the option list for things needed now (e.g. prologue file), but could
* be changed from defaults. An attempt to follow to Adobe's 2.0 structuring
* conventions.
*
*/
while ( (ch = getopt(argc, argv, optnames)) != EOF )
if ( ch == 'L' )
setpaths(optarg);
else if ( ch == 'B' )
dobbox = TRUE;
else if ( ch == '?' )
error(FATAL, "");
optind = old_optind; /* restored for options() */
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);
if ( dobbox == TRUE )
fprintf(stdout, "%s %s\n", BOUNDINGBOX, ATEND);
fprintf(stdout, "%s", ENDCOMMENTS);
if ( cat(prologue) == FALSE )
error(FATAL, "can't read %s", prologue);
if ( DOROUND )
cat(ROUNDPAGE);
fprintf(stdout, "%s", ENDPROLOG);
fprintf(stdout, "%s", BEGINSETUP);
fprintf(stdout, "mark\n");
} /* End of header */
/*****************************************************************************/
options()
{
int ch;
extern char *optarg;
extern int optind;
/*
*
* Command line options - there are too many!
*
*/
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': /* select the encoding scheme */
if ( (encoding = atoi(optarg)) < 0 || encoding > MAXENCODING )
encoding = DFLTENCODING;
realencoding = encoding;
break;
case 'm': /* magnification */
magnification = atof(optarg);
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 */
landscape = (*optarg == 'l') ? TRUE : FALSE;
if ( landscape == TRUE )
fprintf(stdout, "/landscape true def\n");
else fprintf(stdout, "/landscape false def\n");
break;
case 't': /* compatibility */
break;
case 'w': /* line width - for drawing */
fprintf(stdout, "/linewidth %s def\n", optarg);
break;
case 'x': /* shift horizontally */
xoffset = atof(optarg);
fprintf(stdout, "/xoffset %s def\n", optarg);
break;
case 'y': /* shift vertically */
yoffset = atof(optarg);
fprintf(stdout, "/yoffset %s def\n", optarg);
break;
case 'A': /* job accounting */
case 'J':
if ( (fp_acct = fopen(optarg, "a")) == NULL )
error(FATAL, "can't open accounting file %s", optarg);
break;
case 'B': /* enable BoundingBox calculations */
dobbox = TRUE;
fprintf(stdout, "/rotation 1 def\n");
fprintf(stdout, "/gotpagebbox true def\n");
break;
case 'C': /* copy file to output */
if ( cat(optarg) == FALSE )
error(FATAL, "can't read %s", optarg);
break;
case 'E': /* text font encoding - override DESC */
useencoding = optarg;
break;
case 'F': /* font table directory */
fontdir = optarg;
break;
case 'H': /* host resident font directory */
hostfontdir = optarg;
break;
case 'L': /* prologue file */
setpaths(optarg); /* already been done in header() */
break;
case 'O': /* disable picture inclusion */
picflag = OFF;
break;
case 'P': /* copy string to output */
fprintf(stdout, "%s\n", optarg);
break;
case 'R': /* 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;
argv += optind;
} /* End of options */
/*****************************************************************************/
setpaths(name)
char *name;
{
char *path;
/*
*
* Extends the -L option to permit modification of more than just the prologue
* file pathname. Syntax is -Lpath or -Lname:path. For debugging and development
* only!
*
*/
for ( path = name; *path; path++ )
if ( *path == ':' || *path == ' ' ) {
while ( *path == ':' || *path == ' ' ) path++;
break;
} /* End if */
if ( *path == '\0' ) /* didn't find "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 */
/*****************************************************************************/
setup()
{
double t;
/*
*
* Job and BoundingBox initialization. Called once from t_init() - must know
* the resolution before generating the PostScript call to setup. dpost only
* includes an encoding file if it's set in the DESC file or requested with
* the -E option.
*
*/
writerequest(0, stdout); /* global requests e.g. manual feed */
fprintf(stdout, "/resolution %d def\n", res);
if ( useencoding != NULL || fontencoding != NULL )
setencoding((useencoding != NULL) ? useencoding : fontencoding);
fprintf(stdout, "setup\n");
fprintf(stdout, "%d setdecoding\n", realencoding);
if ( formsperpage > 1 ) { /* 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);
if ( dobbox == TRUE ) { /* ctm[] - must agree with prologue */
translate(pagewidth/2.0, pageheight/2.0);
if ( landscape == TRUE ) {
rotate(90.0);
t = pagewidth;
pagewidth = pageheight;
pageheight = t;
} /* End if */
translate(-pagewidth/2.0, pageheight/2.0);
translate(72.0 * xoffset, -72.0 * yoffset);
scale(magnification, magnification);
scale(72.0/devres, 72.0/devres);
} /* End if */
} /* End of setup */
/*****************************************************************************/
arguments()
{
FILE *fp;
/*
*
* Everything else is an input file. No arguments or '-' means 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 */
/*****************************************************************************/
done()
{
int i;
int n;
/*
*
* Force out the last page and add trailing comments.
*
*/
fprintf(stdout, "%s", TRAILER);
fprintf(stdout, "done\n");
fprintf(stdout, "%s %d\n", PAGES, printed);
for ( i = 0, n = 0; i < MAXFONTS+1; i++ )
if ( (fonts[i].flags & USED) && fonts[i].fontname != NULL ) {
if ( n++ == 0 )
fprintf(stdout, "%s", DOCUMENTFONTS);
else if ( (n - 1) % 8 == 0 )
fprintf(stdout, "\n%s", CONTINUECOMMENT);
fprintf(stdout, " %s", fonts[i].fontname);
} /* End if */
if ( n > 0 )
putc('\n', stdout);
if ( dobbox == TRUE )
writebbox(stdout, BOUNDINGBOX, 10);
} /* End of done */
/*****************************************************************************/
account()
{
/*
*
* Accounting record to fp_acct - provided it's not NULL.
*
*/
if ( fp_acct != NULL )
fprintf(fp_acct, " print %d\n copies %d\n", printed, copies);
} /* End of account */
/*****************************************************************************/
conv(fp)
register FILE *fp;
{
register int c;
int m, n, n1, m1;
char str[50];
/*
*
* Read troff output from file fp and translate it into PostScript. The final
* t_page() call means input files start on a new page.
*
*/
redirect(-1); /* do output only after a page command */
lineno = 1;
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);
put1(chindex(str));
break;
case 'N': /* character at position n */
fscanf(fp, "%d", &m);
flushtext();
oput(m);
break;
case 'D': /* drawing functions */
flushtext();
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", &size); /* ignore fractional sizes */
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 ) ;
hgoto(0);
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 */
flushtext();
} /* End of conv */
/*****************************************************************************/
devcntrl(fp)
FILE *fp;
{
char str[50], buf[256], str1[100];
int c, n;
/*
*
* Interpret device control commands, ignoring any we don't recognize. The
* "x X ..." commands are a device dependent collection generated by troff's
* \X'...' request.
*
*/
fscanf(fp, "%s", str);
switch ( str[0] ) {
case 'f': /* load font in a position */
fscanf(fp, "%d %s", &n, str);
fgets(buf, sizeof buf, fp); /* in case there's a filename */
ungetc('\n', fp); /* fgets() goes too far */
str1[0] = '\0'; /* in case there's nothing to come in */
sscanf(buf, "%s", str1);
loadfont(n, str, str1);
break;
case 'i': /* initialize */
t_init();
break;
case 'p': /* pause */
break;
case 'r': /* resolution assumed when prepared */
fscanf(fp, "%d", &res);
break;
case 's': /* stop */
case 't': /* trailer */
flushtext();
break;
case 'H': /* char height */
fscanf(fp, "%d", &n);
t_charht(n);
break;
case 'S': /* slant */
fscanf(fp, "%d", &n);
t_slant(n);
break;
case 'T': /* device name */
fscanf(fp, "%s", devname);
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, "INFO") == 0 ) {
flushtext();
fprintf(tf, "%%INFO%s", buf);
} else if ( strcmp(str, "PS") == 0 || strcmp(str, "PostScript") == 0 ) {
flushtext();
fprintf(tf, "%s", buf);
} else if ( strcmp(str, "ExportPS") == 0 ) { /* dangerous!! */
if ( tf == stdout ) {
restore();
fprintf(tf, "%s", buf);
save();
} /* End if */
} /* End else */
break;
} /* End switch */
while ( (c = getc(fp)) != '\n' && c != EOF ) ;
} /* End of devcntrl */
/*****************************************************************************/
loadfont(m, f, name)
int m;
char *f;
char *name;
{
char path[150];
/*
*
* Load position m with font f. Font file pathname is *fontdir/dev*realdev/*f
* or name, if name isn't empty. Use mapfont() to replace the missing font
* if we're emulating another device, name is empty, and the first mount
* fails.
*
*/
if ( name[0] == '\0' )
sprintf(path, "%s/dev%s/%s", fontdir, realdev, f);
else sprintf(path, "%s", name);
if ( mountfont(path, m) == -1 ) {
if ( name[0] == '\0' ) {
sprintf(path, "%s/dev%s/%s", fontdir, realdev, mapfont(f));
if ( mountfont(path, m) == -1 ) {
sprintf(path, "%s/dev%s/%s", fontdir, realdev, f);
error(FATAL, "can't load %s at %d", path, m);
} /* End if */
} else error(FATAL, "can't load %s at %d", path, m);
} /* End if */
if ( smnt == 0 && mount[m]->specfont )
smnt = m;
if ( m == lastfont ) /* force a call to t_sf() */
lastfont = -1;
if ( m > nfonts ) { /* got more positions */
nfonts = m;
gotspecial = FALSE;
} /* End if */
} /* End of loadfont */
/*****************************************************************************/
char *mapfont(name)
char *name;
{
int i;
/*
*
* Map a missing font name into one that should be available. Only used when
* we're emulating another device and the first mount fails. Consider deleting
* this routine.
*
*/
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 */
/*****************************************************************************/
loadspecial()
{
/*
*
* Fix - later.
*
*/
gotspecial = TRUE;
} /* End of loadspecial */
/*****************************************************************************/
t_init()
{
char path[150];
static int initialized = FALSE;
/*
*
* Finish initialization - just read an "x init" command. Assumes we already
* know the input file resolution.
*
*/
flushtext(); /* moved - for cat'ed troff files */
if ( initialized == FALSE ) {
if ( strcmp(devname, realdev) ) {
sprintf(path, "%s/dev%s/DESC", fontdir, devname);
if ( checkdesc(path) )
realdev = devname;
} /* End if */
sprintf(path, "%s/dev%s/DESC", fontdir, realdev);
if ( getdesc(path) == -1 )
error(FATAL, "can't open %s", path);
nfonts = 0;
gotspecial = FALSE;
widthfac = (float) res /devres;
slop = pointslop * res / POINTS + .5;
rvslop = res * .025;
setup();
initialized = TRUE;
} /* End if */
hpos = vpos = 0;
size = 10;
reset();
} /* End of t_init */
/*****************************************************************************/
t_page(pg)
int pg;
{
static int lastpg = 0;
/*
*
* Finish the previous page and get ready for the next one. End page output
* goes to /dev/null at the start of each input file. Start page output goes
* to /dev/null at the end of each input file.
*
* Consider doing showpage after page level restore (as Adobe recommends). If
* the order is changed use restore() and save(). forms.ps will likely also
* need fixing.
*
*/
if ( tf == stdout )
printed++;
flushtext(); /* just in case */
fprintf(tf, "cleartomark\n");
fprintf(tf, "showpage\n");
fprintf(tf, "saveobj restore\n");
if ( dobbox == TRUE )
writebbox(tf, PAGEBOUNDINGBOX, 10);
fprintf(tf, "%s %d %d\n", ENDPAGE, lastpg, printed);
redirect(pg);
fprintf(tf, "%s %d %d\n", PAGE, pg, printed+1);
if ( dobbox == TRUE )
fprintf(tf, "%s %s\n", PAGEBOUNDINGBOX, ATEND);
fprintf(tf, "/saveobj save def\n");
fprintf(tf, "mark\n");
writerequest(printed+1, tf);
fprintf(tf, "%d pagesetup\n", printed+1);
if ( encoding != realencoding )
fprintf(tf, "%d setdecoding\n", encoding);
if ( gotcolor == TRUE )
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 */
/*****************************************************************************/
t_font(s)
char *s;
{
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 */
/*****************************************************************************/
setfont(m)
int m;
{
/*
*
* Use the font mounted at position m. Bounds checks are probably unnecessary.
* Changing the font and size used by the printer is handled in t_sf().
*
*/
if ( m < 0 || m > MAXFONTS )
error(FATAL, "illegal font %d", m);
font = m;
} /* End of setfont */
/*****************************************************************************/
t_sf()
{
Font *fpos;
char temp[150];
/*
*
* Force a new font or size. Generates name definitions for fonts that haven't
* been named, grabs host resident font files and keeps track of the fonts used
* by this job. When necessary also adjusts the font's height and slant. Should
* only be called immediately before printing a character.
*
*/
if ( tf == stdout && mounted(font) ) {
flushtext();
fpos = mount[font];
if ( (fpos->flags & USED) == 0 ) {
if ( (fpos->flags & NAMED) == 0 && fpos->fontname != NULL ) {
sprintf(temp, "/%s /%s def\n", fpos->name, fpos->fontname);
exportstring(temp);
fpos->flags |= NAMED; /* unnecessary */
} /* End if */
if ( hostfontdir != NULL ) {
sprintf(temp, "%s/%s", hostfontdir, fpos->name);
exportfile(temp);
} /* End if */
} /* End if */
fprintf(tf, "%d %s f\n", size, fpos->name);
if ( fontheight != 0 || fontslant != 0 )
fprintf(tf, "%d %d changefont\n", fontslant, (fontheight != 0) ? fontheight : size);
lastfont = font;
lastsize = size;
fpos->flags |= USED;
} /* End if */
} /* End of t_sf */
/*****************************************************************************/
t_charht(n)
int n;
{
/*
*
* Set character height to n points. Disabled if n is 0 or the current size.
*
*/
fontheight = (n == size) ? 0 : n;
lastfont = -1;
} /* End of t_charht */
/*****************************************************************************/
t_slant(n)
int n;
{
/*
*
* Set slant to n degrees. Disable slanting if n is 0.
*
*/
fontslant = n;
lastfont = -1;
} /* End of t_slant */
/*****************************************************************************/
xymove(x, y)
int x, y;
{
/*
*
* Make the the printer and post-processor agree about the current position.
*
*/
flushtext();
hgoto(x);
vgoto(y);
fprintf(tf, "%d %d m\n", hpos, vpos);
lastx = hpos;
lasty = vpos;
} /* End of xymove */
/*****************************************************************************/
put1(c)
register int c;
{
register int i;
register int j;
register int k;
int code;
int ofont;
/*
*
* Print character c. ASCII if c < ALPHABET, otherwise it's special. Look for
* c in the current font, then in others starting at the first special font.
* Save c in lastc so it's available when oput() runs. Restore original font
* before leaving.
*
*/
lastc = c; /* charlib() needs name not code */
if ( (c -= 32) <= 0 )
return;
k = ofont = font;
if ( (i = onfont(lastc, k)) == -1 && smnt > 0 )
for ( k = smnt, j = 0; j < nfonts; j++, k = k % nfonts + 1 ) {
if ( (i = onfont(lastc, k)) != -1 ) {
setfont(k);
break;
} /* End if */
} /* End for */
if ( i != -1 && (code = mount[k]->wp[i].code) != 0 ) {
lastw = widthfac * (((int)mount[k]->wp[i].wid * size + unitwidth/2) / unitwidth);
oput(code);
} /* End if */
if ( font != ofont )
setfont(ofont);
} /* End of put1 */
/*****************************************************************************/
oput(c)
int c;
{
double llx, lly, urx, ury; /* boundingbox corners */
/*
*
* 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? */
flushtext();
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);
if ( dobbox == TRUE ) {
llx = lastx;
lly = -(vpos + 0.5 * (devres * size / 72.0));
urx = lastx + lastw;
ury = -(vpos - (devres * size / 72.0));
cover(llx, lly);
cover(urx, ury);
} /* End if */
lastx += lastw;
} /* End of oput */
/*****************************************************************************/
starttext()
{
/*
* 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 flushtext().
*
*/
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 */
/*****************************************************************************/
flushtext()
{
int i;
/*
*
* Generates a call to the PostScript procedure that processes all the text we've
* accumulated - provided textcount is positive.
*
*/
if ( textcount > 0 ) {
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 flushtext */
/*****************************************************************************/
endstring()
{
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:
flushtext();
starttext();
break;
} /* End switch */
} /* End of endstring */
/*****************************************************************************/
endline()
{
/*
*
* The vertical position has changed. Dump any accumulated text, then adjust
* the printer's vertical position.
*
*/
flushtext();
if ( encoding == 0 || encoding == MAXENCODING+1 )
fprintf(tf, "%d %d m\n", hpos, vpos);
lastx = stringstart = lastend = hpos;
lasty = vpos;
} /* End of endline */
/*****************************************************************************/
addchar(c)
int c;
{
/*
*
* 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 */
/*****************************************************************************/
addoctal(c)
int c;
{
/*
*
* Add c to the current string as an octal escape.
*
*/
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 */
/*****************************************************************************/
charlib(code)
int code; /* either 1 or 2 */
{
char *name; /* name of the character */
char tname[10]; /* in case it's a single ASCII character */
char temp[150];
/*
*
* 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.
*
*/
flushtext();
if ( lastc < ALPHABET ) { /* ASCII character */
sprintf(tname, "%.3o", lastc);
name = tname;
} else name = chname(lastc);
if ( downloaded[lastc] == 0 ) {
sprintf(temp, "%s/dev%s/charlib/%s", fontdir, realdev, name);
if ( exportfile(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 */
/*****************************************************************************/
reset()
{
/*
*
* Reset variables that keep track of the printer's current position, size and
* font. Eventually forces things back in sync before oput() prints the next
* character.
*
*/
lastx = -(slop + 1);
lasty = -1;
lastfont = lastsize = -1;
} /* End of reset */
/*****************************************************************************/
resetpos()
{
/*
*
* Reset the position tracking variables. Forces oput() to get positions back
* in sync before printing the next character.
*
*/
lastx = -(slop + 1);
lasty = -1;
} /* End of resetpos */
/*****************************************************************************/
save()
{
/*
*
* Save the current PostScript environment. Initialize things that may have
* disappeared after the preceeding restore.
*
*/
fprintf(tf, "/saveobj save def\n");
fprintf(tf, "mark\n");
if ( encoding != realencoding )
fprintf(tf, "%d setdecoding\n", encoding);
if ( gotcolor == TRUE ) /* prevent getcolor() recursion */
setcolor();
} /* End of save */
/*****************************************************************************/
restore()
{
/*
*
* Restore the previous PostScript environment.
*
*/
flushtext();
fprintf(tf, "cleartomark\n");
fprintf(tf, "saveobj restore\n");
reset();
} /* End of restore */
/*****************************************************************************/
exportfile(path)
char *path;
{
int val = FALSE;
/*
*
* Exports the contents of file path to the global environment. Returns TRUE
* if we're doing output (i.e. tf == stdout) and the copy worked.
*
*/
if ( tf == stdout && access(path, 04) == 0 ) {
restore();
fprintf(tf, "%s", BEGINGLOBAL);
val = cat(path);
fprintf(tf, "%s", ENDGLOBAL);
save();
} /* End if */
return(val);
} /* End of exportfile */
/*****************************************************************************/
exportstring(str)
char *str;
{
/*
*
* Exports string str to the global environment. No return value needed yet.
*
*/
if ( tf == stdout && str != NULL && *str != '\0' ) {
restore();
fprintf(tf, "%s", BEGINGLOBAL);
fprintf(tf, "%s", str);
fprintf(tf, "%s", ENDGLOBAL);
save();
} /* End if */
} /* End of exportstring */
/*****************************************************************************/
redirect(pg)
int pg;
{
static FILE *fp_null = NULL;
/*
*
* 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 */
/*****************************************************************************/