1*34114Skarels #ifndef lint
2*34114Skarels static char Notice[] = "Copyright (c) 1985 Adobe Systems Incorporated";
3*34114Skarels static char *RCSID="$Header: pscatmap.c,v 2.1 85/11/24 11:50:07 shore Rel $";
4*34114Skarels #endif
5*34114Skarels /* pscatmap.c
6*34114Skarels  *
7*34114Skarels  * Copyright (C) 1985 Adobe Systems Incorporated
8*34114Skarels  *
9*34114Skarels  * Build font width files for troff and correspondence tables
10*34114Skarels  * for pscat.
11*34114Skarels  *
12*34114Skarels  * Edit History:
13*34114Skarels  * Andrew Shore: Sat Nov  9 15:37:04 1985
14*34114Skarels  * End Edit History.
15*34114Skarels  *
16*34114Skarels  * The correspondence tables are intended to be created by humans for
17*34114Skarels  * describing a set of 4 fonts to be used by troff: detailing the
18*34114Skarels  * mapping from troff & C/A/T codes to output actions for pscat
19*34114Skarels  * These actions take one of three forms:
20*34114Skarels  *	PFONT	- map to a character in a PostScript font
21*34114Skarels  *	PLIG	- map to a "fake" ligature
22*34114Skarels  *	PPROC	- map to a named PostScript procedure
23*34114Skarels  *	PNONE	- no action
24*34114Skarels  *
25*34114Skarels  * PFONT is straightforward, the mapping specifies which character in
26*34114Skarels  * which PostScript font is desired and the character width is
27*34114Skarels  * collected from the PostScript "afm" font metrics file format.
28*34114Skarels  * Note that ascender and descender information is "wired in" to the code;
29*34114Skarels  * it might be a better idea to "compute" it from the character
30*34114Skarels  * bounding box information.
31*34114Skarels  *
32*34114Skarels  * PLIG is provided so that the correct width for the ligature may
33*34114Skarels  * be "computed" by this program, rather than hand done by the user.
34*34114Skarels  * There are 3 ligature types:
35*34114Skarels  *	0126	- build "ff" out of "f" and "f"
36*34114Skarels  *	0131	- build "ffi" out of "f" and "fi"
37*34114Skarels  *	0130	- build "ffl" out of "f" and "fl"
38*34114Skarels  * This list should probably be expanded to span the space.
39*34114Skarels  *
40*34114Skarels  * PPROC provides a general callback mechanism so that users can
41*34114Skarels  * create and character definition with a PostScript procedure.
42*34114Skarels  * The procedures are "named" with user-specified numbers, and are
43*34114Skarels  * called with all information available to them (current position,
44*34114Skarels  * font size, intended width, railmag, ...).  Such procedures are used
45*34114Skarels  * for troff characters not in any PostScript font (rules, boxes,
46*34114Skarels  * constructed braces, etc.), but may also be used as a general
47*34114Skarels  * "escape hatch" for incorporating ANY PostScript routine into a
48*34114Skarels  * troff document -- a logo, a scanned image, etc.  The exact
49*34114Skarels  * calling rules are:
50*34114Skarels  *	an absolute moveto is performed to the position for this character
51*34114Skarels  *	The following numbers are pushed on the PS stack:
52*34114Skarels  *	pointsize
53*34114Skarels  *	troff character code
54*34114Skarels  *	"railmag"
55*34114Skarels  *	character width
56*34114Skarels  *	procedure "number"
57*34114Skarels  *	x offset
58*34114Skarels  *	y offset
59*34114Skarels  *	width
60*34114Skarels  *
61*34114Skarels  *	then a procedure named "PSn" where n is the procedure number
62*34114Skarels  *	is executed.  It is that procedure's responsibility to image
63*34114Skarels  *	the character and move by the appropriate width.
64*34114Skarels  *
65*34114Skarels  * RCSLOG:
66*34114Skarels  * $Log:	pscatmap.c,v $
67*34114Skarels  * Revision 2.1  85/11/24  11:50:07  shore
68*34114Skarels  * Product Release 2.0
69*34114Skarels  *
70*34114Skarels  * Revision 1.3  85/11/20  00:32:45  shore
71*34114Skarels  * support for System V
72*34114Skarels  * short names, non ".c" output, better arith.
73*34114Skarels  *
74*34114Skarels  * Revision 1.2  85/05/14  11:24:04  shore
75*34114Skarels  *
76*34114Skarels  *
77*34114Skarels  *
78*34114Skarels  */
79*34114Skarels 
80*34114Skarels #include <stdio.h>
81*34114Skarels #ifdef SYSV
82*34114Skarels #include <string.h>
83*34114Skarels #else
84*34114Skarels #include <strings.h>
85*34114Skarels #endif
86*34114Skarels #include "transcript.h"
87*34114Skarels #include "action.h"
88*34114Skarels 
89*34114Skarels #define LFONT 1
90*34114Skarels #define LMAP 2
91*34114Skarels #define LNONE 3
92*34114Skarels 
93*34114Skarels private char linebuf[200];
94*34114Skarels private char *libdir;		/* place for AFM files */
95*34114Skarels 
96*34114Skarels private struct chAction chAction [] = {
97*34114Skarels     PFONT,	"PFONT",	/* character in PostScript font */
98*34114Skarels     PLIG,	"PLIG",		/* fake ligatures: ff ffi ffl */
99*34114Skarels     PPROC,	"PPROC",	/* PostScript procedure outcall */
100*34114Skarels     PNONE,	"0",		/* null action */
101*34114Skarels     0,		0
102*34114Skarels };
103*34114Skarels 
104*34114Skarels #define MAXFONTS 25
105*34114Skarels private int nfonts;
106*34114Skarels private struct font {
107*34114Skarels     char mapname[20];	/* short name in map file e.g., "ROMAN" */
108*34114Skarels     char afmname[80];	/* name of AFM file for this font (in map file) */
109*34114Skarels     char psname[80];	/* PS font name from AFM file */
110*34114Skarels     int  widths[256];   /* PostScript widths of encoded chars (1000/em) */
111*34114Skarels     /* more here */
112*34114Skarels } fonts[MAXFONTS];
113*34114Skarels private char	faces[] = "RIBS0"; /* order important! */
114*34114Skarels 
115*34114Skarels private char	familyname[80];
116*34114Skarels private char	facenames[4][80];
117*34114Skarels 
118*34114Skarels /* there should be on the order of 4 * 128 characters, so 1000 is generous */
119*34114Skarels 
120*34114Skarels #define MAXCHARS 1000
121*34114Skarels private struct ctab {
122*34114Skarels     int		trcode;			/* troff char code */
123*34114Skarels     int		catcode;		/* CAT char code */
124*34114Skarels     int		wid;			/* table-driven width */
125*34114Skarels     int		action;			/* action type */
126*34114Skarels     int		x, y;			/* x and y offset */
127*34114Skarels     int		font;			/* PostScript font */
128*34114Skarels     int		pschar;			/* PS character code */
129*34114Skarels     int		pswidth;		/* PS width (/1000) */
130*34114Skarels     char 	*descr;			/* short print description */
131*34114Skarels } ctab[MAXCHARS];
132*34114Skarels private int	nchars;
133*34114Skarels 
134*34114Skarels 
135*34114Skarels private BuildTable()
136*34114Skarels {
137*34114Skarels     char *c;
138*34114Skarels     int status;
139*34114Skarels     int gotfamily, gotfaces, gotfonts, gotmap;
140*34114Skarels 
141*34114Skarels     gotfamily = gotfaces = gotfonts = gotmap = 0;
142*34114Skarels 
143*34114Skarels     while(fgets(linebuf, sizeof linebuf, stdin) != NULL) {
144*34114Skarels 	/* strip off newline */
145*34114Skarels 	if ((c = INDEX(linebuf, '\n')) == 0) {
146*34114Skarels 	    fprintf(stderr, "line too long \"%s\"\n",linebuf);
147*34114Skarels 	    exit(1);
148*34114Skarels 	}
149*34114Skarels 	*c = '\0';
150*34114Skarels 	/* ignore blank or comment (%) lines */
151*34114Skarels 	if ((*linebuf == '%') || (*linebuf == '\0')) continue;
152*34114Skarels 	if (*linebuf == '@') {
153*34114Skarels 	    /* "command" line */
154*34114Skarels 	    /* printf("C: %s\n", linebuf); */
155*34114Skarels 	    if (strncmp(&linebuf[1], "FAMILYNAME", 10) == 0) {
156*34114Skarels 		if (sscanf(linebuf,"@FAMILYNAME %s",familyname) != 1) {
157*34114Skarels 		    fprintf(stderr,"bad familyname %s\n",familyname);
158*34114Skarels 		    exit(1);
159*34114Skarels 		}
160*34114Skarels 		gotfamily++;
161*34114Skarels 	    }
162*34114Skarels 	    if (strncmp(&linebuf[1], "FACENAMES", 9) == 0) {
163*34114Skarels 		if (sscanf(linebuf,"@FACENAMES %s %s %s %s",
164*34114Skarels 			facenames[0],facenames[1],
165*34114Skarels 			facenames[2],facenames[3]) != 4) {
166*34114Skarels 		    fprintf(stderr,"must be four facenames\n");
167*34114Skarels 		    exit(1);
168*34114Skarels 		}
169*34114Skarels 		gotfaces++;
170*34114Skarels 	    }
171*34114Skarels 	    if (strcmp(&linebuf[1], "BEGINFONTS") == 0) {
172*34114Skarels 		if ((!gotfamily) || (!gotfaces)) {
173*34114Skarels 		    fprintf(stderr,"FAMILYNAME and FACENAMES must come before BEGINFONTS\n");
174*34114Skarels 		    exit(1);
175*34114Skarels 		}
176*34114Skarels 		VOIDC strcpy(fonts[0].mapname,"0");
177*34114Skarels 		VOIDC strcpy(fonts[0].afmname,"0");
178*34114Skarels 		VOIDC strcpy(fonts[0].psname,"0");
179*34114Skarels 		nfonts = 1;
180*34114Skarels 		status = LFONT;
181*34114Skarels 		continue;
182*34114Skarels 	    }
183*34114Skarels 	    if (strcmp(&linebuf[1], "ENDFONTS") == 0) {
184*34114Skarels 		gotfonts++;
185*34114Skarels 		status = LNONE;
186*34114Skarels 		continue;
187*34114Skarels 	    }
188*34114Skarels 	    if (strcmp(&linebuf[1], "BEGINMAP") == 0) {
189*34114Skarels 		if (!gotfonts) {
190*34114Skarels 		    fprintf(stderr,"BEGINFONTS/ENDFONTS must come before map\n");
191*34114Skarels 		    exit(1);
192*34114Skarels 		}
193*34114Skarels 		status = LMAP;
194*34114Skarels 		continue;
195*34114Skarels 	    }
196*34114Skarels 	    if (strcmp(&linebuf[1], "ENDMAP") == 0) {
197*34114Skarels 		status = LNONE;
198*34114Skarels 		gotmap++;
199*34114Skarels 		continue;
200*34114Skarels 	    }
201*34114Skarels 	}
202*34114Skarels 	switch (status) {
203*34114Skarels 	    case LFONT:
204*34114Skarels 		GetFont();
205*34114Skarels 		break;
206*34114Skarels 	    case LMAP:
207*34114Skarels 		GetMap();
208*34114Skarels 		break;
209*34114Skarels 	    default:
210*34114Skarels 		break;
211*34114Skarels 	}
212*34114Skarels     }
213*34114Skarels     if (!gotmap) {
214*34114Skarels 	fprintf(stderr,"missing @ENDMAP\n");
215*34114Skarels 	exit(1);
216*34114Skarels     }
217*34114Skarels }
218*34114Skarels 
219*34114Skarels private GetFont () {
220*34114Skarels     char   *eqp;
221*34114Skarels     register int    i;
222*34114Skarels     char   afmpath[200];
223*34114Skarels #ifdef SYSV
224*34114Skarels     char   shortname[40];
225*34114Skarels #endif
226*34114Skarels 
227*34114Skarels     if ((eqp = INDEX(linebuf, '=')) == 0) {
228*34114Skarels 	fprintf(stderr, "bad FONTS line:%s\n",linebuf);
229*34114Skarels 	exit(1);
230*34114Skarels     }
231*34114Skarels     *eqp++ = '\0';
232*34114Skarels     for (i = 0; i < nfonts; i++) {
233*34114Skarels 	if (strcmp (fonts[i].mapname, linebuf) == 0) {
234*34114Skarels 	    fprintf(stderr, "duplicate entry for font %s\n", linebuf);
235*34114Skarels 	    exit(1);
236*34114Skarels 	}
237*34114Skarels     }
238*34114Skarels     if (nfonts >= MAXFONTS) {
239*34114Skarels 	fprintf(stderr, "Too many FONTS\n");
240*34114Skarels 	exit(1);
241*34114Skarels     }
242*34114Skarels     if (strlen(linebuf) > (sizeof fonts[0].mapname)) {
243*34114Skarels 	fprintf(stderr, "FONT name too long %s\n", linebuf);
244*34114Skarels 	exit(1);
245*34114Skarels     }
246*34114Skarels     if (strlen(eqp) > (sizeof fonts[0].afmname)) {
247*34114Skarels 	fprintf(stderr, "FONT name too long %s\n", eqp);
248*34114Skarels 	exit(1);
249*34114Skarels     }
250*34114Skarels     VOIDC strcpy(fonts[nfonts].mapname, linebuf);
251*34114Skarels     VOIDC strcpy(fonts[nfonts].afmname, eqp);
252*34114Skarels 
253*34114Skarels     /* read the font's .afm file to get real widths */
254*34114Skarels     if (*eqp == '/') {
255*34114Skarels 	VOIDC strcpy(afmpath,eqp);
256*34114Skarels     }
257*34114Skarels     else {
258*34114Skarels 	VOIDC strcpy(afmpath,libdir);
259*34114Skarels 	VOIDC strcat(afmpath,"/");
260*34114Skarels #ifdef SYSV
261*34114Skarels 	mapname(eqp,shortname);
262*34114Skarels 	VOIDC strcat(afmpath,shortname);
263*34114Skarels #else
264*34114Skarels 	VOIDC strcat(afmpath, eqp);
265*34114Skarels #endif
266*34114Skarels 	VOIDC strcat(afmpath,".afm");
267*34114Skarels     }
268*34114Skarels     ReadAFM(afmpath);
269*34114Skarels     nfonts++;
270*34114Skarels     return;
271*34114Skarels }
272*34114Skarels 
273*34114Skarels 
274*34114Skarels private ReadAFM(afmfile) char *afmfile; {
275*34114Skarels     char *c;
276*34114Skarels     FILE *afm;
277*34114Skarels     int gotMetrics, gotName, inChars, ccode, cwidth;
278*34114Skarels     char afmbuf[1000];
279*34114Skarels 
280*34114Skarels     if ((afm = fopen(afmfile, "r")) == NULL) {
281*34114Skarels 	fprintf(stderr,"Can't open afm file %s\n",afmfile);
282*34114Skarels 	exit(1);
283*34114Skarels     }
284*34114Skarels     inChars = gotMetrics = gotName = 0;
285*34114Skarels     while(fgets(afmbuf, sizeof afmbuf, afm) != NULL) {
286*34114Skarels 	/* strip off newline */
287*34114Skarels 	if ((c = INDEX(afmbuf, '\n')) == 0) {
288*34114Skarels 	    fprintf(stderr, "AFM line too long %s\n", afmbuf);
289*34114Skarels 	    exit(1);
290*34114Skarels 	}
291*34114Skarels 	*c = '\0';
292*34114Skarels 	/* ignore blank lines */
293*34114Skarels 	if (*afmbuf == '\0') continue;
294*34114Skarels 	if (strcmp(afmbuf,"StartFontMetrics 1.0") == 0) {
295*34114Skarels 	    gotMetrics++;
296*34114Skarels 	    continue;
297*34114Skarels 	}
298*34114Skarels 	if (strncmp(afmbuf,"FontName ", 9) == 0) {
299*34114Skarels 	    VOIDC sscanf(afmbuf,"FontName %s",fonts[nfonts].psname);
300*34114Skarels 	    gotName++;
301*34114Skarels 	    continue;
302*34114Skarels 	}
303*34114Skarels 	if (strcmp(afmbuf,"EndCharMetrics") == 0) {
304*34114Skarels 	    if (!inChars) {
305*34114Skarels 		fprintf(stderr,"AFM: %s without StartCharMetrics\n",afmbuf);
306*34114Skarels 		exit(1);
307*34114Skarels 	    }
308*34114Skarels 	    inChars++;
309*34114Skarels 	    break;
310*34114Skarels 	}
311*34114Skarels 	if (strcmp(afmbuf,"StartCharMetrics") == 0) {
312*34114Skarels 	    inChars++;
313*34114Skarels 	    continue;
314*34114Skarels 	}
315*34114Skarels 	if (inChars == 1) {
316*34114Skarels 	    if (sscanf(afmbuf,"C %d ; WX %d ;",&ccode, &cwidth) != 2) {
317*34114Skarels 		fprintf(stderr, "Bad Character in AFM file %s\n",afmbuf);
318*34114Skarels 		exit(1);
319*34114Skarels 	    }
320*34114Skarels 	    if (ccode == -1) continue; /* skip unencoded chars */
321*34114Skarels 	    if (ccode > 255) {
322*34114Skarels 		fprintf(stderr, "Bad Character Code skipped %s\n", afmbuf);
323*34114Skarels 		continue;
324*34114Skarels 	    }
325*34114Skarels 	    fonts[nfonts].widths[ccode] = cwidth;
326*34114Skarels 	    continue;
327*34114Skarels 	}
328*34114Skarels     }
329*34114Skarels     if ((inChars != 2) || (!gotMetrics) || (!gotName)) {
330*34114Skarels 	fprintf(stderr,"improper AFM file %s\n",afmfile);
331*34114Skarels 	exit(1);
332*34114Skarels     }
333*34114Skarels     VOIDC fclose(afm);
334*34114Skarels }
335*34114Skarels 
336*34114Skarels 
337*34114Skarels private GetMap(){
338*34114Skarels     int		trcode;
339*34114Skarels     char	trfont;
340*34114Skarels     int		catcode;
341*34114Skarels     int		wid;
342*34114Skarels     char	action[10];
343*34114Skarels     int		x,y;
344*34114Skarels     char	psfont[20];
345*34114Skarels     int		pschar;
346*34114Skarels     char	descr[100];
347*34114Skarels 
348*34114Skarels     char	*fp;
349*34114Skarels     int		trface;  /* 0 - 3  : R I B S */
350*34114Skarels     int		pf;
351*34114Skarels     struct ctab *ch;
352*34114Skarels     struct chAction *act;
353*34114Skarels 
354*34114Skarels     if (sscanf(linebuf,"%o %c %o %d %s %d %d %s %o \"%[^\"]\"",
355*34114Skarels         &trcode, &trfont, &catcode, &wid, action, &x, &y,
356*34114Skarels 	psfont, &pschar, descr) != 10) {
357*34114Skarels 	fprintf(stderr,"Bad line %s",linebuf);
358*34114Skarels     }
359*34114Skarels 
360*34114Skarels     /* verify the integrity of the data we got */
361*34114Skarels     if ((fp = INDEX(faces, trfont)) == 0) {
362*34114Skarels 	fprintf(stderr, "Bad face code in %s\n", linebuf);
363*34114Skarels 	exit(1);
364*34114Skarels     }
365*34114Skarels     trface = fp - faces;
366*34114Skarels     for (act = chAction; act->actName != 0; act++) {
367*34114Skarels 	if (strcmp(action, act->actName) == 0) break;
368*34114Skarels     }
369*34114Skarels     if (act->actName == 0) {
370*34114Skarels 	fprintf(stderr, "Bad action in %s\n", linebuf);
371*34114Skarels 	exit(1);
372*34114Skarels     }
373*34114Skarels     for (pf = 0; pf < nfonts; pf++) {
374*34114Skarels 	if (strcmp(fonts[pf].mapname, psfont) == 0) goto gotfont;
375*34114Skarels     }
376*34114Skarels     fprintf(stderr, "Bad font (%s) name %s\n", linebuf, psfont);
377*34114Skarels     exit(1);
378*34114Skarels 
379*34114Skarels     gotfont:
380*34114Skarels 
381*34114Skarels     if (nchars >= MAXCHARS) {
382*34114Skarels 	fprintf(stderr,"Too many character definitions\n");
383*34114Skarels 	exit(1);
384*34114Skarels     }
385*34114Skarels     ch = &ctab[nchars];
386*34114Skarels     ch->trcode = trcode;
387*34114Skarels     ch->catcode = (trface << 7) | catcode;
388*34114Skarels     ch->action = act->actCode;
389*34114Skarels     ch->x = x;
390*34114Skarels     ch->y = y;
391*34114Skarels     ch->font = pf;
392*34114Skarels     ch->pschar = pschar;
393*34114Skarels     if (descr[0]) {
394*34114Skarels 	if ((ch->descr = malloc((unsigned) (strlen(descr)+1))) == NULL) {
395*34114Skarels 	    fprintf(stderr,"malloc failed\n");
396*34114Skarels 	    exit(1);
397*34114Skarels 	}
398*34114Skarels 	VOIDC strcpy(ch->descr, descr);
399*34114Skarels     }
400*34114Skarels 
401*34114Skarels     /* calculate width in cat units (432 per inch) of 6 pt
402*34114Skarels      * character (the way troff wants them).
403*34114Skarels      * 6 pts = 36 cat units
404*34114Skarels      */
405*34114Skarels 
406*34114Skarels     if (ch->action == PLIG) {/* fake ligature, fake width */
407*34114Skarels 	switch (catcode) {
408*34114Skarels 	    case 0126:	/* ff = f f */
409*34114Skarels 		ch->pswidth = fonts[pf].widths['f'] * 2;
410*34114Skarels 		break;
411*34114Skarels 	    case 0131: /* ffi = f fi */
412*34114Skarels 		ch->pswidth = fonts[pf].widths['f'] + fonts[pf].widths[0256];
413*34114Skarels 		break;
414*34114Skarels 	    case 0130: /* ffl = f fl */
415*34114Skarels 	    	ch->pswidth = fonts[pf].widths['f'] + fonts[pf].widths[0257];
416*34114Skarels 		break;
417*34114Skarels 	    default:
418*34114Skarels 		fprintf(stderr,"Unknown ligature 0%o\n",catcode);
419*34114Skarels 		exit(1);
420*34114Skarels 	}
421*34114Skarels     }
422*34114Skarels     else {
423*34114Skarels 	ch->pswidth = fonts[pf].widths[pschar];
424*34114Skarels     }
425*34114Skarels     ch->wid = (wid >= 0) ? wid :
426*34114Skarels     	(int) ( (((float) ch->pswidth * 36.0 ) / 1000.0) + 0.5);
427*34114Skarels     if (ch->wid > 255) {
428*34114Skarels 	fprintf(stderr,"Scaled width too big!\n");
429*34114Skarels 	exit(1);
430*34114Skarels     }
431*34114Skarels     nchars++;
432*34114Skarels }
433*34114Skarels 
434*34114Skarels 
435*34114Skarels private int compCTent(a,b)
436*34114Skarels struct ctab *a, *b;
437*34114Skarels {
438*34114Skarels     if (a->catcode < b->catcode) return -1;
439*34114Skarels     if (a->catcode > b->catcode) return 1;
440*34114Skarels     return 0;
441*34114Skarels }
442*34114Skarels 
443*34114Skarels 
444*34114Skarels private WriteTable() {
445*34114Skarels     int	i, curcode;
446*34114Skarels     struct ctab *ct;
447*34114Skarels     struct map map, emptymap;
448*34114Skarels     char outname[84];
449*34114Skarels     FILE *mapfile;
450*34114Skarels 
451*34114Skarels     /* write out font mapping */
452*34114Skarels     if (familyname[0] == 0) {
453*34114Skarels 	fprintf(stderr,"No FAMILYNAME specified!\n");
454*34114Skarels 	exit(1);
455*34114Skarels     }
456*34114Skarels     VOIDC strcpy(outname, familyname);
457*34114Skarels     VOIDC strcat(outname, ".ct");
458*34114Skarels     if ((mapfile = fopen(outname, "w")) == NULL) {
459*34114Skarels 	fprintf(stderr,"can't open output file %s\n", mapfile);
460*34114Skarels 	exit(1);
461*34114Skarels     }
462*34114Skarels     VOIDC putw(nfonts, mapfile);
463*34114Skarels     for (i = 0; i < nfonts; i++) {
464*34114Skarels 	VOIDC fwrite(fonts[i].psname, sizeof fonts[0].psname, 1, mapfile);
465*34114Skarels     }
466*34114Skarels     /* sort ctab by catcode */
467*34114Skarels     qsort((char *) ctab, nchars, sizeof (struct ctab), compCTent);
468*34114Skarels     /* write it out */
469*34114Skarels     VOIDC putw(ctab[nchars-1].catcode,mapfile);
470*34114Skarels     VOIDC fflush(mapfile);
471*34114Skarels     emptymap.wid = emptymap.x = emptymap.y = emptymap.font =
472*34114Skarels     emptymap.pschar = emptymap.action = emptymap.pswidth = 0;
473*34114Skarels 
474*34114Skarels     ct = &ctab[0];
475*34114Skarels     curcode = 0;
476*34114Skarels     for (i = 0; i < MAXCHARS; i++) {
477*34114Skarels 	while (curcode < ct->catcode) {
478*34114Skarels 	    VOIDC write(fileno(mapfile), &emptymap, sizeof map);
479*34114Skarels 	    curcode++;
480*34114Skarels 	}
481*34114Skarels 	while ((ct->catcode < curcode) && (ct <= &ctab[nchars])) {
482*34114Skarels 	    ct++;
483*34114Skarels 	    i++;
484*34114Skarels 	}
485*34114Skarels 	if (ct >= &ctab[nchars]) break;
486*34114Skarels 	map.wid = ct->wid;
487*34114Skarels 	map.pswidth = ct->pswidth;
488*34114Skarels 	map.x = ct->x;
489*34114Skarels 	map.y = ct->y;
490*34114Skarels 	map.action = ct->action;
491*34114Skarels 	map.pschar = ct->pschar;
492*34114Skarels 	map.font = ct->font;
493*34114Skarels 	VOIDC write(fileno(mapfile), &map, sizeof map);
494*34114Skarels 	ct++;
495*34114Skarels 	curcode++;
496*34114Skarels     }
497*34114Skarels     VOIDC fclose(mapfile);
498*34114Skarels }
499*34114Skarels 
500*34114Skarels 
501*34114Skarels /* called by qsort to compare troff codes */
502*34114Skarels /* if troff codes are the same, use facecode to decide */
503*34114Skarels 
504*34114Skarels private compTRcode(a,b)struct ctab *a, *b;
505*34114Skarels {
506*34114Skarels     register int i;
507*34114Skarels     i = a->trcode - b->trcode;
508*34114Skarels     if (i == 0) return ((a->catcode>>7) - (b->catcode>>7));
509*34114Skarels     return(i);
510*34114Skarels }
511*34114Skarels 
512*34114Skarels 
513*34114Skarels private DoWidths() {
514*34114Skarels     int f; /* font index 0-3 */
515*34114Skarels 
516*34114Skarels     qsort((char *) ctab, nchars, sizeof(struct ctab), compTRcode);
517*34114Skarels     for (f = 0; f < 4; f++) {
518*34114Skarels 	dofont(f);
519*34114Skarels     }
520*34114Skarels }
521*34114Skarels 
522*34114Skarels 
523*34114Skarels #define ASCENDER 0200
524*34114Skarels #define DESCENDER 0100
525*34114Skarels 
526*34114Skarels /* note that both is 0300 */
527*34114Skarels /* ascenders are:
528*34114Skarels  *	b d f h i j k l t beta gamma delta zeta theta lambda xi
529*34114Skarels  *	phi psi Gamma Delta  Theta Lambda Xi Pi Sigma
530*34114Skarels  *	Upsilon Phi Psi Omega gradient
531*34114Skarels  */
532*34114Skarels 
533*34114Skarels private char Ascenders[] = "bdfhijklt\231\232\233\235\237\242\245\254\256\260\261\262\263\264\264\265\266\270\271\272\273\326";
534*34114Skarels 
535*34114Skarels /* descenders are:
536*34114Skarels  *	g j p q y beta zeta eta mu xi
537*34114Skarels  *	rho phi chi psi terminal-sigma
538*34114Skarels  */
539*34114Skarels 
540*34114Skarels private char Descenders[] = "gjpqy\231\235\236\243\245\250\254\255\256\275";
541*34114Skarels 
542*34114Skarels 
543*34114Skarels private dofont(fontno)
544*34114Skarels int  fontno; /* troff font number 0-3 */
545*34114Skarels {
546*34114Skarels     char ftfilename[100];
547*34114Skarels     FILE *ftfile;	/* generated font widths file */
548*34114Skarels     int		wid, curcode;
549*34114Skarels     struct	ctab *ct, *cc;
550*34114Skarels     int		asde;
551*34114Skarels 
552*34114Skarels #ifdef SYSV
553*34114Skarels     VOIDC sprintf(ftfilename, "ft%s", facenames[fontno]);
554*34114Skarels #else
555*34114Skarels     VOIDC sprintf(ftfilename, "ft%s.c", facenames[fontno]);
556*34114Skarels #endif
557*34114Skarels     if ((ftfile = fopen(ftfilename, "w")) == NULL) {
558*34114Skarels 	perror(ftfilename);
559*34114Skarels 	exit(1);
560*34114Skarels     }
561*34114Skarels #ifdef BSD
562*34114Skarels     fprintf(ftfile, "/* troff width file for %s - %s (%c) */\n",
563*34114Skarels     	familyname, facenames[fontno], faces[fontno]);
564*34114Skarels     fprintf(ftfile, "char ft%s[256-32] = {\n", facenames[fontno]);
565*34114Skarels #endif
566*34114Skarels 
567*34114Skarels     /* write out entry for each troff character code */
568*34114Skarels     /* codes 0-31 (decimal) are not used */
569*34114Skarels     /* the first interesting code is 32 (040) - space */
570*34114Skarels 
571*34114Skarels     ct = ctab;
572*34114Skarels     for (curcode = 32; curcode < 256; curcode++) {
573*34114Skarels 	while ((ct < &ctab[nchars]) && (ct->trcode < curcode) ||
574*34114Skarels 		((ct->trcode == curcode) && ((ct->catcode>>7) < fontno))) {
575*34114Skarels 		    ct++;
576*34114Skarels 		}
577*34114Skarels 	asde = 0;
578*34114Skarels 	if ((ct >= &ctab[nchars]) || (curcode != ct->trcode) ||
579*34114Skarels 	    ((ct->catcode>>7) != fontno)) {
580*34114Skarels 		/* not found */
581*34114Skarels 		wid = 0;
582*34114Skarels 		cc = 0;
583*34114Skarels 	}
584*34114Skarels 	else {
585*34114Skarels 	    wid = ct->wid;
586*34114Skarels 	    cc = ct;
587*34114Skarels 	    /* calculate ascender/descender info (heuristic) */
588*34114Skarels 	    if (((curcode >= '0') && (curcode <= '9')) ||
589*34114Skarels 	    	((curcode >= 'A') && (curcode <= 'Z'))) asde |= ASCENDER;
590*34114Skarels 	    if (INDEX(Ascenders, curcode)) asde |= ASCENDER;
591*34114Skarels 	    if (INDEX(Descenders, curcode)) asde |= DESCENDER;
592*34114Skarels 	}
593*34114Skarels #ifdef SYSV
594*34114Skarels 	/* for system V we write a binary file of the width bytes */
595*34114Skarels 	putc(0377&(wid+asde),ftfile);
596*34114Skarels #else
597*34114Skarels 	/* for BSD we write a "c" array to be compiled */
598*34114Skarels 	fprintf(ftfile, "%d", wid);
599*34114Skarels 	if (asde) fprintf(ftfile,"+0%o",asde);
600*34114Skarels 	fprintf(ftfile, ",\t%s/* %s */\n",
601*34114Skarels 		asde?"":"\t",cc?cc->descr:"NULL");
602*34114Skarels #endif
603*34114Skarels     }
604*34114Skarels #ifdef BSD
605*34114Skarels     fprintf(ftfile,"};\n");
606*34114Skarels #endif
607*34114Skarels     VOIDC fclose(ftfile);
608*34114Skarels }
609*34114Skarels 
610*34114Skarels 
611*34114Skarels main(argc, argv)
612*34114Skarels int argc;
613*34114Skarels char **argv;
614*34114Skarels {
615*34114Skarels     if (argc != 2) {
616*34114Skarels 	fprintf(stderr,"usage: pscatmap mapfile\n");
617*34114Skarels 	exit(1);
618*34114Skarels     }
619*34114Skarels     if (freopen(*++argv,"r",stdin) == NULL) {
620*34114Skarels 	perror("pscatmap");
621*34114Skarels 	exit(1);
622*34114Skarels     }
623*34114Skarels 
624*34114Skarels     if ((libdir = envget("PSLIBDIR")) == NULL) libdir = LibDir;
625*34114Skarels 
626*34114Skarels     BuildTable();
627*34114Skarels     WriteTable();
628*34114Skarels     DoWidths();
629*34114Skarels }
630*34114Skarels 
631