/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 1994 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ /* All Rights Reserved */ /* from OpenSolaris "dpost.c 1.11 05/06/08 SMI" SVr4.0 1.2 */ /* * Portions Copyright (c) 2005 Gunnar Ritter, Freiburg i. Br., Germany * * Sccsid @(#)dpost.c 1.176 (gritter) 8/19/08 */ /* * * dpost - troff post-processor for PostScript printers. * * A program that translates output generated by the device independent troff * into PostScript. Much was borrowed from dimpress and dps (formally dlzw), * and even though the code has been changed, credit has to be given to Richard * Flood for his early work on the PostScript driver. * * Among the most interesting new features are color support (see devcntrl() and * file color.c) and code to handle complex paths pieced together using any of the * standard drawing commands (see devcntrl() and file draw.c). Reverse video mode * has also been included as a special case of the color support. Two encoding * schemes based on widthshow are also new additions. The safe one is obtained when * you set encoding to 2 (eg. using the -e2 option). The slightly faster method * is obtained by setting encoding to 3 (eg. using the -e3 option), although it's * not recommended. Rounding errors in character widths can accumulate and become * quite noticeable by the time you get to the right margin. More often than not * you end up getting a ragged right margin. * * The program handles files formatted for any device, although the best and * most efficient output is generated when the font and description files match * PostScript's resident fonts. Device emulation is relatively expensive, and * can produce output files that are more than twice the size of the input files. * In most cases output files will be smaller than input files, perhaps by up to * 40 percent, although the results you get depend on what you're doing and the * text encoding you're using. You'll get the worst results if you're emulating * another device, using special bitmap characters, like the logo, or doing lots * of vertical motion or drawing. * * PostScript fonts don't support all of troff's characters, so some have to * be built by special PostScript procedures. Those routines can be found in * *fontdir/devpost/charlib, and are only used when we try to print a character * that has been assigned a code less than 32. Definitions are only made the * first time each character is used. Subsequent requests to print the character * only generate a call to the PostScript procedure that's been copied to the * output file. For example you'll find a file called sq in directory * *fontdir/devpost/charlib. It defines a PostScript procedure called build_sq * that's called whenever we need to print a square. Special characters that * have been assigned a code of 2 are expected to come in two pieces. The * definition part and bitmap part (or whatever). The definition is only made * once, but the contents of the character's .map file are copied to the output * file each time, immediately after charlib() generates the call to the * PostScript procedure (build_?? ) that builds the character. That's typically * how logos built from bitmaps would be handled. * * Several different methods can be used to encode lines of text. What's done * depends on the value assigned to encoding. Print time should decrease as * encoding increases (up to MAXENCODING). Setting encoding to 0, which should * probably be the default, produces output essentially identical to the original * version of dpost. It's the slowest but most stable method of encoding lines of * text, and won't be bothered by rounding errors in the font width tables that * could become noticeable by the time you get to the end of a line. Other schemes * seem to work, but aren't well tested and are not guaranteed for all possible * jobs. encoding can be changed on the command line using the -e option. Part of * the support for different encoding schemes was to move control of all text * related output to separate routines. It makes dpost work harder, but changing * things is easy. For example adding stuff to support widthshow took less than * an hour. * * I've also added code that handles the DOCUMENTFONTS comment, although it's * only produced for those fonts in directory /usr/lib/font/devpost that have an * associated .name file. The first string in a .name file should be the (long) * PostScript name (eg. Times-Roman in R.name). For now everything else in the * .name file is ignored, although that may also change. You'll find .name files * for all the supported fonts in the devpost source directory, although they may * not be installed in /usr/lib/font/devpost. * * The PostScript prologue is copied from *prologue before any of the input files * are translated. The program expects the following procedures are avaliable: * * setup * * mark ... setup - * * Handles special initialization stuff that depends on how the program * was called. Expects to find a mark followed by key/value pairs on the * stack. The def operator is applied to each pair up to the mark, then * the default state is set up. An 'x res' command must preceed the * 'x init' command! * * pagesetup * * page pagesetup - * * Called at the start of each page, immediately after the page level * save, to do special initialization on a per page basis. Right now the * only argument is the current page number, and actually nothing of any * importance is currently done. * * setdecoding * * num setdecoding - * * Selects the text decoding procedure (ie. what's assigned to PostScript * procedure t) from the decodingdefs array defined in the prologue. num * should be the value assigned to variable encoding (in dpost) and will * remain constant throughout a job, unless special features, like reverse * video printing, are requested. The text encoding scheme can be set on * the command line using the -e option. Print time and the size of the * output file will usually decrease as the value assigned to encoding * increases. * * f * * size font f - * * Selects the size and font to be used for character imaging. Font names * are defined, in *prologue, so they agree with the one or two character * names used by troff. * * m * * x y m - * * Moves to point (x, y). Normally only used when the vertical position * changes. Horizontal positioning between words (or letters) is handled * in procedure t (below). * * t * * mark text t mark * * Processes everything on the stack, up to the mark, as a single line * of text to be printed at a fixed vertical position. What's put out as * text depends on the encoding scheme. Setting encoding to 0 produces * output essentially identical to the original version of dpost. In that * case everything on the stack, up to a mark, is interpreted (from top * down) as an absolute horizontal position and a string to be printed at * that point. For example the stack might look like, * * mark(this)1000(is)1100(an)1200(example)1300 t * * Procedure t would go through the stack, up to the mark, adjusting the * horizontal position before printing each string. In other encoding * schemes, like the one based on widthshow, strings containing several * space separated words would appear on the stack, and each one would be * preceeded by a number that's expected to be added to the width of a * space. For example we might have, * * mark(an example)30(this is)40 2 1000 2000 t * * where (1000, 2000) is where the first string starts and 2 is the repeat * count (ie. number of string and space pairs on the stack). * * w * * string x y w - * * Prints a single word starting at position (x, y). Only used in the more * complicated encoding schemes (eg. the ones based on widthshow). * * done * * Makes sure the last page is printed. Only needed when we're printing * more than one page on each sheet of paper. * * The PostScript procedures that support troff's drawing commands have been moved * out of *prologue and put in a separate file (ie. DRAW as defined in path.h). * The procedures are used by the routines in file draw.c, and are copied to the * prologue. * * Many default values, like the magnification and orientation, are defined in * the prologue, which is where they belong. If they're changed (by options), an * appropriate definition is made after the prologue is added to the output file. * The -P option passes arbitrary PostScript through to the output file. Among * other things it can be used to set (or change) values that can't be accessed by * other options. * * * output language from troff: * all numbers are character strings * * sn size in points * fn font as number from 1-n * cx ascii character x * Cxyz funny char xyz. terminated by white space * Hn go to absolute horizontal position n * Vn go to absolute vertical position n (down is positive) * hn go n units horizontally (relative) * vn ditto vertically * nnc move right nn, then print c (exactly 2 digits!) * (this wart is an optimization that shrinks output file size * about 35% and run-time about 15% while preserving ascii-ness) * Dt ...\n draw operation 't': * Dl x y line from here by x,y * Dc d circle of diameter d with left side here * De x y ellipse of axes x,y with left side here * Da x1 y1 x2 y2 arc counter-clockwise from current point (x, y) to * (x + x1 + x2, y + y1 + y2) * D~ x y x y ... wiggly line by x,y then x,y ... * nb a end of line (information only -- no action needed) * b = space before line, a = after * p new page begins -- set v to 0 * #...\n comment * x ...\n device control functions: * x i init * x T s name of device is s * x r n h v resolution is n/inch * h = min horizontal motion, v = min vert * x p pause (can restart) * x s stop -- done forever * x t generate trailer * x f n s font position n contains font s * x H n set character height to n * x S n set slant to N * * Subcommands like "i" are often spelled out like "init". * * * * To get dpost output conforming to Adobe's structuring conventions (DSC), * all output is accumulated in temporary files first. When the document is * completed, files that contain global data are output first, followed by * regular commands, all surrounded by DSC comments. Speed problems, which * were the reason why this was not done by previous versions of dpost, are * no longer of concern in 2005 since several hundred pages of text are * processed now in less than a second. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "comments.h" /* PostScript file structuring comments */ #include "gen.h" /* general purpose definitions */ #include "path.h" /* for the prologue and a few other files */ #include "ext.h" /* external variable definitions */ #include "dev.h" /* typesetter and font descriptions */ #include "dpost.h" /* a few definitions just used here */ #include "asciitype.h" #include "afm.h" #include "fontmap.h" #if defined (__GLIBC__) && defined (_IO_getc_unlocked) #undef getc #define getc(f) _IO_getc_unlocked(f) #endif #if defined (__GLIBC__) && defined (_IO_putc_unlocked) #undef putc #define putc(c, f) _IO_putc_unlocked(c, f) #endif char *progname; char *prologue = DPOST; /* the basic PostScript prologue */ char *colorfile = COLOR; /* things needed for color support */ char *drawfile = DRAW; /* and drawing */ char *cutmarksfile = CUTMARKS; 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. * * Encoding 4 is new as of 9/8/05. It stores only the distances between words and * thus saves a bit of output size. It is automatically enabled at high resolutions. * */ int encoding = DFLTENCODING; int realencoding = DFLTENCODING; int maxencoding = MAXENCODING; int eflag; int LanguageLevel; /* PostScript output language level */ static int Binary; /* PostScript output contains binary data */ /* * * seenfonts[] keeps track of the fonts we've used, based on internal numbers. It * helps manage host resident fonts and the DOCUMENTFONTS comment, but only works * if all fonts have internal numbers less than MAXINTERNAL. *docfonts counts the * number of font names we've recorded in *temp_file. If it's positive routine * done() adds *temp_file to the output file before quitting. * */ char seenfonts[MAXINTERNAL+1]; int docfonts = 0; struct afmtab **afmfonts; int afmcount = 0; /* * * devname[] is the device troff used when the job was formatted, while *realdev * is combined with *fontdir and used to locate the font and device tables that * that control the translation of the input files into PostScript. *realdev can * be changed using the -T option, but if you do you may end up getting garbage. * The character code field must agree with PostScript's font encoding and font * names must be properly mapped into PostScript font names in the prologue. * */ #define devname troff_devname 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 starts this way */ struct Font **fontbase; /* FONT files begin this way */ int *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 */ unsigned short **fitab; /* locates char info on each font */ int **fontab; /* character width data for each font */ unsigned short **codetab; /* and codes to get characters printed */ char **kerntab; /* for makefont() */ /* * * Special characters missing from standard PostScript fonts are defined by files * in directory *fontdir/devpost/charlib. Files have the same names as the troff * special character names (for now at least) and each one defines a PostScript * procedure that begins with the prefix build_ and ends with the character's * name. * * For example, the routine used to build character \(12, would be build_12. * downloaded[] points to an array, allocated in fontinit(), that keeps track of * the characters that have already been defined - so we only do it once. * */ char *downloaded; /* nonzero means it's been downloaded */ /* * * Variables that keep track of troff's requests. All are set from values in the * input files. nfonts is adjusted in t_fp() as new fonts are mounted. * */ int nfonts = 0; /* number of font positions */ int size = 1; /* current size - internal value */ #define FRACTSIZE -23 /* if size == FRACTSIZE then ... */ float fractsize = 0; /* fractional point size */ int font = 0; /* font position we're using now */ int subfont = 0; /* extra encoding vector */ 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 track = 0; /* tracking hint from troff */ int lasttrack = 0; /* previous tracking hint */ int tracked; /* records need to flush track */ int lastc = 0; /* and its name (or index) */ int res; /* resolution assumed in input file */ float widthfac = 1.0; /* for emulation = res/dev.res */ float horscale = 1.0; /* horizontal font scaling */ float lasthorscale = 1.0; /* last horizontal font scaling */ int wordspace = 0; /* w command was last */ /* * * Remember some of the same things, but this time for the printer. lastend is only * used when we're doing reverse video, and is where the last character on the * current line was printed. * */ int lastsize = -1; /* last internal size we used */ float lastfractsize = -1; /* last fractional size */ int lastfont = -1; /* last font we told printer about */ int lastsubfont = -1; /* last extra encoding vector */ float lastx = -1; /* printer's current position */ int lasty = -1; int savey = -1; int lastend; /* where last character on this line was */ /* * * fontname[] keeps track of the mounted fonts. Filled in (by t_fp()) from data * in the binary font files. * * When font metrics are directly read from AFM files, all characters that * are not ASCII are put into the remaining positions in PostScript encoding * vectors. Their position in these vectors in recorded in afm->encmap, and * characters from troff are translated if necessary. * */ struct { struct afmtab *afm; /* AFM data, if any */ char *name; /* name of the font loaded here */ int number; /* its internal number */ float fontheight; /* points from x H ... */ int fontslant; /* angle from x S ... */ } fontname[NFONT+1]; /* * * All the special fonts will be mounted after the last legitimate font position. * It helps when we're translating files prepared for devices, like the 202, that * have a different set of special fonts. The set of special fonts needed when * *realdev's tables are used may not get mounted when we're emulating another * device. gotspecial keeps track of whether we've done it yet. seenpage is set * to TRUE after we've seen the first page command in the input file. It controls * what's done in t_font() and is needed because nfonts is no longer set when the * DESC file is read, but rather is updated from "x font" commands in the * input files. gotregular ensures that at least one regular font is mounted. * */ int gotspecial = FALSE; int gotregular = 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 Sflag; /* unless -S gives explicit slop */ 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 laststrstart = INT_MIN; /* save for optimization */ int spacecount = 0; /* spaces seen so far on current line */ int charcount = 0; /* characters on current line */ /* * * Things that can be used by text line encoding schemes that need to read and * remember an entire line before doing any output. The strings that make up the * line can be saved in array strings[] and accessed by fields in line[]. *strptr * points to the next free slot in strings[]. * */ 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 */ /* * * Variables and functions for the pdfmark operator. * */ static char *Author; /* DOCINFO /Author */ static char *Title; /* DOCINFO /Title */ static char *Subject; /* DOCINFO /Subject */ static char *Keywords; /* DOCINFO /Keywords */ static struct Bookmark { char *Title; /* OUT /Title */ char *title; /* unencoded title */ int Count; /* OUT /Count */ int level; /* used to generate count */ int closed; /* the bookmark is closed initially */ } *Bookmarks; static size_t nBookmarks; static double pagelength = 792; /* lenght of page in points */ #define MAXBOOKMARKLEVEL 20 static void orderbookmarks(void); static struct box { int val[4]; int flag; } mediasize, bleedat, trimat, cropat; /* * * For the -M option. * */ static enum { M_NONE = 000, M_CUT = 001, M_STAR = 002, M_REG = 004, M_COL = 010, M_ALL = 077 } Mflag; static void setmarks(char *); /* * * A few variables that are really only used if we're doing accounting. Designed * for our use at Murray Hill and probably won't suit your needs. Changes should * be easy and can be made in routine account(). * */ int printed = 0; /* charge for this many pages */ /* * * Output and accounting file definitions. The PostScript output always goes to * stdout or /dev/null, while the accounting file can be selected using the -A * option. * */ FILE *tf = NULL; /* PostScript output goes here */ FILE *gf = NULL; /* global data goes here */ FILE *rf = NULL; /* resource data goes here */ FILE *sf = NULL; /* supplied resource comments go here */ FILE *nf = NULL; /* needed resource comments go here */ FILE *pf = NULL; /* elements of _custompagesetup */ int sfcount; /* count of supplied resources */ int nfcount; /* count of needed resources */ int ostdout; /* old standard output */ FILE *fp_acct = NULL; /* accounting stuff written here */ /* * * Very temporary space that can be used to do things like building up pathnames * immediately before opening a file. Contents may not be preserved across calls * to subroutines defined in this file, so it probably should only be used in low * level subroutines like loadfont() or fontinit() and nowhere else. * */ char temp[4096]; /*****************************************************************************/ static char *linkborderstyle; static char *ulinkborderstyle; static void sethorscale(char *); static void t_papersize(char *); static void t_cutat(const char *, struct box *, char *); static void t_track(char *); static void t_strack(void); static void t_pdfmark(char *); static void t_locale(char *); static void t_anchor(char *); static void t_link(char *); static void t_linkcolor(char *); static void t_linkborder(char *); static void t_ulink(char *); static void t_ulinkcolor(char *); static void t_ulinkborder(char *); static char *t_linkborderstyle(char *); static int mb_cur_max; /*****************************************************************************/ int main(int agc, char *agv[]) { const char template[] = "/var/tmp/dpostXXXXXX"; char *tp; FILE *fp; /* * * A program that translates troff output into PostScript. All the input files * must have been formatted for the same device, which doesn't necessarily have to * be *realdev. If there's more than one input file, each begins on a new page. * */ setlocale(LC_CTYPE, ""); mb_cur_max = MB_CUR_MAX; ostdout = dup(1); if (close(mkstemp(tp = strdup(template))) < 0 || freopen(tp, "r+", stdout) == NULL) { perror(tp); return 2; } unlink(tp); if (close(mkstemp(tp = strdup(template))) < 0 || (gf = fopen(tp, "r+")) == NULL) { perror(tp); return 2; } unlink(tp); if (close(mkstemp(tp = strdup(template))) < 0 || (rf = fopen(tp, "r+")) == NULL) { perror(tp); return 2; } unlink(tp); if (close(mkstemp(tp = strdup(template))) < 0 || (sf = fopen(tp, "r+")) == NULL) { perror(tp); return 2; } unlink(tp); if (close(mkstemp(tp = strdup(template))) < 0 || (nf = fopen(tp, "r+")) == NULL) { perror(tp); return 2; } unlink(tp); if (close(mkstemp(tp = strdup(template))) < 0 || (pf = fopen(tp, "r+")) == NULL) { perror(tp); return 2; } unlink(tp); argc = agc; /* global so everyone can use them */ argv = agv; progname = prog_name = argv[0]; /* just for error messages */ init_signals(); /* sets up interrupt handling */ options(); /* command line options */ arguments(); /* translate all the input files */ done(); /* add trailing comments etc. */ fp = fdopen(ostdout, "w"); header(fp); /* PostScript file structuring comments */ account(); /* job accounting data */ return(x_stat); /* everything probably went OK */ } /* End of main */ /*****************************************************************************/ int putint(int n, FILE *fp) { char buf[20]; int c = 0, i; /* * * Print an integer in PostScript binary token representation. * */ if (n >= -128 && n <= 127) { buf[c++] = 136; buf[c++] = n; } else if (n >= -32768 && n <= 32767) { buf[c++] = 134; buf[c++] = (n&0xff00) >> 8; buf[c++] = (n&0x00ff); } else { buf[c++] = 132; buf[c++] = (n&0xff000000) >> 24; buf[c++] = (n&0x00ff0000) >> 16; buf[c++] = (n&0x0000ff00) >> 8; buf[c++] = (n&0x000000ff); } for (i = 0; i < c; i++) putc(buf[i]&0377, fp); return c; } int putstring1(const char *sp, int n, FILE *fp) { /* * * Print a string in PostScript binary token representation. * */ putc(142, fp); putc(n, fp); fwrite(sp, 1, n, fp); return n + 2; } int putstring(const char *sp, int n, FILE *fp) { int c = 0, m; do { m = n > 250 ? 250 : n; c += putstring1(sp, m, fp); sp += m; n -= m; } while (n > 0); return c; } /*****************************************************************************/ void init_signals(void) { void interrupt(int); /* signal handler */ /* * * Make sure we handle interrupts. * */ if ( signal(SIGINT, interrupt) == SIG_IGN ) { signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGHUP, SIG_IGN); } else { signal(SIGHUP, interrupt); signal(SIGQUIT, interrupt); } /* End else */ signal(SIGTERM, interrupt); } /* End of init_signals */ /*****************************************************************************/ static char * pdfdate(time_t *tp, char *buf, size_t size) { struct tm *tmptr; int tzdiff, tzdiff_hour, tzdiff_min; tzdiff = *tp - mktime(gmtime(tp)); tzdiff_hour = (int)(tzdiff / 60); tzdiff_min = tzdiff_hour % 60; tzdiff_hour /= 60; tmptr = localtime(tp); if (tmptr->tm_isdst > 0) tzdiff_hour++; snprintf(buf, size, "(D:%04d%02d%02d%02d%02d%02d%+03d'%02d')", tmptr->tm_year + 1900, tmptr->tm_mon + 1, tmptr->tm_mday, tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, tzdiff_hour, tzdiff_min); return buf; } /*****************************************************************************/ static void pdfbox(const char *boxname, struct box *bp, FILE *fp, int perpage) { double llx, lly, urx, ury; if (bp->flag == 0) return; llx = bp->val[0] * 72.0 / res; lly = pagelength - ((bp->val[1] + bp->val[3]) * 72.0 / res); urx = (bp->val[0] + bp->val[2]) * 72.0 / res; ury = pagelength - (bp->val[1] * 72.0 / res); fprintf(gf, "/_%s [%g %g %g %g] def\n", boxname, llx, lly, urx, ury); if (perpage) fprintf(fp, "[ {ThisPage} 1 dict dup /%s _%s put /PUT pdfmark\n", boxname, boxname); else fprintf(gf, "[ /%s _%s /PAGES pdfmark\n", boxname, boxname); } /*****************************************************************************/ void header(FILE *fp) { /* * * Print the DSC header, followed by the data generated so far. This function * is now called after all input has been processed. * */ struct Bookmark *bp; time_t now; int n; double x = 0, y = 0; char buf[4096]; char crdbuf[40]; time(&now); if (mediasize.flag) { x = mediasize.val[2] * 72.0 / res; y = mediasize.val[3] * 72.0 / res; } fprintf(fp, "%s", CONFORMING); fprintf(fp, "%s %s\n", CREATOR, creator); fprintf(fp, "%s %s", CREATIONDATE, ctime(&now)); if (LanguageLevel > 1) fprintf(fp, "%%%%LanguageLevel: %d\n", LanguageLevel); if (Binary) fprintf(fp, "%%%%DocumentData: Binary\n"); if ( temp_file != NULL ) { if ( docfonts > 0 ) { cat(temp_file, fp); putc('\n', fp); } /* End if */ unlink(temp_file); } /* End if */ fprintf(fp, "%s %d\n", PAGES, printed); if (mediasize.flag & 2) fprintf(fp, "%%%%DocumentMedia: x%gy%g %g %g 0 () ()\n", x, y, x, y); fflush(nf); rewind(nf); while ((n = fread(buf, 1, sizeof buf, nf)) > 0) fwrite(buf, 1, n, fp); fflush(sf); rewind(sf); while ((n = fread(buf, 1, sizeof buf, sf)) > 0) fwrite(buf, 1, n, fp); fprintf(fp, "%s", ENDCOMMENTS); fprintf(fp, "%s\n", "%%BeginProlog"); if ( cat(prologue, fp) == FALSE ) error(FATAL, "can't read %s", prologue); fflush(rf); rewind(rf); while ((n = fread(buf, 1, sizeof buf, rf)) > 0) fwrite(buf, 1, n, fp); fprintf(fp, "%s", ENDPROLOG); fprintf(fp, "%s", BEGINSETUP); fprintf(fp, "\ [ /CreationDate %s\n\ /Creator (%s)\n", pdfdate(&now, crdbuf, sizeof crdbuf), creator); if (Author) fprintf(fp, " /Author %s\n", Author); if (Title) fprintf(fp, " /Title %s\n", Title); if (Subject) fprintf(fp, " /Subject %s\n", Subject); if (Keywords) fprintf(fp, " /Keywords %s\n", Keywords); fprintf(fp, "/DOCINFO pdfmark\n"); if (Bookmarks) { orderbookmarks(); for (bp = &Bookmarks[0]; bp < &Bookmarks[nBookmarks]; bp++) { fprintf(fp, "[ /Title %s\n", bp->Title); if (bp->Count) fprintf(fp, " /Count %d\n", bp->closed ? -bp->Count : bp->Count); fprintf(fp, " /Dest /Bookmark$%d\n" "/OUT pdfmark\n", (int)(bp - &Bookmarks[0])); } } fflush(pf); rewind(pf); fprintf(fp, "/_custompagesetup {\n"); pdfbox("TrimBox", &trimat, fp, 1); pdfbox("BleedBox", &bleedat, fp, 1); pdfbox("CropBox", &cropat, fp, 0); while ((n = fread(buf, 1, sizeof buf, pf)) > 0) fwrite(buf, 1, n, fp); fprintf(fp, "} def\n"); fprintf(fp, "/_marks {\n"); if (Mflag & M_CUT) fprintf(fp, "_cutmarks\n"); if (Mflag & M_REG) fprintf(fp, "_regmarks\n"); if (Mflag & M_STAR) fprintf(fp, "_startargets\n"); if (Mflag & M_COL) fprintf(fp, "_colorbars\n"); fprintf(fp, "} def\n"); fflush(gf); rewind(gf); while ((n = fread(buf, 1, sizeof buf, gf)) > 0) fwrite(buf, 1, n, fp); if (mediasize.flag) { fprintf(fp, "/pagebbox [0 0 %g %g] def\n", x, y); fprintf(fp, "userdict /gotpagebbox true put\n"); if (mediasize.flag & 2) fprintf(fp, "/setpagedevice where {pop " "1 dict dup /PageSize [%g %g] put setpagedevice" "} if\n", x, y); } fprintf(fp, "mark\n"); fflush(stdout); rewind(stdout); while ((n = fread(buf, 1, sizeof buf, stdout)) > 0) fwrite(buf, 1, n, fp); fprintf(fp, "%s", ENDOFFILE); } /* End of header */ /*****************************************************************************/ void options(void) { const char optnames[] = "a:c:e:m:n:o:p:tw:x:y:A:C:J:F:H:L:M:OP:R:S:T:DI"; int ch; /* name returned by getopt() */ /* * * Reads and processes the command line options. There are, without a doubt, too * many options! * */ while ( (ch = getopt(argc, argv, optnames)) != EOF ) { switch ( ch ) { case 'a': /* aspect ratio */ fprintf(stdout, "/aspectratio %s def\n", optarg); break; case 'c': /* number of copies */ copies = atoi(optarg); fprintf(stdout, "/#copies %s store\n", optarg); break; case 'e': /* change the encoding scheme */ if ( (encoding = atoi(optarg)) < 0 || encoding > MAXENCODING ) encoding = DFLTENCODING; else eflag = 1; realencoding = encoding; break; case 'm': /* magnification */ fprintf(stdout, "/magnification %s def\n", optarg); break; case 'n': /* forms per page */ formsperpage = atoi(optarg); fprintf(stdout, "%s %s\n", FORMSPERPAGE, optarg); fprintf(stdout, "/formsperpage %s def\n", optarg); break; case 'o': /* output page list */ out_list(optarg); break; case 'p': /* landscape or portrait mode */ if ( *optarg == 'l' ) fprintf(stdout, "/landscape true def\n"); else fprintf(stdout, "/landscape false def\n"); break; case 't': /* just for compatibility */ break; case 'w': /* line width for drawing */ fprintf(stdout, "/linewidth %s def\n", optarg); break; case 'x': /* shift horizontally */ fprintf(stdout, "/xoffset %s def\n", optarg); break; case 'y': /* and vertically on the page */ fprintf(stdout, "/yoffset %s def\n", optarg); break; case 'A': /* force job accounting */ case 'J': if ( (fp_acct = fopen(optarg, "a")) == NULL ) error(FATAL, "can't open accounting file %s", optarg); break; case 'C': /* copy file to straight to output */ if ( cat(optarg, stdout) == FALSE ) error(FATAL, "can't read %s", optarg); break; case 'F': /* font table directory */ fontdir = optarg; break; case 'H': /* host resident font directory */ hostfontdir = optarg; break; case 'L': /* PostScript prologue file */ setpaths(optarg); break; case 'M': /* print cut marks */ setmarks(optarg); break; case 'O': /* turn picture inclusion off */ picflag = OFF; break; case 'P': /* PostScript pass through */ fprintf(stdout, "%s\n", optarg); break; case 'R': /* special global or page level request */ saverequest(optarg); break; case 'S': /* horizontal position error */ if ( (pointslop = atof(optarg)) < 0 ) pointslop = 0; Sflag = 1; break; case 'T': /* target printer */ realdev = optarg; break; case 'D': /* debug flag */ debug = ON; tf = stdout; break; case 'I': /* ignore FATAL errors */ ignore = ON; break; case '?': /* don't know the option */ error(FATAL, ""); break; default: error(FATAL, "missing case for option %c", ch); break; } /* End switch */ } /* End while */ argc -= optind; /* get ready for non-options args */ argv += optind; if (Mflag) { FILE *otf = tf; tf = stdout; doglobal(cutmarksfile); tf = otf; } } /* End of options */ /*****************************************************************************/ void setpaths ( char *name /* string that followed the -L option */ ) { char *path; /* start of the pathname */ /* * * Extends the -L option to permit run time modification of pathnames that were * fixed or didn't exist in previous versions of dpost. For example, the PostScript * drawing procedures have been moved out of *prologue and put in *drawfile. The * new syntax can be either -Lfile or -Lname:file. If the "name:" prefix is omitted * file will be used as the prologue, otherwise name should be one of "prologue", * "font", "draw", "color", or "form" and is used to select the pointer that gets * set to string "file". * */ for ( path = name; *path; path++ ) if ( *path == ':' || *path == ' ' ) { while ( *path == ':' || *path == ' ' ) path++; break; } /* End if */ if ( *path == '\0' ) /* didn't find a "name:" prefix */ path = name; if ( path == name || strncmp(name, "prologue", strlen("prologue")) == 0 ) prologue = path; else if ( strncmp(name, "draw", strlen("draw")) == 0 ) drawfile = path; else if ( strncmp(name, "color", strlen("color")) == 0 ) colorfile = path; else if ( strncmp(name, "form", strlen("form")) == 0 ) formfile = path; else if ( strncmp(name, "baseline", strlen("baseline")) == 0 ) baselinefile = path; else if ( strncmp(name, "cutmarks", strlen("cutmarks")) == 0 ) cutmarksfile = path; } /* End of setpaths */ /*****************************************************************************/ static int prefix(const char *str, const char *pfx) { while (*pfx && *str == *pfx) str++, pfx++; return *str == 0; } static void setmarks(char *str) { char *sp; int c; do { for (sp = str; *sp && *sp != ':'; sp++); c = *sp; *sp = 0; if (prefix(str, "cutmarks")) Mflag |= M_CUT; else if (prefix(str, "registrationmarks")) Mflag |= M_REG; else if (prefix(str, "startargets")) Mflag |= M_STAR; else if (prefix(str, "colorbars")) Mflag |= M_COL; else if (prefix(str, "all")) Mflag |= M_ALL; else error(FATAL, "unknown mark: -M %s", str); *sp = c; str = &sp[1]; } while (c); } /*****************************************************************************/ void setup(void) { /* * Handles things that must be done after the options are read but before the * input files are processed. Called from t_init() after an "x init" command is * read, because we need the resolution before we can generate the call to the * setup procedure defined in *prologue. Only allowing one call to setup assumes * all the input files have been prepared for the same device. * */ writerequest(0, stdout); /* global requests eg. manual feed */ fprintf(stdout, "/resolution %d def\n", res); fprintf(stdout, "setup\n"); fprintf(stdout, "%d setdecoding\n", encoding); if ( formsperpage > 1 ) { /* followed by stuff for multiple pages */ if ( cat(formfile, stdout) == FALSE ) error(FATAL, "can't read %s", formfile); fprintf(stdout, "%d setupforms\n", formsperpage); } /* End if */ fprintf(stdout, "%s", ENDSETUP); } /* End of setup */ /*****************************************************************************/ void arguments(void) { FILE *fp; /* next input file */ /* * * Makes sure all the non-option command line arguments are processed. If we get * here and there aren't any arguments left, or if '-' is one of the input files * we'll translate stdin. * */ if ( argc < 1 ) conv(stdin); else while ( argc > 0 ) { if ( strcmp(*argv, "-") == 0 ) fp = stdin; else if ( (fp = fopen(*argv, "r")) == NULL ) error(FATAL, "can't open %s", *argv); conv(fp); if ( fp != stdin ) fclose(fp); argc--; argv++; } /* End while */ } /* End of arguments */ /*****************************************************************************/ void done(void) { /* * * Finished with all the input files, so mark the end of the pages with a TRAILER * comment, make sure the last page prints, and add things like the DOCUMENTFONTS * and PAGES comments that can only be determined after all the input files have * been read. * */ fprintf(stdout, "%s", TRAILER); fprintf(stdout, "done\n"); } /* End of done */ /*****************************************************************************/ void account(void) { /* * * Writes an accounting record to *fp_acct provided it's not NULL. Accounting is * requested using the -A or -J options. * */ if ( fp_acct != NULL ) fprintf(fp_acct, " print %d\n copies %d\n", printed, copies); } /* End of account */ /*****************************************************************************/ void conv( register FILE *fp /* next input file */ ) { register int c; /* usually first char in next command */ int m, n, n1, m1; /* when we need to read integers */ char str[4096]; /* for special chars and font numbers */ char b; /* * * Controls the translation of troff's device independent output language into * PostScript. The call to t_page() that prints the last page is made when we * exit the loop, but probably belongs in t_trailer(). * */ redirect(-1); /* only do output after a page command */ lineno = 1; /* line in current file */ while ((c = getc(fp)) != EOF) { switch (c) { case '\n': /* just count this line */ lineno++; break; case ' ': /* when input is text */ case 0: /* occasional noise creeps in */ break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* two motion digits plus a character */ hmot((c-'0')*10 + getc(fp)-'0'); put1(getc(fp)); break; case 'c': /* single ascii character */ put1(getc(fp)); break; case 'C': /* special character */ sget(str, sizeof str, fp); put1s(str); break; case 'N': /* character at position n */ fscanf(fp, "%d", &m); endtext(); oput(m); endtext(); break; case 'D': /* drawing functions */ endtext(); getdraw(); if ( size != lastsize || (size == FRACTSIZE && fractsize != lastfractsize) || horscale != lasthorscale) { subfont = 0; t_sf(0); } switch ((c=getc(fp))) { case 'p': /* draw a path */ while (fscanf(fp, "%d %d", &n, &m) == 2) drawline(n, m); lineno++; break; case 'P': /* solid polygon */ fprintf(tf, "newpath %d %d neg moveto\n", hpos, vpos); while (fscanf(fp, "%d %d", &n, &m) == 2) { hpos += n; vpos += m; fprintf(tf, "%d %d neg lineto\n", hpos, vpos); } fprintf(tf, "closepath fill\n"); lineno++; break; case 'l': /* draw a line */ fscanf(fp, "%d %d %c", &n, &m, &b); n1 = b; drawline(n, m); break; case 'c': /* circle */ case 'C': /* filled circle */ fscanf(fp, "%d", &n); drawcirc(n, c); break; case 'e': /* ellipse */ case 'E': /* filled ellipse */ fscanf(fp, "%d %d", &m, &n); drawellip(m, n, c); break; case 'a': /* counter-clockwise arc */ case 'A': /* clockwise arc */ fscanf(fp, "%d %d %d %d", &n, &m, &n1, &m1); drawarc(n, m, n1, m1, c); break; case 'q': /* spline without end points */ drawspline(fp, 1); lineno++; break; case '~': /* wiggly line */ drawspline(fp, 2); lineno++; break; case 't': /* set line width, ignore */ fscanf(fp, "%d %d", &m, &n); hgoto(hpos + m); lineno++; break; case 'F': /* color scheme, ignore */ case 'f': /* filling color, ignore */ fgets(str, sizeof str, fp); lineno++; break; default: error(FATAL, "unknown drawing function %c", c); break; } /* End switch */ break; case 's': /* use this point size */ fscanf(fp, "%d", &n); /* ignore fractional sizes */ if (n != FRACTSIZE) setsize(t_size(n), 0); else { float f; fscanf(fp, "%f", &f); setsize(FRACTSIZE, f); } break; case 'f': /* use font mounted here */ sget(str, sizeof str, fp); setfont(t_font(str)); break; case 'H': /* absolute horizontal motion */ fscanf(fp, "%d", &n); hgoto(n); break; case 'h': /* relative horizontal motion */ fscanf(fp, "%d", &n); hmot(n); break; case 'w': /* word space */ wordspace++; break; case 'V': /* absolute vertical position */ fscanf(fp, "%d", &n); vgoto(n); break; case 'v': /* relative vertical motion */ fscanf(fp, "%d", &n); vmot(n); break; case 'p': /* new page */ fscanf(fp, "%d", &n); t_page(n); break; case 'n': /* end of line */ while ( (c = getc(fp)) != '\n' && c != EOF ) ; t_newline(); lineno++; break; case '#': /* comment */ while ( (c = getc(fp)) != '\n' && c != EOF ) ; lineno++; break; case 'x': /* device control function */ devcntrl(fp); lineno++; break; default: error(FATAL, "unknown input character %o %c", c, c); done(); } /* End switch */ } /* End while */ t_page(-1); /* print the last page */ endtext(); } /* End of conv */ /*****************************************************************************/ void devcntrl( FILE *fp /* current input file */ ) { char str[4096], *buf, str1[4096]; int c, n, size; /* * * Called from conv() to process the rest of a device control function. There's * a whole family of them and they all start with the string "x ", which we've * already read. The "x X ..." commands are an extensible (and device dependent) * family that we use here for things like picture inclusion. Unrecognized device * control commands are ignored. * */ buf = malloc(size = 4096); sget(str, sizeof str, fp); /* get the control function name */ switch ( str[0] ) { /* only the first character counts */ case 'i': /* initialize */ t_init(); break; case 'T': /* device name */ sget(devname, sizeof devname, fp); getdevmap(); /* * This used to be "strcpy(devname, realdev);" but * it does not work when DESC is a text file because * the fonts are in a different directory. */ if (dev.afmfonts || (devname[0] == 'p' && devname[1] == 's')) realdev = devname; else n_strcpy(devname, realdev, sizeof(devname)); break; case 't': /* trailer */ t_trailer(); break; case 'p': /* pause -- can restart */ t_reset('p'); break; case 's': /* stop */ t_reset('s'); break; case 'r': /* resolution assumed when prepared */ fscanf(fp, "%d", &res); break; case 'f': /* load font in a position */ fscanf(fp, "%d", &n); sget(str, sizeof str, fp); fgets(buf, size, fp); /* in case there's a filename */ ungetc('\n', fp); /* fgets() goes too far */ str1[0] = '\0'; /* in case there's nothing to come in */ c = 0; sscanf(buf, "%s %d", str1, &c); loadfont(n, mapdevfont(str), str1, 0, c); break; /* these don't belong here... */ case 'H': /* char height */ fscanf(fp, "%d", &n); if (n != FRACTSIZE) t_charht(n, 0); else { float f; fscanf(fp, "%f", &f); t_charht(FRACTSIZE, f); } break; case 'S': /* slant */ fscanf(fp, "%d", &n); t_slant(n); break; case 'X': /* copy through - from troff */ do c = getc(fp); while (spacechar(c)); n = 0; if (c != EOF) do { if (n + 1 < sizeof str) str[n++] = c; c = getc(fp); } while (c != EOF && !spacechar(c) && c != ':'); str[n] = 0; if (c != ':') ungetc(c, fp); n = 0; for (;;) { fgets(&buf[n], size - n, fp); if ((c = getc(fp)) != '+') { ungetc(c, fp); break; } while (buf[n]) n++; if (size - n < 4096) buf = realloc(buf, size += 4096); lineno++; } if ( strcmp(str, "PI") == 0 || strcmp(str, "PictureInclusion") == 0 ) picture(buf); else if ( strcmp(str, "InlinePicture") == 0 ) inlinepic(fp, buf); else if ( strcmp(str, "SupplyFont") == 0 ) t_supply(buf); else if ( strcmp(str, "PaperSize") == 0 ) t_papersize(buf); else if ( strcmp(str, "TrimAt") == 0 ) t_cutat("Trim size", &trimat, buf); else if ( strcmp(str, "BleedAt") == 0 ) t_cutat("Bleed size", &bleedat, buf); else if ( strcmp(str, "CropAt") == 0 ) t_cutat("Crop size", &cropat, buf); else if ( strcmp(str, "Track") == 0 ) t_track(buf); else if ( strcmp(str, "PDFMark") == 0 ) t_pdfmark(buf); else if ( strcmp(str, "LC_CTYPE") == 0 ) t_locale(buf); else if ( strcmp(str, "Anchor") == 0 ) t_anchor(buf); else if ( strcmp(str, "Link") == 0 ) t_link(buf); else if ( strcmp(str, "SetLinkColor") == 0 ) t_linkcolor(buf); else if ( strcmp(str, "SetLinkBorder") == 0 ) t_linkborder(buf); else if ( strcmp(str, "SetBorderStyle") == 0 ) linkborderstyle = t_linkborderstyle(buf); else if ( strcmp(str, "SetUBorderStyle") == 0 ) ulinkborderstyle = t_linkborderstyle(buf); else if ( strcmp(str, "ULink") == 0 ) t_ulink(buf); else if ( strcmp(str, "SetULinkColor") == 0 ) t_ulinkcolor(buf); else if ( strcmp(str, "SetULinkBorder") == 0 ) t_ulinkborder(buf); else if ( strcmp(str, "HorScale") == 0 ) sethorscale(buf); else if ( strcmp(str, "BeginPath") == 0 ) beginpath(buf, FALSE); else if ( strcmp(str, "DrawPath") == 0 ) drawpath(buf, FALSE); else if ( strcmp(str, "BeginObject") == 0 ) beginpath(buf, TRUE); else if ( strcmp(str, "EndObject") == 0 ) drawpath(buf, TRUE); else if ( strcmp(str, "NewBaseline") == 0 ) newbaseline(buf); else if ( strcmp(str, "DrawText") == 0 ) drawtext(buf); else if ( strcmp(str, "SetText") == 0 ) settext(buf); else if ( strcmp(str, "SetColor") == 0 ) { newcolor(buf); setcolor(); } else if ( strcmp(str, "Sync") == 0 ) { if (tracked) tracked = -1; subfont = 0; t_sf(1); xymove(hpos, vpos); } else if ( strcmp(str, "PSSetup") == 0 ) { fprintf(gf, "%s", buf); } else if ( strcmp(str, "PS") == 0 || strcmp(str, "PostScript") == 0 ) { endtext(); /* xymove(hpos, vpos); ul90-22006 */ fprintf(tf, "%s", buf); } /* End else */ goto done; } /* End switch */ while ( (c = getc(fp)) != '\n' && c != EOF ) ; done: free(buf); } /* End of devcntrl */ /*****************************************************************************/ void fontinit(void) { char *descp; /* for reading the DESC file */ char *filebase; /* the whole thing goes here */ int i; /* loop index */ /* * * Reads *realdev's DESC file and uses what's there to initialize things like * the list of available point sizes. Old versions of the program used *devname's * DESC file to initialize nfonts, but that meant we needed to have *devname's * binary font files available for emulation. That restriction has been removed * and we now set nfonts using the "x font" commands in the input file, so by the * time we get here all we really need is *realdev. In fact devcntrl() reads the * device name from the "x T ..." command, but almost immediately replaces it with * string *realdev so we end up using *realdev's DESC file. Later on (in * t_font()) we mount all of *realdev's special fonts after the last legitimate * font position, just to be sure device emulation works reasonably well - there's * no guarantee *devname's special fonts match what's needed when *realdev's tables * are used. * */ snprintf(temp, sizeof temp, "%s/dev%s/FONTMAP", fontdir, devname); rdftmap(temp); snprintf(temp, sizeof temp, "%s/dev%s/DESC", fontdir, devname); if ( (descp = readdesc(temp)) == 0 ) error(FATAL, "can't open tables for %s", temp); memcpy(&dev, descp, sizeof dev); nfonts = 0; /* was dev.nfonts - now set in t_fp() */ nsizes = dev.nsizes; nchtab = dev.nchtab; unitwidth = dev.unitwidth; filebase = &descp[sizeof dev]; pstab = (int *) filebase; chtab = (short *)(pstab + nsizes + 1); chname = (char *) (chtab + nchtab); fsize = 3 * 255 + nchtab + 128 - 32 + sizeof(struct Font); fitab = calloc(NFONT+1, sizeof *fitab); fontab = calloc(NFONT+1, sizeof *fontab); codetab = calloc(NFONT+1, sizeof *codetab); kerntab = calloc(NFONT+1, sizeof *kerntab); fontbase = calloc(NFONT+1, sizeof *fontbase); for ( i = 1; i <= NFONT; i++ ) { /* so loadfont() knows nothing's there */ fontbase[i] = NULL; } /* End for */ if ( (downloaded = (char *) calloc(nchtab + 128, sizeof(char))) == NULL ) error(FATAL, "no memory"); } /* End of fontinit */ /*****************************************************************************/ void loadfont ( int n, /* load this font position */ char *s, /* with the file for this font */ char *s1, /* taken from here - possibly */ int forcespecial, /* this is definitively a special font */ int spec /* map specification */ ) { char *fpout = NULL; /* for reading *s file */ int fin; /* for reading *s.afm file */ int nw; /* number of width table entries */ char *p; char *path; size_t l; /* * * Loads font position n with the binary font file for *s provided it's not * already there. If *s1 is NULL or points to the empty string we read files from * directory *fontdir/dev*devname, otherwise directory *s1 is used. If the first * open fails we try to map font *s into one we expect will be available, and then * we try again. * */ if ( n < 0 || n > NFONT ) /* make sure it's a legal position */ error(FATAL, "illegal fp command %d %s", n, s); if ( fontbase[n] != NULL && strcmp(s, fontbase[n]->namefont) == 0 ) return; path = temp; if (s1 && strchr(s1, '/') != NULL) path = afmdecodepath(s1); else if (s1 && strstr(s1, ".afm") != NULL) snprintf(temp, sizeof temp, "%s/dev%s/%s", fontdir, devname, s1); else if (strchr(s, '/') != NULL) { path = afmdecodepath(s); if (spec == 0 && s1) spec = atoi(s1); } else if (strstr(s, ".afm") != NULL) { snprintf(temp, sizeof temp, "%s/dev%s/%s", fontdir, devname, s); if (spec == 0 && s1) spec = atoi(s1); } else snprintf(temp, sizeof temp, "%s/dev%s/%s.afm", fontdir, devname, s); if ( (fin = open(path, O_RDONLY)) >= 0 ) { struct afmtab *a; struct stat st; char *contents; int i; if ((p = strrchr(s, '/')) == NULL) p = s; else p++; if (p[0] == 'S' && (p[1] == '\0' || (digitchar(p[1]&0377) && p[2] == '\0') || p[2] == '.')) forcespecial = 1; for (i = 0; i < afmcount; i++) if (afmfonts[i] && strcmp(afmfonts[i]->path, path) == 0 && afmfonts[i]->spec == spec) { a = afmfonts[i]; close(fin); goto have; } if ((a = calloc(1, sizeof *a)) == NULL || fstat(fin, &st) < 0 || (contents = malloc(st.st_size+1)) == NULL || read(fin, contents, st.st_size) != st.st_size) { free(a); close(fin); goto fail; } close(fin); l = strlen(path) + 1; a->path = malloc(l); n_strcpy(a->path, path, l); if (path != temp) free(path); a->file = s; a->spec = spec; if (afmget(a, contents, st.st_size) < 0) { free(a); free(contents); goto fail; } free(contents); afmfonts = realloc(afmfonts, (afmcount+1) * sizeof *afmfonts); afmfonts[afmcount] = a; snprintf(a->Font.intname, sizeof a->Font.intname, "%d", dev.nfonts + ++afmcount); if (forcespecial) a->Font.specfont = 1; have: fontbase[n] = &a->Font; fontab[n] = a->fontab; codetab[n] = a->codetab; fitab[n] = a->fitab; t_fp(n, a->fontname, fontbase[n]->intname, a); goto done; } if (strchr(s, '/') != NULL) goto fail; if ( s1 == NULL || s1[0] == '\0' ) snprintf(temp, sizeof temp, "%s/dev%s/%s", fontdir, devname, s); else snprintf(temp, sizeof temp, "%s/%s", s1, s); if ( access(temp, R_OK) < 0 ) snprintf(temp, sizeof temp, "%s/dev%s/%s", fontdir, devname, mapfont(s)); if ((fpout = readfont(temp, &dev, 0)) == NULL) fail: error(FATAL, "can't open font table %s", temp); if ( fontbase[n] != NULL ) /* something's already there */ free(fontbase[n]); /* so release the memory first */ fontbase[n] = (struct Font *)fpout; p = (char *) fontbase[n] + sizeof(struct Font); nw = fontbase[n]->nwfont & BMASK; makefont(n, p, NULL, p + 2 * nw, p + 3 * nw, nw); t_fp(n, fontbase[n]->namefont, fontbase[n]->intname, NULL); done: if ( smnt == 0 && (fontbase[n]->specfont == 1 || forcespecial) ) smnt = n; if (fontbase[n]->specfont == 1 || forcespecial) gotregular = TRUE; if ( debug == ON ) fontprint(n); } /* End of loadfont */ /*****************************************************************************/ void loadspecial(void) { char *p; /* for next binary font file */ int nw; /* width entries in next font */ int i; /* loop index */ /* * * Loads all the special fonts after the last legal font position. Mostly used * for device emulation, but we'll do it no matter what. Needed because there's * no consistency in special fonts across different devices, and relying on having * them mounted in the input file doesn't guarantee the whole collection will be * there. The special fonts are determined and mounted using the copy of the * DESC file that's been read into memory. Initially had this stuff at the * end of fontinit(), but we now don't know nfonts until much later. * */ if ( gotregular == FALSE ) loadfont(++nfonts, ((struct Font *)(&chname[dev.lchname]))->namefont, NULL, 0, 0); if ( gotspecial == FALSE ) for ( i = 1, p = chname + dev.lchname; i <= dev.nfonts; i++ ) { nw = *p & BMASK; if ( ((struct Font *) p)->specfont == 1 ) loadfont(++nfonts, ((struct Font *)p)->namefont, NULL, 1, 0); p += 3 * nw + dev.nchtab + 128 - 32 + sizeof(struct Font); } /* End for */ gotregular = TRUE; gotspecial = TRUE; } /* End of loadspecial */ /*****************************************************************************/ char *defaultFonts[] = { "R", "I", "B", "BI", "CW", "H", "HB", "HX", "S1", "S", NULL }; void loaddefault(void) { int i; for (i = 0; defaultFonts[i] != NULL ; i++) loadfont(++nfonts, defaultFonts[i], NULL, defaultFonts[i][0] == 'S', 0); } void fontprint ( int i /* font's index in fontbase[] */ ) { int j, n; char *p; /* * * Debugging routine that dumps data about the font mounted in position i. * */ fprintf(tf, "font %d:\n", i); p = (char *) fontbase[i]; n = fontbase[i]->nwfont & BMASK; fprintf(tf, "base=0%lo, nchars=%d, spec=%d, name=%s, widtab=0%lo, fitab=0%lo\n", (long)p, n, fontbase[i]->specfont, fontbase[i]->namefont, (long)fontab[i], (long)fitab[i]); fprintf(tf, "widths:\n"); for ( j = 0; j <= n; j++ ) { fprintf(tf, " %2d", fontab[i][j]); if ( j % 20 == 19 ) putc('\n', tf); } /* End for */ fprintf(tf, "\ncodetab:\n"); for ( j = 0; j <= n; j++ ) { fprintf(tf, " %2d", codetab[i][j]); if ( j % 20 == 19 ) putc('\n', tf); } /* End for */ fprintf(tf, "\nfitab:\n"); for ( j = 0; j <= dev.nchtab + 128-32; j++ ) { fprintf(tf, " %2d", fitab[i][j]); if ( j % 20 == 19 ) putc('\n', tf); } /* End for */ putc('\n', tf); } /* End of fontprint */ /*****************************************************************************/ 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. * */ name = mapft(name); for ( i = 0; fontmap[i].name != NULL; i++ ) if ( strcmp(name, fontmap[i].name) == 0 ) return(fontmap[i].use); switch ( *++name ) { case 'I': return("I"); case 'B': return("B"); case 'X': return("BI"); default: return("R"); } /* End switch */ } /* End of mapfont */ /*****************************************************************************/ void getdevmap(void) { FILE *fp; /* for reading the device fontmap file */ int i = 0; /* number of mapping pairs we've read */ int c; /* for skipping lines */ /* * * Looks for the device font mapping file *fontdir/dev*realdev/fontmaps/devname. * The file, if it exists, should be an ASCII file containing pairs of one or two * character font names per line. The first name is the font troff will be asking * for and the second is the one we'll use. Comments are lines that begin with * a '#' as the first non-white space character on a line. The devfontmap list * ends with a member that has the empty string in the name field. * */ snprintf(temp, sizeof temp, "%s/dev%s/fontmaps/%s", fontdir, realdev, devname); if ( devfontmap == NULL && (fp = fopen(temp, "r")) != NULL ) { devfontmap = (Devfontmap *) malloc(10 * sizeof(Devfontmap)); while ( sget(temp, sizeof temp, fp) == 1 ) { if ( temp[0] != '#' && strlen(temp) < 3 ) if ( sget(&temp[3], sizeof temp - 3, fp) == 1 && strlen(&temp[3]) < 3 ) { n_strcpy((devfontmap + i)->name, temp, sizeof(devfontmap->name)); n_strcpy((devfontmap + i)->use, &temp[3], sizeof(devfontmap->use)); if ( ++i % 10 == 0 ) devfontmap = (Devfontmap *) realloc(devfontmap, (i + 10) * sizeof(Devfontmap)); } /* End if */ while ( (c = getc(fp)) != '\n' && c != EOF ) ; } /* End while */ (devfontmap + i)->name[0] = '\0'; /* end the list we just read */ fclose(fp); } /* End if */ } /* End of getdevmap */ /*****************************************************************************/ char * mapdevfont(char *str) { int i; /* * * Called immediately before loadfont() after an 'x font' command is recognized. * Takes the font name that troff asked for, looks it up in the devfontmap list, * and returns the mapped name to the caller. No mapping is done if the devfontmap * list is empty or font *str isn't found in the list. * */ if ( devfontmap != NULL ) for ( i = 0; (devfontmap + i)->name[0] != '\0'; i++ ) if ( strcmp((devfontmap + i)->name, str) == 0 ) return((devfontmap + i)->use); return(str); } /* End of mapdevfont */ /*****************************************************************************/ void reset(void) { /* * * Resets the variables that keep track of the printer's current position, font, * and size. Typically used after a restore/save pair (eg. when we finish with a * page) to make sure we force the printer back into sync (in terms of the font * and current point) before text is printed. * */ lastx = -(slop + 1); savey = lasty = -1; lastfont = lastsubfont = lastsize = -1; if (tracked) tracked = -1; } /* End of reset */ /*****************************************************************************/ void resetpos(void) { /* * * Resets the variables that keep track of the printer's current position. Used * when there's a chance we've lost track of the printer's current position or * done something that may have wiped it out, and we want to force dpost to set * the printer's position before printing text or whatever. For example stroke or * fill implicitly do a newpath, and that wipes out the current point, unless the * calls were bracketed by a gsave/grestore pair. * */ lastx = -(slop + 1); savey = lasty = -1; } /* End of resetpos */ /*****************************************************************************/ void t_init(void) { static int initialized = FALSE; /* only do most things once */ /* * * Called from devcntrl() after an "x init" command is read. Things only work if * we've already seen the "x res" command, and much of the stuff, including the * call to setup, should only be done once. Restricting everything to one call of * setup (ie. the one in the prologue) means all the input files must have been * formatted for the same device. * */ endtext(); /* moved - for cat'ed troff files */ if ( initialized == FALSE ) { /* only do this stuff once per job */ fontinit(); gotspecial = FALSE; gotregular = FALSE; widthfac = (float) res /dev.res; if (dev.afmfonts) { if (Sflag == 0) pointslop = 0; } if (eflag == 0) realencoding = encoding = dev.encoding; if (encoding == 5) { LanguageLevel = MAX(LanguageLevel, 2); Binary++; } slop = pointslop * res / POINTS + .5; rvslop = res * .025; setup(); initialized = TRUE; } /* End if */ hpos = vpos = 0; /* upper left corner */ setsize(t_size(10), 0); /* start somewhere */ reset(); /* force position and font stuff - later */ } /* End of t_init */ /*****************************************************************************/ void needresource(const char *s, ...) { va_list ap; if (nfcount++ == 0) fprintf(nf, "%%%%DocumentNeededResources: "); else fprintf(nf, "%%%%+ "); va_start(ap, s); vfprintf(nf, s, ap); va_end(ap); putc('\n', nf); } static struct supplylist { struct supplylist *next; char *font; char *file; char *type; int done; } *supplylist; void t_supply(char *font) /* supply a font */ { struct supplylist *sp; char *np, *file, *type = NULL, c; while (*font == ' ' || *font == '\t') font++; for (np = font; *np && *np != ' ' && *np != '\t' && *np != '\n'; np++); if (*np == '\0' || *np == '\n') return; *np = '\0'; file = &np[1]; while (*file == ' ' || *file == '\t') file++; for (np = file; *np && *np != ' ' && *np != '\t' && *np != '\n'; np++); c = *np; *np = '\0'; if (c != '\0' && c != '\n') { type = &np[1]; while (*type == ' ' || *type == '\t') type++; for (np = type; *np && *np != ' ' && *np != '\t' && *np != '\n'; np++); *np = '\0'; } for (sp = supplylist; sp; sp = sp->next) if (strcmp(sp->font, font) == 0) return; sp = calloc(1, sizeof *sp); sp->font = strdup(font); sp->file = afmdecodepath(file); sp->type = type && *type ? strdup(type) : NULL; sp->next = supplylist; supplylist = sp; } static unsigned long ple32(const char *cp) { return (unsigned long)(cp[0]&0377) + ((unsigned long)(cp[1]&0377) << 8) + ((unsigned long)(cp[2]&0377) << 16) + ((unsigned long)(cp[3]&0377) << 24); } static const char ps_adobe_font_[] = "%!PS-AdobeFont-"; static const char ps_truetypefont[] = "%!PS-TrueTypeFont"; static const char hex[] = "0123456789abcdef"; static void supplypfb(char *font, char *path, FILE *fp) { char buf[30]; long length; int i, c = EOF, n, type = 0, lastc = EOF; if (fread(buf, 1, 6, fp) != 6) error(FATAL, "no data in %s", path); if ((buf[0]&0377) != 0200 || (type = buf[1]) != 1) error(FATAL, "invalid header in %s", path); length = ple32(&buf[2]); n = 0; while (ps_adobe_font_[n] && --length > 0 && (c = getc(fp)) != EOF) { if (c != ps_adobe_font_[n++]) error(FATAL, "file %s does not start with \"%s\"", path, ps_adobe_font_); } while (--length > 0 && (c = getc(fp)) != EOF && c != '\r' && c != '\n'); if (c != '\n') { if ((c = getc(fp)) != '\n') ungetc(c, fp); else length--; } if (sfcount++ == 0) fprintf(sf, "%%%%DocumentSuppliedResources: font %s\n", font); else fprintf(sf, "%%%%+ font %s\n", font); fprintf(rf, "%%%%BeginResource: font %s\n", font); for (;;) { switch (type) { case 1: while (length > 0 && (c = getc(fp)) != EOF) { length--; switch (c) { case '\r': if ((c = getc(fp)) != '\n') ungetc(c, fp); else length--; putc('\n', rf); lastc = '\n'; break; case 0: continue; default: putc(c, rf); lastc = c; } } if (c == EOF) error(FATAL, "short text data in %s", path); break; case 2: while (length) { n = length > sizeof buf ? sizeof buf : length; if (fread(buf, 1, n, fp) != n) error(FATAL, "short binary data in %s", path); for (i = 0; i < n; i++) { putc(hex[(buf[i]&0360)>>4], rf); putc(hex[buf[i]&017], rf); } putc('\n', rf); lastc = '\n'; length -= n; } break; case 3: if (lastc != '\n') putc('\n', rf); fprintf(rf, "%%%%EndResource\n"); fclose(fp); return; default: error(FATAL, "invalid header type %d in %s", path, type); } if ((n = fread(buf, 1, 6, fp)) != 6 && (buf[1] != 3 || n < 2)) error(FATAL, "missing header in %s", path); if ((buf[0]&0377) != 0200) error(FATAL, "invalid header in %s", path); if ((type = buf[1]) != 3) length = ple32(&buf[2]); } } static void supplyotf(char *font, char *path, FILE *fp) { static int cffcount; struct stat st; char *contents; size_t size, offset, length; int i; int fsType; const char StartData[] = " StartData "; if (fstat(fileno(fp), &st) < 0) error(FATAL, "cannot stat %s", path); size = st.st_size; contents = malloc(size); if (fread(contents, 1, size, fp) != size) error(FATAL, "cannot read %s", path); fclose(fp); if ((fsType = otfcff(path, contents, size, &offset, &length)) < 0) { free(contents); return; } /* * Adobe Technical Note #5176, "The Compact Font Format * Specification", Version 1.0, 12/4/2003, p. 53 proposes * a weird syntax for CFF DSC comments ("ProcSet" etc.); * Adobe Distiller 7 complains about it with DSC warnings * enabled. What follows is an attempt to fix this. */ if (cffcount++ == 0) { fprintf(rf, "%%%%IncludeResource: procset FontSetInit 0 0\n"); needresource("procset FontSetInit 0 0"); } if (sfcount++ == 0) fprintf(sf, "%%%%DocumentSuppliedResources: font %s\n", font); else fprintf(sf, "%%%%+ font %s\n", font); fprintf(rf, "%%%%BeginResource: font %s\n", font); fprintf(rf, "/FontSetInit /ProcSet findresource begin\n"); if (encoding == 5) { fprintf(rf, "%%%%BeginData: %ld Binary Bytes\n", (long)(length + 13 + strlen(font) + 12)); fprintf(rf, "/%s %12ld StartData ", font, (long)length); fwrite(&contents[offset], 1, length, rf); fprintf(rf, "\n%%%%EndData\n"); } else { fprintf(rf, "/%s %ld ", font, (long)length); fprintf(rf, "currentfile /ASCIIHexDecode filter cvx exec\n"); for (i = 0; StartData[i]; i++) { putc(hex[(StartData[i]&0360)>>4], rf); putc(hex[StartData[i]&017], rf); } putc('\n', rf); for (i = offset; i < offset+length; i++) { putc(hex[(contents[i]&0360)>>4], rf); putc(hex[contents[i]&017], rf); if (i > offset && (i - offset + 1) % 34 == 0) putc('\n', rf); } fprintf(rf, ">\n"); } fprintf(rf, "%%%%EndResource\n"); free(contents); LanguageLevel = MAX(LanguageLevel, 3); } static void supplyttf(char *font, char *path, FILE *fp) { struct stat st; char *contents; size_t size; if (fstat(fileno(fp), &st) < 0) error(FATAL, "cannot stat %s", path); size = st.st_size; contents = malloc(size); if (fread(contents, 1, size, fp) != size) error(FATAL, "cannot read %s", path); fclose(fp); if (sfcount++ == 0) fprintf(sf, "%%%%DocumentSuppliedResources: font %s\n", font); else fprintf(sf, "%%%%+ font %s\n", font); fprintf(rf, "%%%%BeginResource: font %s\n", font); otft42(font, path, contents, size, rf); fprintf(rf, "%%%%EndResource\n"); free(contents); LanguageLevel = MAX(LanguageLevel, 2); } static void supply1(char *font, char *file, char *type) { FILE *fp; char line[4096], c; if (strchr(file, '/') == 0) { snprintf(temp, sizeof temp, "%s/dev%s/%s.%s", fontdir, devname, file, type); file = temp; } if ((fp = fopen(file, "r")) == NULL) error(FATAL, "can't open %s", file); if (type == NULL) { c = getc(fp); ungetc(c, fp); type = c == '\200' ? "pfb" : c == 'O' ? "otf" : c == 0 || c == 't' ? "ttf" : "anything"; } if (strcmp(type, "pfb") == 0) { supplypfb(font, file, fp); return; } if (strcmp(type, "otf") == 0) { supplyotf(font, file, fp); return; } if (strcmp(type, "ttf") == 0) { supplyttf(font, file, fp); return; } if (fgets(line, sizeof line, fp) == NULL) error(FATAL, "missing data in %s", file); if (strncmp(line, ps_adobe_font_, strlen(ps_adobe_font_)) && strncmp(line, ps_truetypefont, strlen(ps_truetypefont))) error(FATAL, "file %s does not start with \"%s\" or \"%s\"", file, ps_adobe_font_, ps_truetypefont); if (sfcount++ == 0) fprintf(sf, "%%%%DocumentSuppliedResources: font %s\n", font); else fprintf(sf, "%%%%+ font %s\n", font); fprintf(rf, "%%%%BeginResource: font %s\n", font); while (fgets(line, sizeof line, fp) != NULL) fputs(line, rf); fclose(fp); fprintf(rf, "%%%%EndResource\n"); } static void t_dosupply(const char *font) { struct supplylist *sp; for (sp = supplylist; sp; sp = sp->next) if (strcmp(sp->font, font) == 0) { if (sp->done == 0) { supply1(sp->font, sp->file, sp->type); sp->done = 1; } return; } needresource("font %s", font); } /*****************************************************************************/ static void boxcmp(const char *name, struct box *bp, int a, int b, int c, int d) { if (bp->flag && (a != bp->val[0] || b != bp->val[1] || c != bp->val[2] || d != bp->val[3])) error(NON_FATAL, "%s has changed, using new values", name); } static void t_papersize(char *buf) { int x, y, setmedia = 0; if (sscanf(buf, "%d %d %d", &x, &y, &setmedia) < 2) return; boxcmp("Media size", &mediasize, 0, 0, x, y); mediasize.val[2] = x; mediasize.val[3] = y; mediasize.flag |= 1; if (setmedia) mediasize.flag |= 2; pagelength = y * 72.0 / res; } static void t_cutat(const char *name, struct box *bp, char *buf) { int c[4], i; if (sscanf(buf, "%d %d %d %d", &c[0], &c[1], &c[2], &c[3]) < 4) return; boxcmp(name, bp, c[0], c[1], c[2], c[3]); for (i = 0; i < 4; i++) bp->val[i] = c[i]; bp->flag |= 1; } /*****************************************************************************/ void t_page ( int pg /* troff's current page number */ ) { static int lastpg = 0; /* last one we started - for ENDPAGE */ /* * * Called whenever we've finished the last page and want to get ready for the * next one. Also used at the end of each input file, so we have to be careful * about what's done. The first time through (up to the redirect(pg) call) output * goes to /dev/null because of the redirect(-1) call made in conv(). * * Adobe now recommends that the showpage operator occur after the page level * restore so it can be easily redefined to have side-effects in the printer's VM. * Although it seems reasonable I haven't implemented it, because it makes other * things, like selectively setting manual feed or choosing an alternate paper * tray, clumsy - at least on a per page basis. * */ if ( tf == stdout ) /* count the last page */ printed++; endtext(); /* print the last line? */ fprintf(tf, "_marks\n"); fprintf(tf, "cleartomark\n"); fprintf(tf, "showpage\n"); fprintf(tf, "restore\n"); fprintf(tf, "%s %d %d\n", ENDPAGE, lastpg, printed); redirect(pg); fprintf(tf, "%s %d %d\n", PAGE, pg, printed+1); fprintf(tf, "save\n"); fprintf(tf, "mark\n"); writerequest(printed+1, tf); fprintf(tf, "%d pagesetup\n", printed+1); setcolor(); lastpg = pg; /* for the next ENDPAGE comment */ hpos = vpos = 0; /* get ready for the next page */ reset(); /* force position and font stuff - later */ seenpage = TRUE; } /* End of t_page */ /*****************************************************************************/ void t_newline(void) { /* * * Just finished the last line. All we do is set the horizontal position to 0, * although even that probably isn't necessary. * */ hpos = 0; } /* End of t_newline */ /*****************************************************************************/ int t_size ( int n /* convert this to an internal size */ ) { int i; /* loop index */ /* * * Converts a point size into an internal size that can be used as an index into * pstab[]. The internal size is one plus the index of the least upper bound of * n in pstab[], or nsizes if n is larger than all the listed sizes. * */ if ( n <= pstab[0] ) return(1); else if (n >= pstab[nsizes-1]) return(nsizes); for ( i = 0; n > pstab[i]; i++ ) ; return(i+1); } /* End of t_size */ /*****************************************************************************/ void setsize ( int n, float f /* new internal size */ ) { /* * * Now using internal size n, where pstab[n-1] is the best available approximation * to the size troff asked for. * */ size = n; fractsize = f; lasthorscale = horscale = 1.0; } /* End of setsize */ /*****************************************************************************/ void t_fp ( int n, /* this position */ char *s, /* now has this font mounted */ char *si, /* its internal number */ void *a ) { /* * * Updates nfonts and the array that keeps track of the mounted fonts. Called from * loadfont() after an "x font pos font" command is read, and if pos is larger than * the current value assigned to nfonts we set gotspecial to FALSE to make sure * t_font() loads all the special fonts after the last legitimate font position. * */ fontname[n].name = s; fontname[n].number = atoi(si); fontname[n].afm = a; if ( n == lastfont ) /* force a call to t_sf() */ lastfont = lastsubfont = -1; if ( n > nfonts ) { /* got more positions */ nfonts = n; gotspecial = FALSE; gotregular = FALSE; } /* End if */ } /* End of t_fp */ /*****************************************************************************/ int t_font ( char *s /* use font in this position next */ ) { int n; /* * * Converts the string *s into an integer and checks to make sure it's a legal * font position. Also arranges to mount all the special fonts after the last * legitimate font (by calling loadspecial()), provided it hasn't already been * done. * */ n = atoi(s); if ( seenpage == TRUE ) { if ( n < 0 || n > nfonts ) error(FATAL, "illegal font position %d", n); if ( gotspecial == FALSE || gotregular == FALSE ) loadspecial(); } /* End if */ if (tracked) tracked = -1; track = 0; return(n); } /* End of t_font */ /*****************************************************************************/ static void sethorscale(char *buf) { horscale = atof(buf); } /*****************************************************************************/ static void t_track(char *buf) { int t; /* * Handling of track kerning. troff provides this parameter as a hint * only. dpost can use it in combination with the PostScript "ashow" * operator. When the variable "track" is not zero, the printer is * advised to perform tracking by the given amount. This relieves us * of the need to adjust the character position explicitly after each * character and thus greatly reduces the size of the output. * * Currently this is done in encodings 0, 4, and 5 only. */ if (sscanf(buf, "%d", &t) != 1) t = 0; if (t != lasttrack) { tracked = -1; } else if (t && tracked != -1) tracked = 1; track = t; } static void t_strack(void) { endtext(); fprintf(tf, "%d T\n", track); if (tf == stdout) { tracked = track != 0; lasttrack = track; } } /*****************************************************************************/ void setfont ( int n /* use the font mounted here */ ) { /* * * troff wants to use the font that's been mounted in position n. All we do here * is update the variable that keeps track of the current position. PostScript * font changes are handled in t_sf(), and are only generated right before we're * ready to print or draw something. * */ if ( n < 0 || n > NFONT ) error(FATAL, "illegal font %d", n); if ( fontname[n].name == NULL && fontname[n].number == 0) loaddefault(); if ( fontname[n].name == NULL && fontname[n].number == 0) error(FATAL, "font %d not loaded: check 'dpost' input for 'x font %d XXX' before 'f%d'", n, n, n); font = n; subfont = 0; lasthorscale = horscale = 1.0; } /* End of setfont */ /*****************************************************************************/ static void endvec(struct afmtab *a, int n) { fprintf(gf, "] def\n"); fprintf(gf, "\ /%s findfont\n\ dup length dict begin\n\ {1 index /FID ne {def} {pop pop} ifelse} forall\n\ /Encoding Encoding-@%s@%d def\n\ currentdict\n\ end\n", a->fontname, a->Font.intname, n); if (a->spec & SPEC_S) { fprintf(gf, "/%s-tmp-@%s", a->fontname, a->Font.intname); if (n) fprintf(gf, "@%d", n); fprintf(gf, " exch definefont pop\n"); fprintf(gf, "_Sdefsadj\n"); fprintf(gf, "/%s-tmp-@%s", a->fontname, a->Font.intname); if (n) fprintf(gf, "@%d", n); fprintf(gf, " /%s-@%s", a->fontname, a->Font.intname); if (n) fprintf(gf, "@%d", n); fprintf(gf, " Sdefs cf\n"); fprintf(gf, "/%s-tmp-@%s", a->fontname, a->Font.intname); if (n) fprintf(gf, "@%d", n); fprintf(gf, " undefinefont\n"); } else if (a->spec & SPEC_S1) { fprintf(gf, "/%s-tmp-@%s", a->fontname, a->Font.intname); if (n) fprintf(gf, "@%d", n); fprintf(gf, " exch definefont pop\n"); fprintf(gf, "/%s-tmp-@%s", a->fontname, a->Font.intname); if (n) fprintf(gf, "@%d", n); fprintf(gf, " /%s-@%s", a->fontname, a->Font.intname); if (n) fprintf(gf, "@%d", n); fprintf(gf, " S1defs cf\n"); fprintf(gf, "/%s-tmp-@%s", a->fontname, a->Font.intname); if (n) fprintf(gf, "@%d", n); fprintf(gf, " undefinefont\n"); } else if (n) fprintf(gf, "/%s-@%s@%d exch definefont pop\n", a->fontname, a->Font.intname, n); else fprintf(gf, "/%s-@%s exch definefont pop\n", a->fontname, a->Font.intname); fprintf(gf, "/@%s", a->Font.intname); if (n) fprintf(gf, "@%d", n); fprintf(gf, " /%s-@%s", a->fontname, a->Font.intname); if (n) fprintf(gf, "@%d", n); fprintf(gf, " def\n"); fprintf(gf, "/&%s", a->Font.intname); if (n) fprintf(gf, "@%d", n); fprintf(gf, " {@%s", a->Font.intname); if (n) fprintf(gf, "@%d", n); fprintf(gf, " F} bind def\n"); } static void printencsep(int *colp) { if (*colp >= 60) { putc('\n', gf); *colp = 0; } else { putc(' ', gf); (*colp)++; } } static int * printencvector(struct afmtab *a) { int i, j, k, n, col = 0, s; int *encmap = NULL; fprintf(gf, "/Encoding-@%s@0 [\n", a->Font.intname); col = 0; /* * First, write excess entries into the positions from 1 to 31 * for later squeezing of characters >= 0400. */ s = 128 - 32; encmap = calloc(256 + nchtab + a->nchars, sizeof *encmap); col += fprintf(gf, "/.notdef"); printencsep(&col); for (j = 1; j < 32; j++) { while (s < a->nchars + 128 - 32 + nchtab && ((k = a->fitab[s]) == 0 || a->nametab[k] == NULL)) s++; if (s < a->nchars + 128 - 32 + nchtab && (k = a->fitab[s]) != 0 && k < a->nchars && a->nametab[k] != NULL) { encmap[s - 128 + 32] = j; col += fprintenc(gf, a->nametab[k]); printencsep(&col); s++; } else { col += fprintf(gf, "/.notdef"); printencsep(&col); } } col += fprintf(gf, "/space"); printencsep(&col); for (i = 1; i < a->nchars + 128 - 32 + nchtab && i < 256 - 32; i++) { if (i < 128 - 32 && (k = a->fitab[i]) != 0 && k < a->nchars && a->nametab[k] != NULL) { col += fprintenc(gf, a->nametab[k]); printencsep(&col); } else { while (s < a->nchars + 128 - 32 + nchtab && ((k = a->fitab[s]) == 0 || a->nametab[k] == NULL)) s++; if (s < a->nchars + 128 - 32 + nchtab && (k = a->fitab[s]) != 0 && k < a->nchars && a->nametab[k] != NULL) { encmap[s - 128 + 32] = i + 32; col += fprintenc(gf, a->nametab[k]); printencsep(&col); s++; } else { col += fprintf(gf, "/.notdef"); printencsep(&col); } } } endvec(a, 0); n = 1; while (s < a->nchars + 128 - 32 + nchtab) { fprintf(gf, "/Encoding-@%s@%d [\n", a->Font.intname, n); col = 0; for (i = 0; i < 256; i++) { while (s < a->nchars + 128 - 32 + nchtab && ((k = a->fitab[s]) == 0 || a->nametab[k] == NULL)) s++; if (s < a->nchars + 128 - 32 + nchtab && (k = a->fitab[s]) != 0 && k < a->nchars && a->nametab[k] != NULL) { encmap[s - 128 + 32] = i | n << 8; col += fprintenc(gf, a->nametab[k]); printencsep(&col); s++; } else { col += fprintf(gf, "/.notdef"); printencsep(&col); } } endvec(a, n++); } return encmap; } /*****************************************************************************/ void t_sf(int forceflush) { int fnum; /* internal font number */ int cmd; /* command to execute */ /* * * Called whenever we need to use a new font or size. Only done right before we * print a character. The seenfonts[] array keeps track of the fonts we've used. * Helps manage host resident fonts and the DOCUMENTFONTS comment that's put out * at the end of the job. The array is indexed by internal number. Only works for * fonts that have internal numbers less than or equal to MAXINTERNAL. * */ if ( fontname[font].name == NULL ) return; endtext(); if ( (fnum = fontname[font].number) > MAXINTERNAL || fnum < 0 ) fnum = 0; if ( fnum > 0 && seenfonts[fnum] == 0 && hostfontdir != NULL ) { snprintf(temp, sizeof temp, "%s/%s", hostfontdir, fontname[font].name); if ( access(temp, 04) == 0 ) doglobal(temp); } /* End if */ cmd = 'f'; if (forceflush == 0) { if (font == lastfont && subfont == lastsubfont) cmd = 's'; else if (size == lastsize && fractsize == lastfractsize) cmd = 'F'; } if (horscale != 1.0) cmd = 'h'; if ( tf == stdout ) { lastfont = font; lastsubfont = subfont; lastsize = size; lastfractsize = fractsize; lasthorscale = horscale; if ( seenfonts[fnum] == 0 ) { documentfonts(); } if (fontname[font].afm && fontname[font].afm->encmap == NULL) fontname[font].afm->encmap = printencvector(fontname[font].afm); seenfonts[fnum] = 1; } /* End if */ if (cmd == 'f' || cmd == 's' || cmd == 'h') { if (size != FRACTSIZE) fprintf(tf, "%d ", pstab[size-1]); else fprintf(tf, "%g ", (double)fractsize); } if (fontname[font].afm && cmd == 'F') { if (subfont) fprintf(tf, "&%s@%d\n", fontname[font].afm->Font.intname, subfont); else fprintf(tf, "&%s\n", fontname[font].afm->Font.intname); cmd = 0; } else if (cmd == 'f' || cmd == 'F' || cmd == 'h') { if (fontname[font].afm && subfont) fprintf(tf, "@%s@%d ", fontname[font].afm->Font.intname, subfont); else if (fontname[font].afm) fprintf(tf, "@%s ", fontname[font].afm->Font.intname); else fprintf(tf, "%s ", fontname[font].name); } if (cmd == 'h') fprintf(tf, "%g ", horscale); if (cmd) fprintf(tf, "%c\n", cmd); if ( fontname[font].fontheight != 0 || fontname[font].fontslant != 0 ) { if (size != FRACTSIZE) fprintf(tf, "%d %g changefont\n", fontname[font].fontslant, (fontname[font].fontheight != 0) ? (double)fontname[font].fontheight : pstab[size-1]); else fprintf(tf, "%d %g changefont\n", fontname[font].fontslant, (fontname[font].fontheight != 0) ? (double)fontname[font].fontheight : (double)fractsize); } if (tracked < 0 || tracked > 0 && forceflush) t_strack(); } /* End of t_sf */ /*****************************************************************************/ void t_charht ( int n, float f /* use this as the character height */ ) { /* * * Remembers the requested height, from 'x H n'. Forces a call to t_sf(), which * is where the real work is done, by setting lastfont to -1. * */ if (n == FRACTSIZE) fontname[font].fontheight = f; else fontname[font].fontheight = (n == pstab[size-1]) ? 0 : n; lastfont = lastsubfont = -1; } /* End of t_charht */ /*****************************************************************************/ void t_slant ( int n /* slant characters this many degrees */ ) { /* * * Remembers the requested slant, from 'x X n'. Forces a call to t_sf(), which * is where the real work is done, by setting lastfont to -1. * */ fontname[font].fontslant = n; lastfont = lastsubfont = -1; } /* End of t_slant */ /*****************************************************************************/ void t_reset ( int c /* pause or restart */ ) { /* * * Found an "x stop" or "x pause" command. Although nothing's done here we could * add code to reset everything so dpost could handle multiple files formatted for * different devices. * */ } /* End of t_reset */ /*****************************************************************************/ void t_trailer(void) { /* * * Called after we find an "x trailer" in the input file. Forcing out the last * page is done at the end of conv(), but probably belongs here. * */ endtext(); } /* End of t_trailer */ /*****************************************************************************/ void hgoto ( int n /* new horizontal position */ ) { /* * * Want to be at this absolute horizontal position next. Actual motion commands * are generated in oput(), charlib(), and the drawing routines. * */ hpos = n; } /* End of hgoto */ /*****************************************************************************/ void hmot ( int n /* move this far horizontally */ ) { /* * * Handles relative horizontal motion. troff's current positon, as recorded in * in hpos, is changed by n units. Usually called right before we're supposed to * print a character. * */ hpos += n; } /* End of hmot */ /*****************************************************************************/ void vgoto ( int n /* new vertical position */ ) { /* * * Moves vertically in troff's coordinate system to absolute position n. * */ vpos = n; } /* End of vgoto */ /*****************************************************************************/ void vmot ( int n /* move this far vertically */ ) { /* * * Handles relative vertical motion of n units in troff's coordinate system. * */ vpos += n; } /* End of vmot */ /*****************************************************************************/ void xymove ( int x, int y /* this is where we want to be */ ) { /* * * Makes sure the post-processor and printer agree about the current position. * */ hgoto(x); vgoto(y); fprintf(tf, "%d %d m\n", hpos, vpos); lastx = hpos; savey = lasty = vpos; } /* End of xymove */ /*****************************************************************************/ void put1s ( register char *s /* find and print this character */ ) { static int i = 0; /* last one we found - usually */ /* * * *s points to the start of a two character string that represents one of troff's * special characters. To print it we first look for *s in the chname[] array using * chtab[i] to find the string representing character i in chname[]. If the lookup * is successful we add 128 to i and ask put1() to finish printing the character. * We remember the index where the last character was found because requests to * print a special character often come in bunches (eg. drawing lines with \(ru). * */ if (s[0] == 'P' && s[1] == 'S' && s[2] != 0) { /* PostScript name */ int m; struct namecache *np; struct afmtab *a; if ((a = fontname[font].afm) != NULL && (np = afmnamelook(a, &s[2])) != NULL && ((m = np->fival[0]) != NOCODE || (m = np->fival[1]) != NOCODE)) { put1(m+32); return; } } if ( strcmp(s, &chname[chtab[i]]) != 0 ) for ( i = 0; i < nchtab; i++ ) if ( strcmp(&chname[chtab[i]], s) == 0 ) break; if ( i < nchtab ) put1(i + 128); else i = 0; } /* End of put1s */ /*****************************************************************************/ void put1 ( register int c /* want to print this character */ ) { register int i = 0; /* character code from fitab */ register int j; /* number of fonts we've checked so far */ register int k; /* font we're currently looking at */ int *pw = NULL; /* font widthtab and */ unsigned short *p = NULL; /* and codetab where c was found */ int code; /* code used to get c printed */ int ofont; /* font when we started */ /* * * Arranges to have character c printed. If c < 128 it's a simple ASCII character, * otherwise it's a special character. Things done here have to agree with the way * the font tables were built by makedev, and work as follows. First we subtract * 32 from c because the tables don't record the non-graphic ASCII characters. * If fitab[k][c] isn't zero the character is on font k and the value is an index * that can be used to recover width and character code data from the other two * tables. If fitab[k][c] is zero the character isn't defined on font k and we * check the next font, which is found as follows. The current font is the first * one we check, and it's followed by a circular search of all the remaining fonts * that starts with the first special font and skips font position 0. If character * c is found somewhere besides the current font we change to that font and use * fitab[k][c] to locate missing data in the other two tables. The width of the * character can be found at fontab[k][c] while codetab[k][c] is whatever we * need to tell the printer to have character c printed. lastc records the real * name of the character because it's lost by the time oput() gets called but * charlib() may need it. * * Took all the debugging stuff out because at least this part of the program is * reasonably solid. * */ lastc = c; /* charlib() needs the name not the code */ if ( (c -= 32) <= 0 ) /* probably never happens */ return; k = ofont = font; if ( fitab[k] && (i = fitab[k][c]) != 0 ) { /* it's on this font */ p = codetab[font]; pw = fontab[font]; } else if ( smnt > 0 ) { /* on special (we hope) */ for ( k=smnt, j=0; j <= nfonts; j++, k = (k+1) % (nfonts+1) ) { if ( k == 0 ) continue; if ( fitab[k] && (i = fitab[k][c]) != 0 ) { p = codetab[k]; pw = fontab[k]; setfont(k); break; } /* End if */ } /* End for */ } /* End else */ lastw = 0; if ( i != 0 && (code = p[i]) != 0 ) { if (size != FRACTSIZE) lastw = horscale * widthfac * ((pw[i] * pstab[size-1] + unitwidth/2) / unitwidth); else lastw = horscale * widthfac * (int)((pw[i] * fractsize + unitwidth/2) / unitwidth); if (widthfac == 1) /* ignore fractional parts since troff */ lastw = (int)lastw; /* does the same */ if (track && encoding != MAXENCODING+2) lastw += track; if (code == NOCODE && fontname[k].afm) code = c + 32; oput(code); } /* End if */ if ( font != ofont ) setfont(ofont); } /* End of put1 */ /*****************************************************************************/ static void oprep(int stext) { if ( textcount > MAXSTACK ) /* don't put too much on the stack? */ endtext(); if ( font != lastfont || size != lastsize || subfont != lastsubfont || (size == FRACTSIZE && fractsize != lastfractsize) || horscale != lasthorscale) { t_sf(0); } if (tracked < 0) t_strack(); if ( vpos != lasty ) endline(); if (stext) { starttext(); if ( ABS(hpos - lastx) > slop ) endstring(); } wordspace = 0; } void oput ( int c /* want to print this character */ ) { /* * * Arranges to print the character whose code is c in the current font. All the * actual positioning is done here, in charlib(), or in the drawing routines. * */ if ( asciichar(c) && printchar(c) ) switch ( c ) { case '(': case ')': case '\\': if (encoding != 5) addchar('\\'); default: addchar(c); } /* End switch */ else if ( c > 040 ) addoctal(c); else charlib(c); lastx += lastw; } /* End of oput */ /*****************************************************************************/ void starttext(void) { /* * Called whenever we want to be sure we're ready to start collecting characters * for the next call to PostScript procedure t (ie. the one that prints them). If * textcount is positive we've already started, so there's nothing to do. The more * complicated encoding schemes save text strings in the strings[] array and need * detailed information about the strings when they're written to the output file * in endtext(). * */ if ( textcount < 1 ) { switch ( encoding ) { case 0: case 1: case 4: putc('(', tf); charcount = 1; break; case 5: strptr = strings; break; case 2: case 3: strptr = strings; spacecount = 0; line[1].str = strptr; line[1].dx = 0; line[1].spaces = 0; line[1].start = hpos; line[1].width = 0; charcount = 0; break; case MAXENCODING+1: /* reverse video */ if ( lastend == -1 ) lastend = hpos; putc('(', tf); charcount = 1; break; case MAXENCODING+2: /* follow a funny baseline */ putc('(', tf); charcount = 1; break; } /* End switch */ textcount = 1; lastx = stringstart = hpos; laststrstart = INT_MIN; } /* End if */ } /* End of starttext */ /*****************************************************************************/ void endtext(void) { char buf[STRINGSPACE+100]; int i; /* loop index */ int n, m; /* * * Generates a call to the PostScript procedure that processes all the text we've * accumulated - provided textcount is positive. * */ if ( textcount > 0 ) { /* started working on some text */ switch ( encoding ) { case 0: fprintf(tf, ")%d t\n", stringstart); break; case 4: if (laststrstart != INT_MIN) fprintf(tf, ")%d %d t\n", stringstart - laststrstart, stringstart); else fprintf(tf, ")%d t\n", stringstart); break; case 5: putstring(strings, strptr - strings, tf); strptr = strings; if (laststrstart != INT_MIN) putint(stringstart - laststrstart, tf); putint(stringstart, tf); putc('t', tf); putc('\n', tf); break; case 1: fprintf(tf, ")%d %d t\n", stringstart, lasty); break; case 2: *strptr = '\0'; line[textcount].width = lastx - line[textcount].start; if ( spacecount != 0 || textcount != 1 ) { n = 0; for ( i = textcount; i > 0; i-- ) { m = snprintf(buf, sizeof buf, "(%s)%d %d", line[i].str, line[i].spaces, line[i].width); if (i < textcount && n + m >= 80) { putc('\n', tf); n = 0; } fputs(buf, tf); n += m; } if (lasty != savey) fprintf(tf, " %d %d %d t\n", textcount, stringstart, lasty); else fprintf(tf, " %d %d u\n", textcount, stringstart); } else { if (lasty != savey) fprintf(tf, "(%s)%d %d w\n", line[1].str, stringstart, lasty); else fprintf(tf, "(%s)%d v\n", line[1].str, stringstart); } savey = lasty; break; case 3: *strptr = '\0'; if ( spacecount != 0 || textcount != 1 ) { n = 0; for ( i = textcount; i > 0; i-- ) { m = snprintf(buf, sizeof buf, "(%s)%d", line[i].str, line[i].dx); if (i < textcount && n + m >= 80) { putc('\n', tf); n = 0; } fputs(buf, tf); n += m; } if (lasty != savey) fprintf(tf, " %d %d %d t\n", textcount, stringstart, lasty); else fprintf(tf, " %d %d u\n", textcount, stringstart); } else { if (lasty != savey) fprintf(tf, "(%s)%d %d w\n", line[1].str, stringstart, lasty); else fprintf(tf, "(%s)%d v\n", line[1].str, stringstart); } savey = lasty; break; case MAXENCODING+1: fprintf(tf, ")%d ", stringstart); fprintf(tf, "%d %d drawrvbox ", lastend - rvslop, (int)(lastx + .5) + rvslop); fprintf(tf, "t\n"/*, stringstart*/); lastend = (lastx + .5) + 2 * rvslop; break; case MAXENCODING+2: fprintf(tf, ")%d %d t\n", stringstart, lasty); break; } /* End switch */ charcount = 0; } /* End if */ textcount = 0; } /* End of endtext */ /*****************************************************************************/ void endstring(void) { int dx; /* * * Horizontal positions are out of sync. End the last open string, adjust the * printer's position, and start a new string. Assumes we've already started * accumulating text. * */ switch ( encoding ) { case 4: if (laststrstart != INT_MIN) charcount += fprintf(tf, ")%d", stringstart - laststrstart); else { putc(')', tf); charcount++; } laststrstart = stringstart; goto nx; case 5: putstring(strings, strptr - strings, tf); strptr = strings; charcount++; if (laststrstart != INT_MIN) charcount += putint(stringstart - laststrstart, tf); laststrstart = stringstart; textcount++; lastx = stringstart = hpos; break; case 0: case 1: charcount += fprintf(tf, ")%d", stringstart); nx: if (charcount >= 60) { putc('\n', tf); charcount = 0; } putc('(', tf); charcount++; textcount++; lastx = stringstart = hpos; break; case 2: case 3: if (!wordspace) { endtext(); starttext(); break; } dx = hpos - lastx; if ( spacecount++ == 0 ) line[textcount].dx = dx; if ( line[textcount].dx != dx ) { *strptr++ = '\0'; line[textcount].width = lastx - line[textcount].start; line[++textcount].str = strptr; *strptr++ = ' '; line[textcount].dx = dx; line[textcount].start = lastx; line[textcount].width = 0; line[textcount].spaces = 1; charcount = 1; } else { *strptr++ = ' '; line[textcount].spaces++; charcount++; } /* End else */ lastx += dx; break; case MAXENCODING+1: charcount += fprintf(tf, ")%d", stringstart); if (charcount >= 60) { putc('\n', tf); charcount = 0; } putc('(', tf); charcount++; textcount++; lastx = stringstart = hpos; break; case MAXENCODING+2: endtext(); starttext(); break; } /* End switch */ } /* End of endstring */ /*****************************************************************************/ void endline(void) { /* * * The vertical position has changed. Dump any accumulated text, then adjust * the printer's vertical position. * */ endtext(); if ( encoding == 0 || encoding == 4 || encoding == MAXENCODING+1 ) { fprintf(tf, "%d %d m\n", hpos, vpos); savey = vpos; } else if (encoding == 5) { putint(hpos, tf); putint(vpos, tf); putc('m', tf); putc('\n', tf); } lastx = stringstart = lastend = hpos; lasty = vpos; } /* End of endline */ /*****************************************************************************/ void addchar ( int c /* next character in current string */ ) { /* * * Does whatever is needed to add character c to the current string. * */ static int lastc; subfont = 0; if (lastc != '\\') oprep(1); lastc = c; switch ( encoding ) { case 0: case 1: case 4: putc(c, tf); if (charcount++ >= 72 && c != '\\') { putc('\\', tf); putc('\n', tf); charcount = 0; } break; case 5: *strptr++ = c; break; case 2: case 3: *strptr++ = c; if (charcount++ >= 72 && c != '\\') { *strptr++ = '\\'; *strptr++ = '\n'; charcount = 0; } break; case MAXENCODING+1: case MAXENCODING+2: putc(c, tf); if (charcount++ >= 72 && c != '\\') { putc('\\', tf); putc('\n', tf); charcount = 0; } break; } /* End switch */ } /* End of addchar */ /*****************************************************************************/ void addoctal ( int c /* add it as an octal escape */ ) { int n; /* * * Adds c to the current string as an octal escape \ddd. * * * If c is not a byte, try to squeeze it into the control area. */ oprep(0); if (c >= 128 && fontname[font].afm && fontname[font].afm->encmap) { c = fontname[font].afm->encmap[c - 128]; subfont = c >> 8; c &= 0377; } else subfont = 0; oprep(1); switch ( encoding ) { case 0: case 1: case 4: charcount += fprintf(tf, "\\%03o", c); if (charcount >= 72) { putc('\\', tf); putc('\n', tf); charcount = 0; } break; case 5: *strptr++ = c; break; case 2: case 3: snprintf(strptr, sizeof strings - (strptr - strings), "\\%03o", c); n = strlen(strptr); strptr += n; charcount += n; if (charcount >= 72) { *strptr++ = '\\'; *strptr++ = '\n'; charcount = 0; } break; case MAXENCODING+1: case MAXENCODING+2: charcount += fprintf(tf, "\\%03o", c); if (charcount >= 72) { putc('\\', tf); putc('\n', tf); charcount = 0; } break; } /* End switch */ } /* End of addoctal */ /*****************************************************************************/ void charlib ( int code /* either 1 or 2 */ ) { char *name; /* name of the character */ char tname[10]; /* in case it's a single ASCII character */ char *filename; /* real file name */ /* * * Called from oput() for characters having codes less than 040. Special files * that define PostScript procedures for certain characters can be found in * directory *fontdir/devpost/charlib. If there's a file that has the same name as * the character we're trying to print it's copied to the output file, otherwise * nothing, except some positioning, is done. * * All character definitions are only made once. Subsequent requests to print the * character generate a call to a procedure that begins with the prefix build_ and * ends with the character's name. Special characters that are assigned codes * other than 1 are assumed to have additional data files that should be copied * to the output file immediately after the build_ call. Those data files should * end in the suffix .map, and usually will be a hex representation of a bitmap. * */ subfont = 0; oprep(1); endtext(); if ( lastc < 128 ) { /* just a simple ASCII character */ snprintf(tname, sizeof tname, "%.3o", lastc); name = tname; } else name = &chname[chtab[lastc - 128]]; /* * This is just a kludge of course. But since only one file exists which * needs a name mapping, no more general method had been implemented. */ if (*name == 'L' && name[1] == 'H' && !name[2]) filename = "LH_uc"; else filename = name; if ( downloaded[lastc] == 0 ) { snprintf(temp, sizeof temp, "%s/dev%s/charlib/%s", fontdir, realdev, filename); if ( access(temp, 04) == 0 && doglobal(temp) == TRUE ) { downloaded[lastc] = 1; t_sf(0); } /* End if */ } /* End if */ if ( downloaded[lastc] == 1 ) { xymove(hpos, vpos); fprintf(tf, "%d build_%s\n", (int) lastw, name); if ( code != 1 ) { /* get the bitmap or whatever */ snprintf(temp, sizeof temp, "%s/dev%s/charlib/%s.map", fontdir, realdev, name); if ( access(temp, 04) == 0 && tf == stdout ) cat(temp, tf); } /* End if */ fprintf(tf, "%d %d m\n", stringstart = hpos + lastw, vpos); savey = vpos; } /* End if */ } /* End of charlib */ /*****************************************************************************/ int doglobal ( char *name /* copy this to the output - globally */ ) { int val = FALSE; /* returned to the caller */ /* * * Copies file *name to the output file and brackets it with whatever commands are * needed to have it exported to the global environment. TRUE is returned if we * successfully add file *name to the output file. * * Actually, all files included this way are procsets, so they go into * the resource section of the PostScript output and not in the global * setup file. * */ if ( tf == stdout ) { val = cat(name, rf); reset(); } /* End if */ return(val); } /* End of doglobal */ /*****************************************************************************/ void documentfont(const char *name) { static FILE *fp_out; /* PostScript name added to this file */ static int pos; static struct fn { struct fn *next; const char *name; } *fn; struct fn *ft; int n; if ( temp_file == NULL ) /* generate a temp file name */ if ( (temp_file = tempname("dpost")) == NULL ) return; if ( fp_out == NULL && (fp_out = fopen(temp_file, "w")) == NULL ) return; for (ft = fn; ft; ft = ft->next) if (strcmp(name, ft->name) == 0) return; ft = calloc(1, sizeof *ft); ft->name = strdup(name); ft->next = fn; fn = ft; if ( docfonts++ == 0 ) pos += fprintf(fp_out, "%s", DOCUMENTFONTS); else { n = strlen(name); if (pos + n >= 80) { fprintf(fp_out, "\n%s", CONTINUECOMMENT); pos = 0; } } pos += fprintf(fp_out, " %s", name); fflush(fp_out); t_dosupply(name); } void documentfonts(void) { FILE *fp_in = NULL; /* PostScript font name read from here */ /* * * Whenever a new font is used we try to record the appropriate PostScript font * name in *temp_file for the DOCUMENTFONTS comment that's put out in done(). * By default PostScript font names are found in /usr/lib/font/devpost. Fonts * that have a .name file are recorded in *temp_file. The first string in that * file is expected to be that font's (long) PostScript name. * */ snprintf(temp, sizeof temp, "%s/dev%s/%s.name", fontdir, realdev, fontname[font].name); if (fontname[font].afm == NULL && (fp_in = fopen(temp, "r")) != NULL ) { if ( sget(temp, sizeof temp, fp_in) == 1 ) { documentfont(temp); } /* End if */ if (fp_in != NULL) fclose(fp_in); } else if (fontname[font].afm != NULL){ documentfont(fontname[font].afm->fontname); } /* End if */ } /* End of documentfonts */ /*****************************************************************************/ void redirect ( int pg /* next page we're printing */ ) { static FILE *fp_null = NULL; /* if output is turned off */ /* * * If we're not supposed to print page pg, tf will be directed to /dev/null, * otherwise output goes to stdout. * */ if ( pg >= 0 && in_olist(pg) == ON ) tf = stdout; else if ( (tf = fp_null) == NULL ) tf = fp_null = fopen("/dev/null", "w"); } /* End of redirect */ /*****************************************************************************/ static char * mbs2pdf(char *mp) { char *ustr, *tp; int c, i, sz; #ifdef EUC int n = 0, w; wchar_t wc; #endif for (tp = mp; *tp && (*tp&~0177) == 0 && *tp&~037; tp++); if (*tp == 0) { ustr = malloc(sz = 16); *ustr = '('; c = i = 1; while (*mp) { switch (*mp) { case '(': case ')': case '\\': ustr[i++] = '\\'; c++; /*FALLTHRU*/ default: ustr[i++] = *mp++; c++; } if (i + 4 >= sz) ustr = realloc(ustr, sz += 16); if (c >= 60) { ustr[i++] = '\\'; ustr[i++] = '\n'; c = 0; } } ustr[i++] = ')'; ustr[i++] = 0; return ustr; } #ifdef EUC ustr = malloc(sz = 16); c = i = snprintf(ustr, sz, " 0xFFFF) { error(NON_FATAL, "only BMP values allowed for PDFMark"); continue; } if (i + 8 >= sz) ustr = realloc(ustr, sz += 16); w = snprintf(&ustr[i], sz - i * sizeof(*ustr), "%04X", (int)wc); i += w; c += w; if (c >= 60) { ustr[i++] = '\n'; c = 0; } } ustr[i++] = '>'; ustr[i] = 0; return ustr; #else /* !EUC */ error(NON_FATAL, "this instance of dpost only supports ASCII with PDFMark"); return NULL; #endif /* !EUC */ } static void t_pdfmark(char *buf) { char *bp, *tp; int n; while (spacechar(*buf&0377)) buf++; for (bp = buf; *bp && !spacechar(*bp&0377); bp++); *bp++ = '\0'; while (spacechar(*bp&0377)) bp++; for (tp = bp; *tp; tp++) if (*tp == '\n') { *tp = '\0'; break; } if (strcmp(buf, "Author") == 0) Author = mbs2pdf(bp); else if (strcmp(buf, "Title") == 0) Title = mbs2pdf(bp); else if (strcmp(buf, "Subject") == 0) Subject = mbs2pdf(bp); else if (strcmp(buf, "Keywords") == 0) Keywords = mbs2pdf(bp); else if (strcmp(buf, "Bookmark") == 0 || strcmp(buf, "BookmarkClosed") == 0) { n = strtol(bp, &bp, 10); while (spacechar(*bp&0377)) bp++; if (n < 0 || n > MAXBOOKMARKLEVEL) { error(NON_FATAL, "invalid PDFMark Bookmark level %d," "maximum is %d\n", n, MAXBOOKMARKLEVEL); return; } Bookmarks = realloc(Bookmarks, ++nBookmarks*sizeof *Bookmarks); Bookmarks[nBookmarks-1].level = n; Bookmarks[nBookmarks-1].Title = mbs2pdf(bp); Bookmarks[nBookmarks-1].title = strdup(bp); Bookmarks[nBookmarks-1].Count = 0; Bookmarks[nBookmarks-1].closed = strcmp(buf, "BookmarkClosed") == 0; endtext(); fprintf(tf, "[ /Dest /Bookmark$%ld\n" " /View [/XYZ -4 %g 0]\n" "/DEST pdfmark\n", (long)nBookmarks - 1, pagelength - (lasty >= 0 ? vpos * 72.0 / res : -4)); } else error(NON_FATAL, "unknown PDFMark attribute %s", buf); } static void orderbookmarks(void) { int counts[MAXBOOKMARKLEVEL+1]; int refs[MAXBOOKMARKLEVEL+1]; int i, j, k, t; int lvl = 0; /* * Generate the Count parameter from the given levels. */ memset(&counts, 0, sizeof counts); for (i = 0; i <= MAXBOOKMARKLEVEL; i++) refs[i] = -1; for (i = 0; i <= nBookmarks; i++) { k = i < nBookmarks ? Bookmarks[i].level : 0; if (i == nBookmarks || k <= lvl) { for (j = k+1; j <= MAXBOOKMARKLEVEL; j++) { t = j - 1; if (refs[t] >= 0) { Bookmarks[refs[t]].Count += counts[j]; refs[t] = -1; } counts[j] = 0; } } if (k > 0 && refs[k-1] < 0) { while (k > 0 && refs[k-1] < 0) k--; error(NON_FATAL, "PDFMark Bookmark \"%s\" at level %d " "has no direct parent, " "using level %d\n", Bookmarks[i].title, Bookmarks[i].level, k); } counts[k]++; refs[k] = i; lvl = k; } } static void t_locale(char *lp) { static char *savlp; if (savlp && strcmp(lp, savlp) == 0) return; free(savlp); savlp = malloc(strlen(lp) + 1); sscanf(lp, "%s", savlp); setlocale(LC_CTYPE, savlp); mb_cur_max = MB_CUR_MAX; } static void pref(const char *lp, FILE *fp) { int c; while ((c = *lp++ & 0377) != 0 && c != '\n') { if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')) putc(c, fp); else fprintf(fp, "$%2x", c); } } static void pref_uri(const char *lp, FILE *fp) { int c; /* Don't do any escaping here to avoid double-escaping. */ while ((c = *lp++ & 0377) != 0 && c != '\n') { putc(c, fp); } } static void t_anchor(char *lp) { int v; v = strtol(lp, &lp, 10); if ((lp = strchr(lp, ' ')) != NULL) { lp++; endtext(); fprintf(tf, "[ /Dest /Anchor$"); pref(lp, tf); fprintf(tf, "\n" " /View [/XYZ -4 %g 0]\n" "/DEST pdfmark\n", pagelength - (v >= 0 ? v * 72.0 / res : -4)); } } static char linkcolor[60] = "0 0 1"; static char linkborder[60] = "0 0 1"; static char * t_linkborderstyle(char *arg) { char c, *s; s = arg; for (s = arg; (c = *s) && c != '\n'; s++); *s = 0; return strdup(arg); } static void t_link(char *lp) { int llx, lly, urx, ury; llx = strtol(lp, &lp, 10); if (*lp) { while (*lp == ',') lp++; lly = strtol(lp, &lp, 10); if (*lp) { while (*lp == ',') lp++; urx = strtol(lp, &lp, 10); if (*lp) { while (*lp == ',') lp++; ury = strtol(lp, &lp, 10); if ((lp = strchr(lp, ' ')) != NULL) { lp++; endtext(); fprintf(tf, "[ /Dest /Anchor$"); pref(lp, tf); fprintf(tf, "\n" "/Rect [%d %d %d %d]\n" "/Color [%s]\n", llx, -lly, urx, -ury, linkcolor); if (linkborderstyle) fprintf(tf, "/BS << %s >>\n", linkborderstyle); else fprintf(tf, "/Border [%s]\n", linkborder); fprintf(tf, "/Subtype /Link\n" "/ANN pdfmark\n"); } } } } } static void t_linkcolor(char *lp) { float r, g, b; r = strtod(lp, &lp); g = strtod(lp, &lp); b = strtod(lp, &lp); snprintf(linkcolor, sizeof linkcolor, "%g %g %g", r, g, b); } static void t_linkborder(char *lp) { float bx, by, c; bx = strtod(lp, &lp); by = strtod(lp, &lp); c = strtod(lp, &lp); snprintf(linkborder, sizeof linkborder, "%g %g %g", bx, by, c); free(linkborderstyle); linkborderstyle = NULL; } static char ulinkcolor[60] = "0 0 1"; static char ulinkborder[60] = "0 0 1"; static void t_ulink(char *lp) { int llx, lly, urx, ury; llx = strtol(lp, &lp, 10); if (*lp) { while (*lp == ',') lp++; lly = strtol(lp, &lp, 10); if (*lp) { while (*lp == ',') lp++; urx = strtol(lp, &lp, 10); if (*lp) { while (*lp == ',') lp++; ury = strtol(lp, &lp, 10); if ((lp = strchr(lp, ' ')) != NULL) { lp++; endtext(); fprintf(tf, "[ /Rect [%d %d %d %d]\n" "/Color [%s]\n", llx, -lly, urx, -ury, ulinkcolor); if (ulinkborderstyle) fprintf(tf, "/BS << %s >>\n", ulinkborderstyle); else fprintf(tf, "/Border [%s]\n", ulinkborder); fprintf(tf, "/Action << /Subtype /URI\n" "/URI ("); pref_uri(lp, tf); fprintf(tf, ") >>\n" "/Subtype /Link\n" "/ANN pdfmark\n"); } } } } } static void t_ulinkcolor(char *lp) { float r, g, b; r = strtod(lp, &lp); g = strtod(lp, &lp); b = strtod(lp, &lp); snprintf(ulinkcolor, sizeof ulinkcolor, "%g %g %g", r, g, b); } static void t_ulinkborder(char *lp) { float bx, by, c; bx = strtod(lp, &lp); by = strtod(lp, &lp); c = strtod(lp, &lp); snprintf(ulinkborder, sizeof ulinkborder, "%g %g %g", bx, by, c); free(ulinkborderstyle); ulinkborderstyle = NULL; }