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