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
BuildTable()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
GetFont()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
ReadAFM(afmfile)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
GetMap()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
compCTent(a,b)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
WriteTable()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
compTRcode(a,b)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
DoWidths()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
dofont(fontno)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
main(argc,argv)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