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