summaryrefslogtreecommitdiff
path: root/Build/source/texk/web2c/mpware/dmp.c
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/texk/web2c/mpware/dmp.c')
-rw-r--r--Build/source/texk/web2c/mpware/dmp.c1326
1 files changed, 1326 insertions, 0 deletions
diff --git a/Build/source/texk/web2c/mpware/dmp.c b/Build/source/texk/web2c/mpware/dmp.c
new file mode 100644
index 00000000000..8682fce330c
--- /dev/null
+++ b/Build/source/texk/web2c/mpware/dmp.c
@@ -0,0 +1,1326 @@
+/* $Id: dmp.c,v 1.17 2005/06/22 17:31:43 olaf Exp $
+ *
+ * Public domain.
+ *
+ * Previous versions of this file were copyright 1990 - 1995 by AT&T
+ * Bell Laboratories. It has since been put into the public domain.
+ *
+ * John Hobby wrote the original version, which has since been
+ * modified by several other people.
+ */
+
+/* This program reads device-independent troff output files,
+ and converts them into a symbolic form understood by MetaPost. Some
+ of the code was borrowed from DVItoMP. It understands all the D? graphics
+ functions that dpost does but it ignores `x X' device control functions
+ such as `x X SetColor:...', `x X BeginPath:', and `x X DrawPath:...'.
+
+ The output file is a sequence of MetaPost picture expressions, one for every
+ page in the input file. It makes no difference where the input file comes
+ from, but it is intended to process the result of running eqn and troff on
+ the output of MPtoTR. Such a file contains one page for every btex...etex
+ block in the original input. This program then creates a corresponding
+ sequence of MetaPost picture expressions for use as an auxiliary input file.
+ Since MetPost expects such files to have the extension .mpx, the output
+ is sometimes called an `mpx' file.
+
+ The |banner| string defined here should be changed whenever this program
+ gets modified.
+*/
+char *banner="% Written by DMP, Version 0.64"; /* first line of output */
+char *term_banner="This is DMP, Version 0.64";
+
+#include "config.h"
+#include <kpathsea/c-proto.h>
+#include <kpathsea/tex-file.h>
+#include <kpathsea/c-ctype.h>
+#include <kpathsea/c-pathch.h>
+#include <math.h>
+
+/* From ../cpascal.h */
+extern void printversionandexit P3H(const_string, const_string, const_string);
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+#define POOLMAX 65000 /* total characters in all font and char names */
+#define FCOUNT 100 /* maximum number of fonts */
+#define SHIFTS 100 /* maximum number of characters with special shifts */
+#define line_length 79 /* maximum output line length (must be at least 60) */
+#define Hprime 2459 /* much bigger than max(chars/font,fonts/job) */
+#define MAXCHARS 2048 /* character codes fall in the range 0..MAXCHARS-1 */
+#define LLENGTH 1024 /* one more than maximum line length for troff output */
+
+#define is_specchar(c) (c<=2) /* does charcode c identify a special char? */
+#define LWscale 0.03 /* line width for graphics as a fraction of pointsize */
+#define YCORR 12.0 /* V coordinate of reference point in (big) points */
+
+char strpool[POOLMAX]; /* text for font names and char names */
+int poolsize = 0; /* chars used so far in strpool */
+
+char *texname[FCOUNT]; /* TeX names (null-terminated strings) */
+int font_num[FCOUNT]; /* external font numbers */
+float font_design_size[FCOUNT]; /* design size in TeX points */
+struct hcell *charcodes[FCOUNT];/* hash tables for translating char names */
+int next_specfnt[FCOUNT]; /* used to link special fonts together */
+float charwd[FCOUNT][MAXCHARS]; /* width/ptsize indexed [font num][char code] */
+int nfonts; /* no. of internal font nums (texname indices)*/
+int shiftchar[SHIFTS]; /* charcode of character to shift, else -1 */
+float shifth[SHIFTS],shiftv[SHIFTS]; /* shift vals/fontsize (y is upward) */
+int shiftptr = 0; /* number of entries in shift tables */
+int shiftbase[FCOUNT]; /* initial index into shifth,shiftv,shiftchar */
+int specfnt = FCOUNT; /* int. num. of first special font (or FCOUNT)*/
+int *specf_tail = &specfnt; /* tail of specfnt list (*specf_tail==FCOUNT) */
+FILE *trf; /* the input file (troff output) */
+FILE *mpxf; /* the output file */
+struct hcell *trfonts; /* hash tab for internal nums of troff fonts */
+float unit = 0.0; /* (big) points per troff unit (0 when unset)*/
+int h,v; /* current position in tr. units (v downward)*/
+float cursize; /* current type size in (big) points */
+int curfont; /* internal number for current font */
+float Xslant; /* degrees additional slant for all fonts */
+float Xheight; /* yscale fonts to this height if nonzero */
+char *dbname = "trfonts.map"; /* file for table of troff & TFM font names */
+char *adjname = "trchars.adj"; /* file for character shift amounts */
+#define tfmpath kpse_tfm_format
+#define dbpath kpse_mpsupport_format
+#define trpath kpse_troff_font_format
+int lnno = 0; /* line num. in troff output file (our input) */
+
+
+void quit P3C(char*,msg1,char*,msg2,char*,msg3)
+{
+ fprintf(stderr,"DMP abort at troff output line %d:\n%s%s%s\n",
+ lnno, msg1, msg2, msg3);
+ exit(1);
+}
+
+void warn P3C(char*,msg1,char*,msg2,char*,msg3)
+{
+ fprintf(stderr,"DMP warning at troff output line %d:\n%s%s%s\n",
+ lnno, msg1, msg2, msg3);
+}
+
+void add_to_pool P1C(char,c)
+{
+ if (poolsize==POOLMAX) quit("Need to increase POOLMAX","","");
+ else strpool[poolsize++] = c;
+}
+
+
+
+
+/**************************************************************
+ Hash tables
+***************************************************************/
+
+typedef struct hcell {
+ char* lab;
+ int datum;
+} Hcell;
+
+#define new_htab (Hcell*) calloc((unsigned)Hprime, (unsigned)sizeof(Hcell))
+
+
+int hash P1C(char*,s)
+{
+ register int r;
+ for(r=0; *s!=0; s++) {
+ /* GROFF - in font metrics file the character name may be 8bit
+ groff_font(5): Groff supports eight bit characters;
+ groff_out(5): Note that single characters can have the
+ eighth bit set, as can the names of fonts and special
+ characters.
+ */
+ r = (r<<1) + *(unsigned char*)s;
+ while (r>=Hprime) r-=Hprime;
+ }
+ return r;
+}
+
+
+/* Find or insert the given string in the hash table and return the address
+ of the datum.
+*/
+Hcell *failure; /* null unless last hfind failed (used below) */
+
+int *hfind P2C(char*,s,Hcell*,htab)
+{
+ register Hcell *p;
+ register int cnt = Hprime;
+ failure = (Hcell *) 0;
+ p = &htab[hash(s)];
+ do {
+ if (p->lab==NULL) {
+ failure = p;
+ if (s<&strpool[0] || s>=&strpool[POOLMAX]) {
+ p->lab = &strpool[poolsize];
+ do add_to_pool(*s); while (*s++!='\0');
+ } else p->lab = s;
+ return &p->datum;
+ }
+ if (strcmp(s, p->lab)==0) return &p->datum;
+ if (p==htab) p= &htab[Hprime-1];
+ else p--;
+ } while (--cnt >= 0);
+ quit("Need to increase Hprime","","");
+ return (int*)0; /* keep lint happy */
+}
+
+
+/* If the last hfind() failed, undo the insertion and return zero (FALSE).
+*/
+int hfound()
+{
+ if (failure==(Hcell *)0) return 1;
+ failure->lab = NULL;
+ return 0;
+}
+
+
+
+/**************************************************************
+ Search Paths
+***************************************************************/
+
+FILE *fsearch P3C(char*,nam, char*,ext, kpse_file_format_type,format)
+{
+ FILE *f = NULL;
+
+ string fname = kpse_find_file (nam, format, true);
+ const_string mode = kpse_format_info[format].binmode
+ ? FOPEN_RBIN_MODE
+ : FOPEN_R_MODE;
+ if (fname) {
+ f = xfopen (fname, mode);
+ }
+ if (f==NULL) quit("Cannot find ",nam,ext);
+ return f;
+}
+
+
+
+/**************************************************************
+ Scanning Numbers
+***************************************************************/
+
+/* The standard functions atoi(), atof(), and sscanf() provide ways of reading
+ numbers from strings but they give no indication of how much of the string
+ is consumed. These homemade versions don't parse scientific notation.
+*/
+char *arg_tail; /* char after the number just gotten; NULL on failure */
+
+
+int get_int P1C(char *,s)
+{
+ register int i, d, neg;
+ if (s==NULL) goto bad;
+ for (neg=0;; s++)
+ if (*s=='-') neg=!neg;
+ else if (*s!=' ' && *s!='\t') break;
+ if (i= *s-'0', 0>i||i>9) goto bad;
+ while (d= *++s-'0', 0<=d&&d<=9)
+ i = 10*i + d;
+ arg_tail = s;
+ return neg ? -i : i;
+bad:arg_tail = NULL;
+ return 0;
+}
+
+/* GROFF font description files use octal character codes
+ groff_font(5): The code can be any integer. If it starts with
+ a 0 it will be interpreted as octal; if it starts with 0x
+ or 0X it will be intepreted as hexadecimal.
+*/
+int get_int_map P1C(char *,s)
+{
+ register int i;
+ if (s==NULL) goto bad;
+ i=strtol(s,&arg_tail,0);
+ if (s==arg_tail) goto bad;
+ return i;
+bad:arg_tail = NULL;
+ return 0;
+}
+
+/* Troff output files contain few if any non-integers, but this program is
+ prepared to read floats whenever they seem reasonable; i.e., when the
+ number is not being used for character positioning. (For non-PostScript
+ applications h and v are usually in pixels and should be integers.)
+*/
+float get_float P1C(char *,s)
+{
+ register int d, neg, digits;
+ register float x, y;
+
+ digits = 0;
+ if (s!=NULL) {
+ for (neg=0;; s++)
+ if (*s=='-') neg=!neg;
+ else if (*s!=' ' && *s!='\t') break;
+ x = 0.0;
+ while (d= *s-'0', 0<=d&&d<=9) {
+ x = 10.0*x + d;
+ digits++; s++;
+ }
+ if (*s=='.') {
+ y = 1.0;
+ while (d= *++s-'0', 0<=d&&d<=9) {
+ y /= 10.0;
+ x += y*d;
+ digits++;
+ }
+ }
+ }
+ if (digits==0) {
+ arg_tail = NULL;
+ return 0.0;
+ }
+ arg_tail = s;
+ return neg ? -x : x;
+}
+
+/* GROFF font description files have metrics field
+ of comma-separated integers. Traditional troff
+ have a float in this position. The value is not
+ used anyway - thus just skip the value,
+ eat all non-space chars.
+*/
+float get_float_map P1C(char *,s)
+{
+ if (s!=NULL) {
+ while (isspace(*s))
+ s++;
+ while (!isspace(*s) && *s)
+ s++;
+ }
+ arg_tail = s;
+ return 0;
+}
+
+/**************************************************************
+ Reading Initialization Files
+***************************************************************/
+
+/* Read the database file, reserve internal font numbers and set
+ the texname[] entries. Each line in the database file contains
+ <troff-name>\t,PostScript-name>\t<TeX-name>
+ or just <troff-name>\t,PostScript-name> if the TeX name matches the
+ PostScript name. ("\t" means one or more tabs.)
+*/
+
+void read_fmap P1C(char*,dbase)
+{
+ FILE *fin;
+ int c; /* last character read */
+ char *nam; /* a font name being read */
+
+ nfonts = 0;
+ fin = fsearch(dbase,"",dbpath);
+ trfonts = new_htab;
+ while ((c=getc(fin))!=EOF) {
+ if (nfonts==FCOUNT) quit("Need to increase FCOUNT","","");
+ nam = &strpool[poolsize];
+ for (;c!='\t'; c=getc(fin)) add_to_pool(c);
+ add_to_pool(0);
+ *hfind(nam, trfonts) = nfonts;
+ texname[nfonts] = &strpool[poolsize];
+ do {
+ poolsize = texname[nfonts] - strpool;
+ do c=getc(fin); while (c=='\t');
+ for (;c!='\t' && c!='\n'; c=getc(fin))
+ add_to_pool(c);
+ add_to_pool(0);
+ } while (c=='\t');
+ font_num[nfonts] = -1; /* indicate font is not mounted */
+ nfonts++;
+ }
+ fclose(fin);
+}
+
+
+/* Some characters need their coordinates shifted in order to agree with
+ troff's view of the world. Logically, this information belongs in the
+ font description files but it actually resides in a PostScript prolog
+ that the troff output processor dpost reads. Since that file is in
+ PostScript and subject to change, we read the same information from
+ a small auxiliary file that gives shift amounts relative to the font
+ size with y upward.
+*/
+/* GROFF NOTE:
+ The PostScript prologue in GNU groff's font directory does not
+ contain any character shift information, so the following function
+ becomes redundant. Simply keeping an empty "trchars.adj" file
+ around will do fine without requiring any changes to this program.
+*/
+void read_char_adj P1C(char*,adjfile)
+{
+ FILE* fin;
+ char buf[200];
+ int i;
+
+ fin = fsearch(adjfile, "", dbpath);
+ for (i=0; i<nfonts; i++)
+ shiftbase[i] = 0;
+ while (fgets(buf,200,fin)!=NULL) {
+ if (shiftptr==SHIFTS-1) quit("Need to increase SHIFTS","","");
+ if (buf[0]!=' ' && buf[0]!='\t') {
+ for (i=0; buf[i]!='\0'; i++)
+ if (buf[i]=='\n') buf[i]='\0';
+ shiftchar[shiftptr++] = -1;
+ shiftbase[*hfind(buf,trfonts)] = shiftptr;
+ if (!hfound()) quit(adjfile," refers to unknown font ",buf);
+ } else {
+ shiftchar[shiftptr] = get_int(buf);
+ shifth[shiftptr] = get_float(arg_tail);
+ shiftv[shiftptr] = -get_float(arg_tail);
+ if (arg_tail==NULL) quit("Bad shift entry : \"",buf,"\"");
+ shiftptr++;
+ }
+ }
+ shiftchar[shiftptr++] = -1;
+ fclose(fin);
+}
+
+
+/**************************************************************
+ Reading Font Files
+***************************************************************/
+
+
+/* Read the TFM file for the font with internal number f, updating the
+ data structures appropriately. We get the character widths out of the
+ tfm file instead of the troff font description file because they don't
+ because the latter source reflects alterations made only by dpost (the
+ troff output driver that is bypassed when using MetaPost).
+*/
+void read_tfm P1C(int, f)
+{
+ FILE* tf;
+ long a = 0;
+ int sizes[5]; /* file & header lengths, bc, ec, words in wd table */
+ long wd[256]; /* the width table (font size relative, scaled 2^20) */
+ int i, j;
+ long wtmp; /* needed to a floating exception on certain machines */
+
+ tf = fsearch(texname[f], ".tfm", tfmpath);
+ for (i=0; i<5; i++) {
+ sizes[i] = getc(tf);
+ sizes[i] = (sizes[i]<<8) | (getc(tf) & 0377);
+ }
+ if (sizes[1]<2 || sizes[2]<0 || sizes[3]<sizes[2]-1 || sizes[3]>255
+ || sizes[0]<sizes[1]+sizes[3]-sizes[2]+sizes[4]+7)
+ quit("Bad tfm file: ",texname[f],".tfm");
+ for (i=2*5; i<28; i++)
+ (void) getc(tf);
+ for (i=0; i<4; i++)
+ a = (a<<8) | (long) (getc(tf) & 0377);
+ font_design_size[f] = ((float) a)/1048576.0;
+ fseek(tf, (long)(28+4*(sizes[1]+sizes[3]-sizes[2])), 0);
+ for (i=0; i<sizes[4]; i++) {
+ wd[i] = 0L;
+ for (j=0; j<4; j++)
+ wd[i] = (wd[i]<<8) | (long) (getc(tf) & 0377);
+ }
+ fseek(tf, (long)(24+4*sizes[1]), 0);
+ for (i=sizes[2]; i<=sizes[3]; i++) {
+ wtmp = wd[getc(tf) & 0377];
+ charwd[f][i] = ((double) wtmp)/1048576.0/unit;
+ for (j=3; --j>=0;) (void) getc(tf);
+ }
+ fclose(tf);
+}
+
+
+/* Given one line from the character description file for the font with
+ internal number f, save the appropriate data in the charcodes[f] table.
+ A return value of zero indicates a syntax error.
+*/
+/* GROFF NOTE:
+ GNU groff uses an extended font description file format documented
+ in groff_font(5). In order to allow parsing of groff's font files,
+ this function needs to be rewritten as follows:
+
+ 1. The `metrics' field parsed by "get_float(lin);" may include
+ a comma-separated list of up to six decimal integers rather
+ than just a single floating-point number.
+
+ 2. The `charcode' field parsed by "lastcode = get_int(arg_tail);"
+ may be given either in decimal, octal, or hexadecimal format.
+*/
+int scan_desc_line P2C(int,f, char*,lin)
+{
+ static int lastcode;
+ char *s;
+
+ s = &strpool[poolsize];
+ while (*lin!=' ' && *lin!='\t' && *lin!='\0')
+ add_to_pool(*lin++);
+ add_to_pool('\0');
+ while (*lin==' ' || *lin=='\t') lin++;
+ if (*lin=='"')
+ *hfind(s,charcodes[f]) = lastcode;
+ else {
+ (void) get_float_map(lin);
+ (void) get_int(arg_tail);
+ lastcode = get_int_map(arg_tail);
+ if (arg_tail==NULL) return 0;
+ *hfind(s,charcodes[f]) = lastcode;
+ if (lastcode<0 || lastcode>=MAXCHARS) return 0;
+ }
+ return 1;
+}
+
+/* Read the font description file for the font with the given troff name
+ and update the data structures. The result is the internal font number.
+*/
+int read_fontdesc P1C(char*,nam)
+{
+ char buf[200];
+ FILE* fin; /* input file */
+ int f; /* internal font number */
+
+ if (unit==0.0) quit("Resolution is not set soon enough","","");
+ f = *hfind(nam, trfonts);
+ if (!hfound())
+ quit("Font was not in map file","","");
+ fin = fsearch(nam, "", trpath);
+ for (;;) {
+ if (fgets(buf,200,fin)==NULL)
+ quit("Description file for ",nam," ends unexpectedly");
+ if (strncmp(buf,"special",7)==0) {
+ *specf_tail = f;
+ next_specfnt[f] = FCOUNT;
+ specf_tail = &next_specfnt[f];
+ } else if (strncmp(buf,"charset",7)==0)
+ break;
+ }
+ charcodes[f] = new_htab;
+ while (fgets(buf,200,fin)!=NULL)
+ if (scan_desc_line(f, buf) == 0)
+ quit(nam," has a bad line in its description file: ",buf);
+ fclose(fin);
+ return f;
+}
+
+
+
+/**************************************************************
+ Low Level Output Routines
+***************************************************************/
+
+/* One of the basic output operations is to write a \MP\ string expression for
+ a sequence of characters to be typeset. The main difficulties are that such
+ strings can contain arbitrary eight-bit bytes and there is no fixed limit on
+ the length of the string that needs to be produced. In extreme cases this
+ can lead to expressions such as
+ char7&char15\&char31&"?FWayzz"
+ &"zzaF"&char15&char3&char31
+ &"Nxzzzzzzzwvtsqo"
+
+ A global variable state keeps track of the output process.
+ When state=normal we have begun a quoted string and the next character
+ should be a printable character or a closing quote. When state=special
+ the last thing printed was a `char' construction or a closing quote
+ and an ampersand should come next. The starting condition state=initial
+ is a lot like state=special, except no ampersand is required.
+*/
+#define special 0 /* the state after printing a `char' expression */
+#define normal 1 /* the state value in a quoted string */
+#define initial 2 /* initial state */
+
+int state = initial;
+int print_col = 0; /* there are at most this many characters on the current line */
+
+
+/* To print a string on the MPX file, initialize print_col, ensure that
+ state=initial, and pass the characters one-at-a-time to print_char.
+*/
+void print_char P1C(char,cc)
+{
+ int printable; /* nonzero if it is safe to print c */
+ int l; /* number of chars in c or the `char' expression */
+ int c; /* equal to cc mod 256, but always positive */
+
+ c = cc&0377;
+ printable = isprint(c) && c<128; /* avoid high-bit-on Latin-1 chars */
+ if (printable) l=1;
+ else if (c<10) l=5;
+ else if (c<100) l=6;
+ else l=7;
+ if (print_col+l>line_length-2) {
+ if (state==normal) {
+ putc('"',mpxf);
+ state = special;
+ }
+ putc('\n',mpxf);
+ print_col = 0;
+ }
+ if (state==normal)
+ if (printable) putc(c,mpxf);
+ else {
+ fprintf(mpxf,"\"&char%d",c);
+ print_col += 2;
+ }
+ else {
+ if (state==special) {
+ putc('&',mpxf);
+ print_col++;
+ }
+ if (printable) {
+ fprintf(mpxf,"\"%c",c);
+ print_col++;
+ } else
+ fprintf(mpxf,"char%d",c);
+ }
+ print_col += l;
+ state = printable ? normal : special;
+}
+
+
+/* The end_char_string procedure gets the string ended properly and ensures
+ that there is room for |l| more characters on the output line.
+*/
+void end_char_string P1C(int, l)
+{
+ while (state>special) {
+ putc('"',mpxf);
+ print_col++;
+ state--;
+ }
+ if (print_col+l>line_length) {
+ putc('\n',mpxf);
+ print_col = 0;
+ }
+ state = initial; /* get ready to print the next string */
+}
+
+
+
+/**************************************************************
+ Page and Character Output
+***************************************************************/
+
+char font_used[FCOUNT]; /* nonzero for fonts used on this page */
+int fonts_used; /* nonzero if any font been used on this page */
+int graphics_used; /* nonzero if any graphics seen on this page */
+float str_h1,str_v; /* corrected start pos for current out string */
+float str_h2; /* where the current output string ends */
+int str_f; /* internal font num for cur. output string */
+float str_size; /* point size for this text string */
+
+
+/* Before using any fonts we need to define a MetaPost macro for typesetting
+ character strings.
+*/
+void prepare_font_use()
+{
+ int k;
+
+ for (k=0;k<nfonts;k++) font_used[k]=0;
+ fonts_used = 1;
+ fprintf(mpxf,"string n[];\n");
+ fprintf(mpxf,"vardef s(expr t,m,x,y) =\n");
+ fprintf(mpxf," addto p also t scaled(m*1.00375) shifted(x,y); enddef;\n");
+}
+
+
+/* Do what is necessary when the font with internal number f is used for the
+ first time on a page.
+*/
+void first_use P1C(int,f)
+{
+ font_used[f] = 1;
+ fprintf(mpxf, "n%d=\"%s\";\n", font_num[f], texname[f]);
+}
+
+
+/* Print any transformations required by the current Xslant and Xheight
+ settings.
+*/
+void slant_and_ht()
+{
+ int i = 0;
+
+ if (Xslant!=0.0) {
+ fprintf(mpxf," slanted%.5f",Xslant);
+ i++;
+ }
+ if (Xheight!=cursize && Xheight!=0.0 && cursize!=0.0) {
+ fprintf(mpxf," yscaled%.4f", Xheight/cursize);
+ i++;
+ }
+ if (i>0) fprintf(mpxf, "\n ");
+}
+
+
+/* We maintain the invariant that str_f is -1 when there is no output string
+ under construction.
+*/
+void finish_last_char()
+{
+ float m,x,y; /* font scale, MetaPost coords of reference point */
+
+ if (str_f>=0) {
+ m = str_size/font_design_size[str_f];
+ x = str_h1*unit;
+ y = YCORR-str_v*unit;
+ if (fabs(x)>=4096.0 || fabs(y)>=4096.0 || m>=4096.0 || m<0) {
+ warn("text out of range ignored","","");
+ end_char_string(67);
+ }
+ else end_char_string(47);
+ fprintf(mpxf,")infont n%d", font_num[str_f]);
+ slant_and_ht();
+ fprintf(mpxf,",%.5f,%.4f,%.4f);\n", m, x,y);
+ str_f = -1;
+ }
+}
+
+
+/* Output character number c in the font with internal number f.
+*/
+void set_num_char P2C(int,f,int,c)
+{
+ float hh, vv; /* corrected versions of h, v */
+ int i;
+
+ hh = h;
+ vv = v;
+ for (i=shiftbase[f]; shiftchar[i]>=0; i++)
+ if (shiftchar[i]==c) {
+ hh += (cursize/unit)*shifth[i];
+ vv += (cursize/unit)*shiftv[i];
+ break;
+ }
+ if (c==0) quit("attempt to typeset an invalid character","","");
+ if (hh-str_h2>=1.0 || str_h2-hh>=1.0 || vv-str_v>=1.0 || str_v-vv>=1.0
+ || f!=str_f || cursize!=str_size) {
+ if (str_f>=0) finish_last_char();
+ else if (!fonts_used)
+ prepare_font_use(); /* first font usage on this page */
+ if (!font_used[f])
+ first_use(f); /* first use of font f on this page */
+ fprintf(mpxf,"s((");
+ print_col = 3;
+ str_f=f; str_v=vv; str_h1=hh;
+ str_size = cursize;
+ }
+ print_char(c);
+ str_h2 = hh + cursize*charwd[f][c];
+}
+
+/* Output a string. */
+void set_string P1C(char*,cname)
+{
+ float hh; /* corrected version of h, current horisontal position */
+
+ if (!*cname) return;
+ hh = h;
+ set_num_char(curfont,*cname);
+ hh+= cursize*charwd[curfont][*cname];
+ while (*++cname){
+ print_char(*cname);
+ hh += cursize*charwd[curfont][*cname];
+ }
+ h = rint(hh);
+ finish_last_char();
+}
+
+/* The following initialization and clean-up is required.
+*/
+void start_picture()
+{
+ fonts_used = graphics_used = 0;
+ str_f = -1;
+ str_v = 0.0;
+ str_h2 = 0.0;
+ str_size = 0.0;
+ fprintf(mpxf,"begingroup save C,D,p,s,n; picture p; p=nullpicture;\n");
+}
+
+void stop_picture()
+{
+ if (str_f>=0) finish_last_char();
+ fprintf(mpxf,"p endgroup\n");
+}
+
+
+
+/**************************************************************
+ Special Characters
+***************************************************************/
+
+/* Given the troff name of a special character, this routine finds its
+ definition and copies it to the MPX file. It also finds the name of
+ the vardef macro, puts it in the string pool, and index where the
+ string starts. The name should be C.<something>.
+*/
+char specintro[] = "vardef "; /* MetaPost name follows this */
+#define speci 7 /* length of the above string */
+
+int copy_spec_char P1C(char*,cname)
+{
+ int k = 0; /* how much of specintro so far */
+ FILE *deff;
+ int c, s;
+
+ deff = fsearch(concat3("charlib",DIR_SEP_STRING,cname), "", dbpath);
+ while (k<speci) {
+ if ((c=getc(deff))==EOF)
+ quit("No vardef in ",concat3("charlib",DIR_SEP_STRING,cname),"");
+ putc(c, mpxf);
+ if (c==specintro[k]) k++; else k=0;
+ }
+ s = poolsize;
+ while ((c=getc(deff))!='(') {
+ if (c==EOF) quit("vardef in ",concat3("charlib",DIR_SEP_STRING,cname),
+ " has no arguments");
+ putc(c, mpxf);
+ add_to_pool(c);
+ }
+ putc(c, mpxf);
+ add_to_pool('\0');
+ while ((c=getc(deff))!=EOF)
+ putc(c, mpxf);
+ return s;
+}
+
+
+/* When given a character name instead of a number, we need to check if
+ it is a special character and download the definition if necessary.
+ If the character is not in the current font we have to search the special
+ fonts.
+*/
+Hcell *spec_tab = (Hcell*)0;
+
+void set_char P1C(char*,cname)
+{
+ int f, c, *flagp;
+
+ if (*cname==' '||*cname=='\t') return;
+ f = curfont;
+ c = *hfind(cname, charcodes[f]);
+ if (!hfound()) {
+ for (f=specfnt; f!=FCOUNT; f=next_specfnt[f]) {
+ c = *hfind(cname, charcodes[f]);
+ if (hfound()) goto out;
+ }
+ quit("There is no character ",cname,"");
+ }
+out:if (!is_specchar(c)) set_num_char(f,c);
+ else {
+ if (str_f>=0) finish_last_char();
+ if (!fonts_used) prepare_font_use();
+ if (!font_used[f]) first_use(f);
+ if (spec_tab==(Hcell*)0)
+ spec_tab = new_htab;
+ flagp = hfind(cname, spec_tab);
+ if (*flagp==0)
+ *flagp = copy_spec_char(cname); /* this won't be zero */
+ fprintf(mpxf, "s(%s(n%d)", &strpool[*flagp], font_num[f]);
+ slant_and_ht();
+ fprintf(mpxf, ",%.5f,%.4f,%.4f);\n",
+ cursize/font_design_size[f], h*unit, YCORR-v*unit);
+ }
+}
+
+
+
+/**************************************************************
+ Font Definitions
+***************************************************************/
+
+/* Mount the font with troff name nam at external font number n and read any
+ necessary font files.
+*/
+void do_font_def P2C(int,n, char*,nam)
+{
+ int f, k;
+
+ f = *hfind(nam, trfonts);
+ if (!hfound())
+ quit("Font ",nam," was not in map file");
+ if (font_design_size[f]==0) {
+ read_fontdesc(nam);
+ read_tfm(f);
+ }
+ for (k=0; k<nfonts; k++)
+ if (font_num[k]==n) font_num[k]= -1;
+ font_num[f] = n;
+}
+
+
+
+/**************************************************************
+ Time on `makepath pencircle'
+***************************************************************/
+
+#define Speed ((float) (PI/4.0))
+
+/* Given the control points of a cubic Bernstein polynomial, evaluate
+ it at t.
+*/
+float Beval P2C(float*,xx, float, t)
+{
+ float zz[4];
+ register int i, j;
+ for (i=0; i<=3; i++) zz[i]=xx[i];
+ for (i=3; i>0; i--)
+ for (j=0; j<i; j++)
+ zz[j] += t*(zz[j+1]-zz[j]);
+ return zz[0];
+}
+
+
+/* Find the direction angle at time t on the path `makepath pencircle'.
+ The tables below give the Bezier control points for MetaPost's cubic
+ approximation to the first octant of a unit circle.
+*/
+float xx[4] = {1.0, 1.0, 0.8946431597, 0.7071067812};
+float yy[4] = {0.0, 0.2652164899, 0.5195704026, 0.7071067812};
+
+float circangle P1C(float,t)
+{
+ float ti;
+
+ ti = floor(t);
+ t -= ti;
+ return (float) atan(Beval(yy,t)/Beval(xx,t)) + ti*Speed;
+}
+
+
+/* Find the spline parameter where `makepath pencircle' comes closest to
+ (cos(a)/2,sin(a)/2).
+*/
+float circtime P1C(float,a)
+{
+ int i;
+ float t;
+ t = a/Speed;
+ for (i=2; --i>=0;)
+ t += (a - circangle(t))/Speed;
+ return t;
+}
+
+
+
+/**************************************************************
+ Troff Graphics
+***************************************************************/
+
+float gx, gy; /* current point for graphics (init. (h,YCORR/unit-v) */
+
+void prepare_graphics()
+{
+ fprintf(mpxf,"vardef D(expr d)expr q =\n");
+ fprintf(mpxf," addto p doublepath q withpen pencircle scaled d; enddef;\n");
+ graphics_used = 1;
+}
+
+
+/* This function prints the current position (gx,gy). Then if it can read dh dv
+ from string s, it increments (gx,gy) and prints "--". By returning the rest
+ of the string s or NULL if nothing could be read from s, it provides the
+ argument for the next iteration.
+*/
+char *do_line P1C(char*,s)
+{
+ float dh, dv;
+
+ fprintf(mpxf, "(%.3f,%.3f)", gx*unit, gy*unit);
+ dh = get_float(s);
+ dv = get_float(arg_tail);
+ if (arg_tail==NULL) return NULL;
+ gx += dh;
+ gy -= dv;
+ fprintf(mpxf,"--\n");
+ return arg_tail;
+}
+
+
+/* Function spline_seg() reads two pairs of (dh,dv) increments and prints the
+ corresponding quadratic B-spline segment, leaving the ending point to be
+ printed next time. The return value is the string with the first (dh,dv)
+ pair lopped off. If only one pair of increments is found, we prepare to
+ terminate the iteration by printing last time's ending point and returning
+ NULL.
+*/
+char * spline_seg P1C(char*,s)
+{
+ float dh1, dv1, dh2, dv2;
+
+ dh1 = get_float(s);
+ dv1 = get_float(arg_tail);
+ if (arg_tail==NULL) quit("Missing spline increments","","");
+ s = arg_tail;
+ fprintf(mpxf, "(%.3f,%.3f)", (gx+.5*dh1)*unit, (gy-.5*dv1)*unit);
+ gx += dh1;
+ gy -= dv1;
+ dh2 = get_float(s);
+ dv2 = get_float(arg_tail);
+ if (arg_tail==NULL) return NULL;
+ fprintf(mpxf, "..\ncontrols (%.3f,%.3f) and (%.3f,%.3f)..\n",
+ (gx-dh1/6.0)*unit, (gy+dv1/6.0)*unit, (gx+dh2/6.0)*unit,
+ (gy-dv2/6.0)*unit);
+ return s;
+}
+
+
+/* Draw an ellipse with the given major and minor axes.
+*/
+void do_ellipse P2C(float,a, float,b)
+{
+ fprintf(mpxf, "makepath(pencircle xscaled %.3f\n yscaled %.3f",
+ a*unit, b*unit);
+ fprintf(mpxf, " shifted (%.3f,%.3f));\n", (gx+.5*a)*unit, gy*unit);
+ gx += a;
+}
+
+
+/* Draw a counter-clockwise arc centered at (cx,cy) with initial and final radii
+ (ax,ay) and (bx,by) respectively.
+*/
+void do_arc P6C(float,cx, float,cy, float,ax, float,ay, float,bx, float,by)
+{
+ float t1, t2;
+
+ t1 = circtime(atan2(ay, ax));
+ t2 = circtime(atan2(by, bx));
+ if (t2 < t1) t2 += 8.0;
+ fprintf(mpxf, "subpath (%.5f,%.5f) of\n", t1, t2);
+ fprintf(mpxf, " makepath(pencircle scaled %.3f shifted (%.3f,%.3f));\n",
+ 2.0*sqrt(ax*ax+ay*ay)*unit, cx*unit, cy*unit);
+ gx = cx + bx;
+ gy = cy + by;
+}
+
+
+
+/* string s is everything following the initial `D' in a troff graphics command.
+*/
+void do_graphic P1C(char*,s)
+{
+ float h1, v1, h2, v2;
+ finish_last_char();
+ /* GROFF uses Fd to set fill color for solid drawing objects to the
+ default, so just ignore that.
+ */
+ if (s[0] == 'F' && s[1] == 'd') return;
+ gx = (float) h;
+ gy = YCORR/unit - ((float) v);
+ if (!graphics_used) prepare_graphics();
+ fprintf(mpxf,"D(%.4f) ", LWscale*cursize);
+ switch (*s++) {
+ case 'c':
+ h1 = get_float(s);
+ if (arg_tail==NULL) quit("Bad argument in ",s-2,"");
+ do_ellipse(h1,h1);
+ break;
+ case 'e':
+ h1 = get_float(s);
+ v1 = get_float(arg_tail);
+ if (arg_tail==NULL) quit("Bad argument in ",s-2,"");
+ do_ellipse(h1,v1);
+ break;
+ case 'A':
+ fprintf(mpxf, "reverse ");
+ /* fall through */
+ case 'a':
+ h1 = get_float(s);
+ v1 = get_float(arg_tail);
+ h2 = get_float(arg_tail);
+ v2 = get_float(arg_tail);
+ if (arg_tail==NULL) quit("Bad argument in ",s-2,"");
+ do_arc(gx+h1, gy-v1, -h1, v1, h2, -v2);
+ break;
+ case 'l': case 'p':
+ while (s!=NULL)
+ s = do_line(s);
+ fprintf(mpxf, ";\n");
+ break;
+ case 'q':
+ do s = spline_seg(s); while (s!=NULL);
+ fprintf(mpxf, ";\n");
+ break;
+ case '~':
+ fprintf(mpxf, "(%.3f,%.3f)--", gx*unit, gy*unit);
+ do s = spline_seg(s); while (s!=NULL);
+ fprintf(mpxf, "--(%.3f,%.3f);\n", gx*unit, gy*unit);
+ break;
+ default:
+ quit("Unknown drawing function",s-2,"");
+ }
+ h = (int) floor(gx+.5);
+ v = (int) floor(YCORR/unit+.5-gy);
+}
+
+
+
+/**************************************************************
+ Interpreting Troff Output
+***************************************************************/
+
+void change_font P1C(int,f)
+{
+ for (curfont=0; curfont<nfonts; curfont++)
+ if (font_num[curfont]==f) return;
+ quit("Bad font setting","","");
+}
+
+
+/* String s0 is everything following the initial `x' in a troff device control
+ command. A zero result indicates a stop command.
+*/
+int do_x_cmd P1C(char *,s0)
+{
+ float x;
+ int n;
+ char *s;
+
+ s = s0;
+ while (*s==' ' || *s=='\t') s++;
+ switch (*s++) {
+ case 'r':
+ if (unit!=0.0) quit("Attempt to reset resolution","","");
+ while (*s!=' ' && *s!='\t') s++;
+ unit = get_float(s);
+ if (unit<=0.0) quit("Bad resolution: x",s0,"");
+ unit = 72.0/unit;
+ break;
+ case 'f':
+ while (*s!=' ' && *s!='\t') s++;
+ n = get_int(s);
+ if (arg_tail==NULL) quit("Bad font def: x",s0,"");
+ s = arg_tail;
+ while (*s==' ' || *s=='\t') s++;
+ do_font_def(n, s);
+ break;
+ case 's':
+ return 0;
+ case 'H':
+ while (*s!=' ' && *s!='\t') s++;
+ Xheight = get_float(s);
+ /* GROFF troff output is scaled
+ groff_out(5): The argument to the s command is in scaled
+ points (units of points/n, where n is the argument
+ to the sizescale command in the DESC file.) The
+ argument to the x Height command is also in scaled points.
+ sizescale for groff devps is 1000
+ */
+ if(unit != 0.0) Xheight *= unit;
+ else Xheight /= 1000.0;
+ if (Xheight==cursize) Xheight=0.0;
+ break;
+ case 'S':
+ while (*s!=' ' && *s!='\t') s++;
+ Xslant = get_float(s)*(PI/180.0);
+ x = cos(Xslant);
+ if (-1e-4<x && x<1e-4) quit("Excessive slant","","");
+ Xslant = sin(Xslant)/x;
+ break;
+ default:
+ /* do nothing */;
+ }
+ return 1;
+}
+
+
+/* This routine reads commands from the troff output file up to and including
+ the next `p' or `x s' command. It also calls set_num_char() and set_char()
+ to generate output when appropriate. A zero result indicates that there
+ are no more pages to do.
+*/
+/* GROFF NOTE:
+ GNU groff uses an extended device-independent output file format
+ documented in groff_out(5). In order to allow parsing of groff's
+ output files, this function either needs to be extended to support
+ the new command codes, or else the use of the "t" and "u" commands
+ must be disabled by removing the line "tcommand" from the DESC file
+ in the $(prefix)/lib/groff/devps directory.
+*/
+int do_page()
+{
+ char buf[LLENGTH];
+ char a, *c, *cc;
+
+ h = v = 0;
+ while (fgets(buf,LLENGTH,trf)!=NULL) {
+ for (c=buf; *c!='\n'; c++)
+ if (*c=='\0') quit("Need to increase LLENGTH","","");
+ *c = '\0';
+ lnno++;
+ c = buf;
+ while (*c!='\0') {
+ switch(*c) {
+ case ' ': case '\t': case 'w':
+ c++;
+ break;
+ case 's':
+ cursize = get_float(c+1);
+ /* GROFF troff output is scaled
+ groff_out(5): The argument to the s command is in scaled
+ points (units of points/n, where n is the argument
+ to the sizescale command in the DESC file.) The
+ argument to the x Height command is also in scaled
+ points.
+ sizescale for groff devps is 1000
+ */
+ if (unit != 0.0) cursize *= unit;
+ else cursize /= 1000.0;
+ goto iarg;
+ case 'f':
+ change_font(get_int(c+1));
+ goto iarg;
+ case 'c':
+ if (c[1]=='\0') quit("Bad c command in troff output","","");
+ cc = c+2;
+ goto set;
+ case 'C':
+ cc=c; do cc++; while (*cc!=' ' && *cc!='\t' && *cc!='\0');
+ goto set;
+ case 'N':
+ set_num_char(curfont, get_int(c+1));
+ goto iarg;
+ case 'H':
+ h = get_int(c+1);
+ goto iarg;
+ case 'V':
+ v = get_int(c+1);
+ goto iarg;
+ case 'h':
+ h += get_int(c+1);
+ goto iarg;
+ case 'v':
+ v += get_int(c+1);
+ goto iarg;
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ if (c[1]<'0' || c[1]>'9' || c[2]=='\0')
+ quit("Bad nnc command in troff output","","");
+ h += 10*(c[0]-'0') + c[1]-'0';
+ c++;
+ cc = c+2;
+ goto set;
+ case 'p':
+ return 1;
+ case 'n':
+ (void) get_int(c+1);
+ (void) get_int(arg_tail);
+ goto iarg;
+ case 'D':
+ do_graphic(c+1);
+ goto eoln;
+ case 'x':
+ if (!do_x_cmd(c+1)) return 0;
+ goto eoln;
+ case '#':
+ goto eoln;
+ case 'F':
+ /* GROFF uses this command to report filename */
+ goto eoln;
+ case 'm':
+ /* GROFF uses this command to control color */
+ goto eoln;
+ case 'u':
+ /* GROFF uses this command to output a word with additional
+ white space between characters, not implemented
+ */
+ quit("Bad command in troff output\n",
+ "change the DESC file for your GROFF PostScript device, ",
+ "remove tcommand");
+ case 't':
+ /* GROFF uses this command to output a word */
+ cc=c; do cc++; while (*cc!=' ' && *cc!='\t' && *cc!='\0');
+ a= *cc; *cc='\0';
+ set_string(++c);
+ c = cc;
+ *c = a;
+ continue;
+ default:
+ quit("Bad command in troff output","","");
+ }
+ continue;
+ set:a= *cc; *cc='\0';
+ set_char(++c);
+ c = cc;
+ *c = a;
+ continue;
+ iarg:c = arg_tail;
+ }
+ eoln:/* do nothing */;
+ }
+ return 0;
+}
+
+
+/**************************************************************
+ Main Program
+***************************************************************/
+
+void dmp_usage P2C(char*,name, int,status)
+{
+ extern KPSEDLL char *kpse_bug_address;
+ FILE *f = status == 0 ? stdout : stderr;
+ fputs ("Usage: dmp [OPTION]... DITROFFFILE [MPXFILE]\n\
+ Translate DITROFFFILE to the MetaPost MPXFILE or standard output.\n\
+\n\
+--help display this help and exit\n\
+--version output version information and exit\n", f);
+ putc ('\n', f);
+ fputs ("Email bug reports to metapost@tug.org.\n", f);
+ exit(status);
+}
+
+int main P2C(int, argc, char**, argv)
+{
+ int more;
+
+ trf = stdin;
+ mpxf = stdout;
+
+ kpse_set_progname (argv[0]);
+
+ if (argc == 1) {
+ fputs ("dmp: Need one or two file arguments.\n", stderr);
+ fputs ("Try `dmp --help' for more information.\n", stderr);
+ exit(1);
+ } else if (argc > 1 && strcmp (argv[1], "--help") == 0) {
+ dmp_usage (argv[0], 0);
+ } else if (argc > 1 && strcmp (argv[1], "--version") == 0) {
+ printversionandexit (term_banner,
+ "AT&T Bell Laboratories", "John Hobby");
+ }
+ if (argc>3) dmp_usage(argv[0], 1);
+ if (argc>1) {
+ trf = fopen(argv[1], "r");
+ if (trf==(FILE*)0) {
+ fprintf (stderr, "%s: ", argv[0]);
+ perror (argv[1]);
+ exit(1);
+ }
+ if (argc>2) {
+ mpxf = fopen(argv[2], "w");
+ if (mpxf==(FILE*)0) {
+ fprintf (stderr, "%s: ", argv[0]);
+ perror (argv[2]);
+ exit(1);
+ }
+ }
+ }
+ fprintf(mpxf, "%s\n", banner);
+ read_fmap(dbname);
+ read_char_adj(adjname);
+ if (do_page()) {
+ do {
+ h=0; v=0;
+ Xslant = Xheight = 0.0;
+ start_picture();
+ more = do_page();
+ stop_picture();
+ fprintf(mpxf,"mpxbreak\n");
+ } while (more);
+ }
+ return 0;
+}