static char rcsid[] = "$Header: /u/jjc/dvitops/RCS/aftopl.c,v 1.3 89/02/22 11:40:31 jjc Rel $"; #include "util.h" #define MAXNAMES 500 #define NOT_DEFINED 100000.0 #ifndef PI #define PI 3.14159265358979324 #endif struct ligatures { char *ch; char *succ; char *lig; }; char *program_name = "aftopl"; static int lineno = 0; FILE *infp; FILE *outfp; #ifdef PROTO int lookup(const char *); char *getkeyvalue(char *, int); void scan_char_metric(char *); void compute_font_dimens(void); void do_kern_pairs(int); void do_char_metrics(int); void do_afms(void); void print_font_dimens(void); void do_encoding(char *); void print_char_metrics(void); void print_lig_kerns(void); void do_ligatures(struct ligatures *); #endif #define bad_afm_file() message(FATAL_ERROR, "bad afm file, line %d", lineno) struct lig_kern_list { struct lig_kern_list *next; int succ; int lig; /* -1 if its a kern */ double x; }; struct { struct lig_kern_list *p; char *name; int code; double llx; double lly; double urx; double ury; double wx; } table[MAXNAMES]; struct { int is_fixed_pitch; double italic_angle; double x_height; /* we use max(urx - wx + italic_correction_space, 0) as the italic correction for each character */ double italic_correction_space; double normal_space; double normal_stretch; double normal_shrink; double extra_space; double quad; double slant; } font = { 0, 0.0, NOT_DEFINED, NOT_DEFINED, NOT_DEFINED, NOT_DEFINED, NOT_DEFINED, NOT_DEFINED, NOT_DEFINED, NOT_DEFINED}; int lookup(s) const char *s; { int i; for (i = 0; i < MAXNAMES; i++) if (table[i].name == NULL) break; else if (strcmp(table[i].name, s) == 0) return i; if (i == MAXNAMES) message(FATAL_ERROR, "too many names"); if ((table[i].name = malloc(strlen(s) + 1)) == NULL) message(FATAL_ERROR, "out of memory"); strcpy(table[i].name, s); table[i].code = -2; table[i].llx = table[i].lly = 0.0; table[i].urx = table[i].ury = 0.0; table[i].wx = 0.0; table[i].p = NULL; return i; } char *getkeyvalue(s, c) char *s; char c; { while (*s != '\0') { while (isspace(*s)) s++; if (*s == c) return s + 1; while (*s != '\0') if (*s++ == ';') break; } return NULL; } void scan_char_metric(s) char *s; { char name[128]; int code, n; double llx, lly, urx, ury; double wx, wy; char *p; while (isspace(*s)) s++; if (*s == '\0') /* ignore blank line */ return; p = getkeyvalue(s, 'N'); if (p == NULL) bad_afm_file(); /* sscanf(p, " %[A-Za-z0-9]", name); */ /* Microsoft C scanf doesn't understand %[ */ sscanf(p, "%s", name); if ((p = strchr(name, ';')) != NULL) *p = '\0'; if ((p = getkeyvalue(s, 'C')) == NULL) bad_afm_file(); if (sscanf(p, "%d", &code) != 1) bad_afm_file(); p = getkeyvalue(s, 'B'); if (p == NULL) bad_afm_file(); if (sscanf(p, "%lg %lg %lg %lg", &llx, &lly, &urx, &ury) != 4) bad_afm_file(); p = getkeyvalue(s, 'W'); if (p == NULL) bad_afm_file(); if (*p == 'X') { if (sscanf(p+1, "%lg", &wx) != 1) bad_afm_file(); wy = 0.0; } else if (sscanf(p, "%lg %lg", &wx, &wy) != 2) bad_afm_file(); n = lookup(name); table[n].code = code; table[n].wx = wx; table[n].llx = llx; table[n].lly = lly; table[n].urx = urx; table[n].ury = ury; while ((p = getkeyvalue(s, 'L')) != NULL) { char successor[128]; char ligature[128]; struct lig_kern_list *ptr; if (sscanf(p, " %s %s", successor, ligature) != 2) bad_afm_file(); else if ((s = strchr(ligature, ';')) != NULL) *s = '\0'; if ((s = strchr(p, ';')) == NULL) bad_afm_file(); s++; if ((ptr = (struct lig_kern_list *)malloc(sizeof(struct lig_kern_list))) == NULL) message(FATAL_ERROR, "out of memory"); ptr->succ = lookup(successor); ptr->lig = lookup(ligature); ptr->next = table[n].p; table[n].p = ptr; } } void compute_font_dimens() { if (font.normal_space == NOT_DEFINED) { int n = lookup("space"); if (table[n].code == -2) message(FATAL_ERROR, "no char metric for space character"); else font.normal_space = table[n].wx; } if (font.quad == NOT_DEFINED) font.quad = (font.is_fixed_pitch?2.0:3.0)*font.normal_space; if (font.italic_correction_space == NOT_DEFINED) font.italic_correction_space = font.normal_space/12.0; if (font.normal_shrink == NOT_DEFINED) font.normal_shrink = font.is_fixed_pitch ? 0.0 : font.normal_space/3.0; if (font.normal_stretch == NOT_DEFINED) font.normal_stretch = font.is_fixed_pitch ? 0.0 : font.normal_space/2.0; if (font.extra_space == NOT_DEFINED) font.extra_space = (font.is_fixed_pitch ? 1.0 :0.5) *font.normal_space; if (font.slant == NOT_DEFINED) if (font.italic_angle == 0.0) font.slant = 0.0; else font.slant = sin(-font.italic_angle*PI/180.0); if (font.x_height == NOT_DEFINED) font.x_height = table[lookup("x")].ury; } void do_kern_pairs(n) int n; { char buf[512]; char name1[128]; char name2[128]; double x, y; for (;;) { int i; struct lig_kern_list *ptr; lineno++; if (fgets(buf, 512, infp) == NULL) bad_afm_file(); if (buf[0] == '\n') continue; if (strcmp(buf, "EndKernPairs\n") == 0) break; if (sscanf(buf, "KPX %s %s %lg", name1, name2, &x) != 3) { if (sscanf(buf, "KP %s %s %lg %lg", name1, name2, &x, &y) != 4) bad_afm_file(); } else y = 0.0; i = lookup(name1); if ((ptr = (struct lig_kern_list *)malloc(sizeof(struct lig_kern_list))) == NULL) message(FATAL_ERROR, "out of memory"); ptr->succ = lookup(name2); ptr->lig = -1; ptr->x = x; ptr->next = table[i].p; table[i].p = ptr; n--; } if (n != 0) message(WARNING, "wrong number of kern pairs"); } void do_char_metrics(n) int n; { char buf[512]; for (;;) { char *ptr = buf; lineno++; if (fgets(buf, 512, infp) == NULL) bad_afm_file(); if (strcmp(buf, "EndCharMetrics\n") == 0) break; while (isspace(*ptr)) ptr++; if (*ptr == '\0') continue; scan_char_metric(buf); n--; } if (n != 0) message(WARNING, "wrong number of char metrics"); } void do_afms() { char buf[512]; lineno = 0; while (fgets(buf, 512, infp) != NULL) { char *key, *value; key = buf; lineno++; while (isspace(*key)) key++; value = key; while (*value != '\0' && !isspace(*value)) value++; if (*value != '\0') { /* strip trailing white space */ char *p; *value++ = '\0'; while (isspace(*value)) value++; p = value; while (*p != '\0') p++; while (p > value && isspace(*(p-1))) p--; *p = '\0'; } if (strcmp(key, "IsFixedPitch") == 0) font.is_fixed_pitch = (strcmp(value, "true") == 0); else if (strcmp(key, "XHeight") == 0) font.x_height = atof(value); else if (strcmp(key, "StartCharMetrics") == 0) do_char_metrics(atoi(value)); else if (strcmp(key, "ItalicAngle") == 0) font.italic_angle = atof(value); else if (strcmp(key, "StartKernPairs") == 0) do_kern_pairs(atoi(value)); else if (strcmp(key, "italicCorrectionSpace") == 0) font.italic_correction_space = atof(value); } } void print_font_dimens() { fprintf(outfp, "(FONTDIMEN\n"); fprintf(outfp, " (SLANT R %f)\n", font.slant); fprintf(outfp, " (SPACE R %f)\n", font.normal_space/1000.0); fprintf(outfp, " (SHRINK R %f)\n", font.normal_shrink/1000.0); fprintf(outfp, " (STRETCH R %f)\n", font.normal_stretch/1000.0); fprintf(outfp, " (EXTRASPACE R %f)\n", font.extra_space/1000.0); fprintf(outfp, " (QUAD R %f)\n", font.quad/1000.0); fprintf(outfp, " (XHEIGHT R %f)\n", font.x_height/1000.0); fprintf(outfp, " )\n"); } void do_encoding(encoding) char *encoding; { int i; char name[256]; FILE *fp; char *texfonts; fprintf(outfp, "(CODINGSCHEME %s)\n", encoding); texfonts = getenv("TEXFONTS"); if (texfonts == NULL) texfonts = TEXFONTS; if ((fp = xfopen(encoding, FALSE, texfonts, ".enc")) == NULL) message(FATAL_ERROR, "can't find %s", encoding); for (i = 0; i < MAXNAMES; i++) if (table[i].code >= -1) table[i].code = -1; for (i = 0; i < 256 && fscanf(fp, " %s ", name) == 1; i++) { int n; if (strcmp(".notdef", name) == 0) continue; n = lookup(name); if (table[n].code == -2) message(WARNING, "%s in encoding but not in font", name); else if (table[n].code == -1) table[n].code = i; else message(ERROR, "%s appears more than once in encoding", name); } } void print_char_metrics() { int i; for (i = 0; i < MAXNAMES; i++) if (table[i].name != NULL && table[i].code > 0) { double charic; fprintf(outfp, "(CHARACTER O %03o\n", table[i].code); fprintf(outfp, " (COMMENT %s)\n", table[i].name); fprintf(outfp, " (CHARWD R %f)\n", table[i].wx/1000.0); /* negative heights and depths mess up \mathaccent */ if (table[i].ury > 0.0) fprintf(outfp, " (CHARHT R %f)\n", table[i].ury/1000.0); if (table[i].lly < 0.0) fprintf(outfp, " (CHARDP R %f)\n", -table[i].lly/1000.0); charic = table[i].urx - table[i].wx + font.italic_correction_space; if (charic > 0.0) fprintf(outfp, " (CHARIC R %f)\n", charic/1000.0); fputs(" )\n", outfp); } } void print_lig_kerns() { int i; fputs("(LIGTABLE\n", outfp); for (i = 0; i < MAXNAMES; i++) { struct lig_kern_list *p; char label_line[128]; if (table[i].name == NULL || table[i].code < 0) continue; sprintf(label_line, " (LABEL O %03o)\n", table[i].code); /* print the ligatures */ for (p = table[i].p; p != NULL; p = p->next) if (p->lig != -1 && table[p->lig].code > 0 && table[p->succ].code > 0) { fputs(label_line, outfp); label_line[0]= '\0'; fprintf(outfp," (LIG O %03o O %03o)\n", table[p->succ].code, table[p->lig].code); } for (p = table[i].p; p != NULL; p = p->next) if (p->lig == -1 && table[p->succ].code > 0) { fputs(label_line, outfp); label_line[0] = '\0'; fprintf(outfp, " (KRN O %03o R %f)\n", table[p->succ].code, p->x/1000.0); } if (label_line[0] == '\0') fputs(" (STOP)\n", outfp); } fputs(" )\n", outfp); } struct ligatures standard_ligatures[] = { "hyphen", "hyphen", "endash", "endash", "hyphen", "emdash", "quoteleft", "quoteleft", "quotedblleft", "quoteright", "quoteright", "quotedblright", "exclam", "quoteleft", "exclamdown", "question", "quoteleft", "questiondown", NULL, NULL, NULL }; struct ligatures tt_ligatures[] = { "exclam", "quoteleft", "exclamdown", "question", "quoteleft", "questiondown", NULL, NULL, NULL }; void do_ligatures(lig) struct ligatures *lig; { int i; for (i = 0; lig[i].lig != NULL; i++) { int n; struct lig_kern_list *p; n = lookup(lig[i].ch); if ((p = (struct lig_kern_list *)malloc(sizeof(struct lig_kern_list))) == NULL) message(FATAL_ERROR, "out_of_memory"); p->succ = lookup(lig[i].succ); p->lig = lookup(lig[i].lig); p->next = table[n].p; table[n].p = p; } } int main(argc, argv) int argc; char **argv; { char filename[128]; int c; char *efile = NULL; while ((c = getopt(argc, argv, "e:")) != EOF) switch(c) { case 'e': efile = optarg; break; case '?': goto usage; } if (argc - optind < 1) goto usage; strcpy(filename, argv[optind]); if (strchr(filename, '.') == NULL) strcat(filename, ".afm"); if ((infp = fopen(filename, "r")) == NULL) message(FATAL_ERROR, "can't open %s", filename); if (argc - optind == 1) strcpy(strchr(filename, '.'), ".pl"); else if (argc - optind == 2) { strcpy(filename, argv[optind+1]); if (strchr(filename, '.') == NULL) strcat(filename, ".pl"); } else goto usage; if ((outfp = fopen(filename, "w")) == NULL) message(FATAL_ERROR, "can't open %s", filename); do_afms(); if (efile != NULL) do_encoding(efile); if (font.is_fixed_pitch) do_ligatures(tt_ligatures); else do_ligatures(standard_ligatures); compute_font_dimens(); print_font_dimens(); print_char_metrics(); print_lig_kerns(); exit(history); usage: message(FATAL_ERROR, "usage: aftopl [-e encoding] afmfile [plfile]"); }