/* $Id$ */ /* Public domain, originally written by Tom Rokicki. * This program converts AFM files to TeX TFM files, and optionally * to TeX VPL files that retain all kerning and ligature information. * Both files make the characters not normally encoded by TeX available * by character codes greater than 127. */ /* (Modified by Don Knuth from Tom Rokicki's pre-VPL version.) */ /* VM/CMS port by J. Hafner (hafner@almaden.ibm.com), based on * the port by Alessio Guglielmi (guglielmi@ipisnsib.bitnet) * and Marco Prevedelli (prevedelli@ipisnsva.bitnet). * This port is still in test state. No guarantees. * 11/3/92: more corrections to VM/CMS port. Now it looks correct * and will be supported by J. Hafner. * */ /* * More changes, primarily from Karl Berry, enough for a new version * number to 8.0; 1 December 1996. Note that this version computes * checksums differently (more intelligently). */ #ifdef KPATHSEA #include "config.h" #include #include #include #include #else /* ! KPATHSEA */ #include #include #include #if defined(SYSV) || defined(VMS) || defined(__THINK__) || defined(MSDOS) || defined(OS2) || defined(ATARIST) || defined(WIN32) #include #else #include #endif #include #ifdef ATARIST #include #endif #endif /* KPATHSEA */ /* JLH: added these to make the code easier to read and remove some ascii<->ebcdic dependencies */ #define ASCII_A 65 #define ASCII_Z 90 #define ASCII_a 97 #define ASCII_z 122 #define ASCII_0 48 #define ASCII_9 57 #ifdef VMCMS #define interesting lookstr /* for 8 character truncation conflicts */ #include "dvipscms.h" extern FILE *cmsfopen(); extern char ebcdic2ascii[]; extern char ascii2ebcdic[]; #ifdef fopen #undef fopen #endif #define fopen cmsfopen #endif /* VMCMS */ #include "dvips.h" /* debug.h redefines fopen to my_real_fopen, but it's still a FILE * */ #ifdef fopen #undef fopen extern FILE *fopen (); #endif struct encoding { const char *name; const char *vec[256]; }; struct encoding staticencoding = { "TeX text", {"Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi", "Sigma", "Upsilon", "Phi", "Psi", "Omega", "arrowup", "arrowdown", "quotesingle", "exclamdown", "questiondown", "dotlessi", "dotlessj", "grave", "acute", "caron", "breve", "macron", "ring", "cedilla", "germandbls", "ae", "oe", "oslash", "AE", "OE", "Oslash", "space", "exclam", "quotedbl", "numbersign", "dollar", "percent", "ampersand", "quoteright", "parenleft", "parenright", "asterisk", "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "colon", "semicolon", "less", "equal", "greater", "question", "at", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "bracketleft", "backslash", "bracketright", "circumflex", "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "tilde", "dieresis", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "" } }; /* * It's easier to put this in static storage and parse it as we go * than to build the structures ourselves. */ const char *staticligkern[] = { "% LIGKERN space l =: lslash ; space L =: Lslash ;", "% LIGKERN question quoteleft =: questiondown ;", "% LIGKERN exclam quoteleft =: exclamdown ;", "% LIGKERN hyphen hyphen =: endash ; endash hyphen =: emdash ;", "% LIGKERN quoteleft quoteleft =: quotedblleft ;", "% LIGKERN quoteright quoteright =: quotedblright ;", "% LIGKERN space {} * ; * {} space ; zero {} * ; * {} zero ;", "% LIGKERN one {} * ; * {} one ; two {} * ; * {} two ;", "% LIGKERN three {} * ; * {} three ; four {} * ; * {} four ;", "% LIGKERN five {} * ; * {} five ; six {} * ; * {} six ;", "% LIGKERN seven {} * ; * {} seven ; eight {} * ; * {} eight ;", "% LIGKERN nine {} * ; * {} nine ;", /* Kern accented characters the same way as their base. */ "% LIGKERN Aacute <> A ; aacute <> a ;", "% LIGKERN Acircumflex <> A ; acircumflex <> a ;", "% LIGKERN Adieresis <> A ; adieresis <> a ;", "% LIGKERN Agrave <> A ; agrave <> a ;", "% LIGKERN Aring <> A ; aring <> a ;", "% LIGKERN Atilde <> A ; atilde <> a ;", "% LIGKERN Ccedilla <> C ; ccedilla <> c ;", "% LIGKERN Eacute <> E ; eacute <> e ;", "% LIGKERN Ecircumflex <> E ; ecircumflex <> e ;", "% LIGKERN Edieresis <> E ; edieresis <> e ;", "% LIGKERN Egrave <> E ; egrave <> e ;", "% LIGKERN Iacute <> I ; iacute <> i ;", "% LIGKERN Icircumflex <> I ; icircumflex <> i ;", "% LIGKERN Idieresis <> I ; idieresis <> i ;", "% LIGKERN Igrave <> I ; igrave <> i ;", "% LIGKERN Ntilde <> N ; ntilde <> n ;", "% LIGKERN Oacute <> O ; oacute <> o ;", "% LIGKERN Ocircumflex <> O ; ocircumflex <> o ;", "% LIGKERN Odieresis <> O ; odieresis <> o ;", "% LIGKERN Ograve <> O ; ograve <> o ;", "% LIGKERN Oslash <> O ; oslash <> o ;", "% LIGKERN Otilde <> O ; otilde <> o ;", "% LIGKERN Scaron <> S ; scaron <> s ;", "% LIGKERN Uacute <> U ; uacute <> u ;", "% LIGKERN Ucircumflex <> U ; ucircumflex <> u ;", "% LIGKERN Udieresis <> U ; udieresis <> u ;", "% LIGKERN Ugrave <> U ; ugrave <> u ;", "% LIGKERN Yacute <> Y ; yacute <> y ;", "% LIGKERN Ydieresis <> Y ; ydieresis <> y ;", "% LIGKERN Zcaron <> Z ; zcaron <> z ;", /* * These next are only included for deficient afm files that * have the lig characters but not the lig commands. */ "% LIGKERN f i =: fi ; f l =: fl ; f f =: ff ; ff i =: ffi ;", "% LIGKERN ff l =: ffl ;", 0 }; /* * The above layout corresponds to TeX Typewriter Type and is compatible * with TeX Text because the position of ligatures is immaterial. */ struct encoding *outencoding = 0; struct encoding *inencoding = 0; char *outenname = NULL, *inenname = NULL;/* the file names for input and output encodings */ int boundarychar = -1; /* the boundary character */ int ignoreligkern; /* do we look at ligkern info in the encoding? */ /* * This is what we store Adobe data in. */ struct adobeinfo { struct adobeinfo *next; int adobenum, texnum, width; const char *adobename; int llx, lly, urx, ury; struct lig *ligs; struct kern *kerns; struct adobeptr *kern_equivs; struct pcc *pccs; int wptr, hptr, dptr, iptr; } *adobechars, *adobeptrs[256], *texptrs[256], *uppercase[256], *lowercase[256]; int nexttex[256]; /* for characters encoded multiple times in output */ /* * These are the eight ligature ops, in VPL terms and in METAFONT terms. */ const char *vplligops[] = { "LIG", "/LIG", "/LIG>", "LIG/", "LIG/>", "/LIG/", "/LIG/>", "/LIG/>>", 0 }; const char *encligops[] = { "=:", "|=:", "|=:>", "=:|", "=:|>", "|=:|", "|=:|>", "|=:|>>", 0 }; struct lig { struct lig *next; const char *succ, *sub; short op, boundleft; }; struct kern { struct kern *next; const char *succ; int delta; }; struct adobeptr { struct adobeptr *next; struct adobeinfo *ch; }; struct pcc { struct pcc *next; const char * partname; int xoffset, yoffset; }; FILE *afmin, *vplout, *tfmout; #define MAXNAME 256 char inname[MAXNAME], outname[MAXNAME]; /* names of input and output files */ char tmpstr[MAXNAME]; /* a buffer for one string */ #define INBUFSIZE 1024 char buffer[INBUFSIZE+10]; /* input buffer (modified while parsing) */ char obuffer[INBUFSIZE+10]; /* unmodified copy of input buffer */ char *param; /* current position in input buffer */ const char *fontname = "Unknown"; const char *codingscheme = "Unspecified"; #ifdef VMCMS char *ebfontname; char *ebcodingscheme; #endif float italicangle = 0.0; char fixedpitch; char makevpl; char pedantic; int noaccentheightadjust; int xheight = 400; int fontspace; int bc, ec; long cksum; float efactor = 1.0, slant = 0.0; float capheight = 0.8; char *efactorparam, *slantparam; double newslant; char titlebuf[500]; static void error(const char *s) { fprintf(stderr, "%s\n", s); if (obuffer[0]) { fprintf(stderr, "%s\n", obuffer); while (param > buffer) { fprintf(stderr, " "); param--; } fprintf(stderr, "^\n"); } if (*s == '!') exit(1); } static int transform(register int x, register int y) { register double acc; acc = efactor * x + slant *y; return (int)(acc>=0? floor(acc+0.5) : ceil(acc-0.5) ); } static int texlive_getline(void) { register char *p; register int c; param = buffer; for (p=buffer; (c=getc(afmin)) != EOF;) { if (p - buffer >= INBUFSIZE) error("! input line too long; perhaps input file is malformed?"); *p++ = c; if (c == '\r') { c = getc(afmin); if (c != EOF) { if (c == '\n') { *p++ = c; } else { ungetc(c, afmin); } } break; } else if (c == '\n') { break; } } *p = 0; strcpy(obuffer, buffer); if (p == buffer && c == EOF) return(0); else return(1); } const char *interesting[] = { "FontName", "ItalicAngle", "IsFixedPitch", "XHeight", "C", "KPX", "CC", "EncodingScheme", NULL}; #define FontName (0) #define ItalicAngle (1) #define IsFixedPitch (2) #define XHeight (3) #define C (4) #define KPX (5) #define CC (6) #define EncodingScheme (7) #define NONE (-1) static int interest(const char *s) { register const char **p; register int n; for (p=interesting, n=0; *p; p++, n++) if (strcmp(s, *p)==0) return(n); return(NONE); } static char * mymalloc(unsigned long len) { register char *p; int i; #ifdef SMALLMALLOC if (len > 65500L) error("! can't allocate more than 64K!"); #endif p = (char *) malloc((unsigned)len); if (p==NULL) error("! out of memory"); for (i=0; i ' ') p++; if (*p != 0) *p++ = 0; q = newstring(param); while (*p && *p <= ' ') p++; param = p; return(q); } static char * paramstring(void) { register char *p, *q; p = param; while (*p > ' ') p++; q = param; if (*p != 0) *p++ = 0; while (*p && *p <= ' ') p++; param = p; return(q); } static int paramnum(void) { register char *p; int i; p = paramstring(); if (sscanf(p, "%d", &i) != 1) error("! integer expected"); return(i); } static float paramfloat(void) { register char *p; float i; p = paramstring(); if (sscanf(p, "%f", &i) != 1) error("! number expected"); return(i); } static struct adobeinfo * newchar(void) { register struct adobeinfo *ai; ai = (struct adobeinfo *)mymalloc((unsigned long)sizeof(struct adobeinfo)); ai->adobenum = -1; ai->texnum = -1; ai->width = -1; ai->adobename = NULL; ai->llx = -1; ai->lly = -1; ai->urx = -1; ai->ury = -1; ai->ligs = NULL; ai->kerns = NULL; ai->kern_equivs = NULL; ai->pccs = NULL; ai->next = adobechars; adobechars = ai; return(ai); } static struct kern * newkern(void) { register struct kern *nk; nk = (struct kern *)mymalloc((unsigned long)sizeof(struct kern)); nk->next = NULL; nk->succ = NULL; nk->delta = 0; return(nk); } static struct pcc * newpcc(void) { register struct pcc *np; np = (struct pcc *)mymalloc((unsigned long)sizeof(struct pcc)); np->next = NULL; np->partname = NULL; np->xoffset = 0; np->yoffset = 0; return(np); } static struct lig * newlig(void) { register struct lig *nl; nl = (struct lig *)mymalloc((unsigned long)sizeof(struct lig)); nl->next = NULL; nl->succ = NULL; nl->sub = NULL; nl->op = 0; /* the default =: op */ nl->boundleft = 0; return(nl); } static void expect(const char *s) { if (strcmp(paramstring(), s) != 0) { fprintf(stderr, "%s expected: ", s); error("! syntax error"); } } static void handlechar(void) { /* an input line beginning with C */ register struct adobeinfo *ai; register struct lig *nl; ai = newchar(); ai->adobenum = paramnum(); expect(";"); expect("WX"); ai->width = transform(paramnum(),0); if (ai->adobenum >= 0 && ai->adobenum < 256) { adobeptrs[ai->adobenum] = ai; } expect(";"); /* Ignore vertical metrics information */ if (*param == 'W' && *(param + 1) == 'Y') { expect("WY"); paramnum(); expect(";"); } expect("N"); ai->adobename = paramnewstring(); expect(";"); expect("B"); ai->llx = paramnum(); ai->lly = paramnum(); ai->llx = transform(ai->llx, ai->lly); ai->urx = paramnum(); ai->ury = paramnum(); ai->urx = transform(ai->urx, ai->ury); /* We need to avoid negative heights or depths. They break accents in math mode, among other things. */ if (ai->lly > 0) ai->lly = 0; if (ai->ury < 0) ai->ury = 0; expect(";"); /* Now look for ligatures (which aren't present in fixedpitch fonts) */ while (*param == 'L' && !fixedpitch) { expect("L"); nl = newlig(); nl->succ = paramnewstring(); nl->sub = paramnewstring(); nl->next = ai->ligs; ai->ligs = nl; expect(";"); } } static struct adobeinfo * findadobe(const char *p) { register struct adobeinfo *ai; for (ai=adobechars; ai; ai = ai->next) if (strcmp(p, ai->adobename)==0) return(ai); return(NULL); } /* * The following comment no longer applies; we rely on the LIGKERN * entries to kill space kerns. Also, the same applies to numbers. * * We ignore kerns before and after space characters, because (1) TeX * is using the space only for Polish ligatures, and (2) TeX's * boundarychar mechanisms are not oriented to kerns (they apply * to both spaces and punctuation) so we don't want to use them. */ static void handlekern(void) { /* an input line beginning with KPX */ register struct adobeinfo *ai; register char *p; register struct kern *nk; p = paramstring(); ai = findadobe(p); if (ai == NULL) error("kern char not found"); else { nk = newkern(); nk->succ = paramnewstring(); nk->delta = transform(paramnum(),0); nk->next = ai->kerns; ai->kerns = nk; } } static void handleconstruct(void) { /* an input line beginning with CC */ register struct adobeinfo *ai; register char *p; register struct pcc *np; register int n; struct pcc *npp = NULL; p = paramstring(); ai = findadobe(p); if (ai == NULL) error("! composite character name not found"); n = paramnum(); expect(";"); while (n--) { if (strcmp(paramstring(),"PCC") != 0) return; /* maybe I should expect("PCC") instead, but I'm playing it safe */ np = newpcc(); np->partname = paramnewstring(); if (findadobe(np->partname)==NULL) return; np->xoffset = paramnum(); np->yoffset = paramnum(); np->xoffset = transform(np->xoffset, np->yoffset); if (npp) npp->next = np; else ai->pccs = np; npp = np; expect(";"); } } static struct encoding *readencoding(char *); #if 0 /* Not used */ static void makeaccentligs(void) { register struct adobeinfo *ai, *aci; register char *p; register struct lig *nl; for (ai=adobechars; ai; ai=ai->next) { p = ai->adobename; if (strlen(p)>2) if ((aci=findadobe(p+1)) && (aci->adobenum > 127)) { nl = newlig(); nl->succ = mymalloc((unsigned long)2); *(nl->succ + 1) = 0; *(nl->succ) = *p; nl->sub = ai->adobename; nl->next = aci->ligs; aci->ligs = nl; } } } #endif static void readadobe(void) { struct adobeinfo *ai; #ifdef VMCMS int i; #endif /* * We allocate a placeholder boundary char. */ ai = newchar(); ai->adobenum = -1; ai->adobename = "||"; /* boundary character name */ while (texlive_getline()) { switch(interest(paramstring())) { case FontName: fontname = paramnewstring(); #ifdef VMCMS /* fontname comes in as ebcdic but we need it asciified for tfm file so we save it in ebfontname and change it in fontname */ ebfontname = newstring(fontname); i=0; while(fontname[i] != '\0') { fontname[i]=ebcdic2ascii[fontname[i]]; i++; }; #endif break; case EncodingScheme: codingscheme = paramnewstring(); #ifdef VMCMS /* for codingscheme, we do the same as we did for fontname */ ebcodingscheme = newstring(codingscheme); i=0; while(codingscheme[i] != '\0') { codingscheme[i]=ebcdic2ascii[codingscheme[i]]; i++; } #endif break; case ItalicAngle: italicangle = paramfloat(); break; case IsFixedPitch: if (*param == 't' || *param == 'T') fixedpitch = 1; else fixedpitch = 0; break; case XHeight: xheight = paramnum(); break; case C: handlechar(); break; case KPX: handlekern(); break; case CC: handleconstruct(); break; default: break; } } fclose(afmin); afmin = 0; } /* * Re-encode the adobe font. Assumes that the header file will * also contain the appropriate instructions! */ static void handlereencoding(void) { if (inenname) { int i; struct adobeinfo *ai; const char *p; ignoreligkern = 1; inencoding = readencoding(inenname); for (i=0; i<256; i++) if (0 != (ai=adobeptrs[i])) { ai->adobenum = -1; adobeptrs[i] = NULL; } for (i=0; i<256; i++) { p = inencoding->vec[i]; if (p && *p && strcmp(p, ".notdef") != 0 && 0 != (ai = findadobe(p))) { ai->adobenum = i; adobeptrs[i] = ai; } } codingscheme = inencoding->name; } ignoreligkern = 0; if (outenname) { outencoding = readencoding(outenname); } else { outencoding = readencoding((char *)0); } } /* * This routine reverses a list. We use it because we accumulate the * adobeinfo list in reverse order, but when we go to map the * characters, we would prefer to use the original ordering. It just * makes more sense. */ static struct adobeinfo * revlist (struct adobeinfo *p) { struct adobeinfo *q = 0, *t; while (p) { t = p->next; p->next = q; q = p; p = t; } return q; } static void assignchars(void) { register const char **p; register int i, j; register struct adobeinfo *ai, *pai; int nextfree = 128; struct pcc *pcp; /* * First, we assign all those that match perfectly. */ for (i=0, p=outencoding->vec; i<256; i++, p++) if (*p && strcmp(*p, ".notdef") != 0 && (ai=findadobe(*p)) && (ai->adobenum >= 0 || ai->pccs != NULL)) { if (ai->texnum >= 0) nexttex[i] = ai->texnum; /* linked list */ ai->texnum = i; texptrs[i] = ai; } if (pedantic) return; /* * Next, we assign all the others, retaining the adobe positions, possibly * multiply assigning characters. Unless the output encoding was * precisely specified. */ for (ai=adobechars; ai; ai=ai->next) if (ai->adobenum >= 0 && ai->adobenum <256 && ai->texnum < 0 && texptrs[ai->adobenum] == 0) { ai->texnum = ai->adobenum; texptrs[ai->adobenum] = ai; } /* * Finally, we map all remaining characters into free locations beginning * with 128, if we know how to construct those characters. We need to * make sure the component pieces are mapped. */ adobechars = revlist(adobechars); for (ai=adobechars; ai; ai=ai->next) if (ai->texnum<0 && (ai->adobenum>=0 || ai->pccs != NULL)) { while (texptrs[nextfree]) { nextfree=(nextfree+1)&255; if (nextfree==128) goto finishup; /* all slots full */ } ai->texnum = nextfree; texptrs[nextfree] = ai; } finishup: /* * We now check all of the composite characters. If any of the * components are not mapped, we unmap the composite character. */ for (i=0; i<256; i++) { ai = texptrs[i]; if (ai && ai->pccs != NULL && ai->texnum >= 0) { for (pcp = ai->pccs; pcp; pcp = pcp->next) { pai = findadobe(pcp->partname); if (pai == NULL || pai->texnum < 0) { texptrs[ai->texnum] = 0; ai->texnum = -1; break; } } } } /* * Now, if any of the characters are encoded multiple times, we want * ai->texnum to be the first one assigned, since that is most likely * to be the most important one. So we reverse the above lists. */ for (ai=adobechars; ai; ai=ai->next) if (ai->texnum >= 0 && ai->texnum < 256) { j = -1; while (nexttex[ai->texnum] >= 0) { i = nexttex[ai->texnum]; nexttex[ai->texnum] = j; j = ai->texnum; ai->texnum = i; } nexttex[ai->texnum] = j; } } static void upmap(void) { /* Compute uppercase mapping, when making a small caps font */ register struct adobeinfo *ai, *Ai; register const char *p; register char *q; register struct pcc *np, *nq; int i; char lwr[50]; /* JLH: changed some lines below to be ascii<->ebcdic independent any reason we don't use 'isupper'?. Looks like we should use isupper to me --karl. */ for (Ai=adobechars; Ai; Ai=Ai->next) { p = Ai->adobename; if (isupper ((unsigned char)*p)) { q = lwr; for (; *p; p++) *q++ = TOLOWER (*p); *q = '\0'; /* changed this too! */ if (0 != (ai=findadobe(lwr))) { for (i = ai->texnum; i >= 0; i = nexttex[i]) uppercase[i] = Ai; for (i = Ai->texnum; i >= 0; i = nexttex[i]) lowercase[i] = ai; } } } /* Note that, contrary to the normal true/false conventions, * uppercase[i] is NULL and lowercase[i] is non-NULL when i is the * ASCII code of an uppercase letter; and vice versa for lowercase letters */ if (0 != (ai=findadobe("germandbls"))) if (0 != (Ai=findadobe("S"))) { /* we also construct SS */ for (i=ai->texnum; i >= 0; i = nexttex[i]) uppercase[i] = ai; ai->adobenum = -1; ai->width = Ai->width << 1; ai->llx = Ai->llx; ai->lly = Ai->lly; ai->urx = Ai->width + Ai->urx; ai->ury = Ai->ury; ai->kerns = Ai->kerns; np = newpcc(); np->partname = "S"; nq = newpcc(); nq->partname = "S"; nq->xoffset = Ai->width; np->next = nq; ai->pccs = np; } if ((ai=findadobe("dotlessi"))) for (i=ai->texnum; i >= 0; i = nexttex[i]) uppercase[i] = findadobe("I"); if ((ai=findadobe("dotlessj"))) for (i=ai->texnum; i >= 0; i = nexttex[i]) uppercase[i] = findadobe("J"); } /* The logic above seems to work well enough, but it leaves useless characters * like `fi' and `fl' in the font if they were present initially, * and it omits characters like `dotlessj' if they are absent initially */ /* Now we turn to computing the TFM file */ int lf, lh, nw, nh, nd, ni, nl, nk, ne, np; static void write16(register short what) { fputc(what >> 8, tfmout); fputc(what & 255, tfmout); } static void writearr(register long *p, register int n) { while (n) { write16((short)(*p >> 16)); write16((short)(*p & 65535)); p++; n--; } } static void makebcpl(register long *p, register const char *s, register int n) { register long t; register long sc; if (strlen(s) < n) n = strlen(s); t = ((long)n) << 24; sc = 16; while (n > 0) { t |= ((long)(*(unsigned const char *)s++)) << sc; sc -= 8; if (sc < 0) { *p++ = t; t = 0; sc = 24; } n--; } *p++ = t; } int source[257]; int unsort[257]; /* * Next we need a routine to reduce the number of distinct dimensions * in a TFM file. Given an array what[0]...what[oldn-1], we want to * group its elements into newn clusters, in such a way that the maximum * difference between elements of a cluster is as small as possible. * Furthermore, what[0]=0, and this value must remain in a cluster by * itself. Data such as `0 4 6 7 9' with newn=3 shows that an iterative * scheme in which 6 is first clustered with 7 will not work. So we * borrow a neat algorithm from METAFONT to find the true optimum. * Memory location what[oldn] is set to 0x7fffffffL for convenience. */ long nextd; /* smallest value that will give a different mincover */ static int mincover(long *what, register long d) /* tells how many clusters result, given max difference d */ { register int m; register long l; register long *p; nextd = 0x7fffffffL; p = what+1; m = 1; while (*p<0x7fffffffL) { m++; l = *p; while (*++p <= l+d); if (*p-l < nextd) nextd = *p-l; } return (m); } static void remap(long * what, int oldn, int newn) { register int i, j; register long d, l; what[oldn] = 0x7fffffffL; for (i=oldn-1; i>0; i--) { d = what[i]; for (j=i; what[j+1]newn) d += d; while (mincover(what,d)>newn) d = nextd; i = 1; j = 0; while (i>31)) ^ ai->width; /* cyclic left shift */ s1 &= 0xffffffff; /* in case we're on a 64-bit machine */ for (p=ai->adobename; *p; p++) #ifndef VMCMS s2 = (s2 * 3) + *p; #else s2 = (s2 * 3) + ebcdic2ascii[*p]; #endif } s1 = (s1 << 1) ^ s2; return s1; } /* * The next routine simply scales something. * Input is in 1000ths of an em. Output is in FIXFACTORths of 1000. */ #define FIXFACTOR (0x100000L) /* 2^{20}, the unit fixnum */ static long scale(long what) { return(((what / 1000) << 20) + (((what % 1000) << 20) + 500) / 1000); } long *header, *charinfo, *width, *height, *depth, *ligkern, *kern, *tparam, *italic; long *tfmdata; static void buildtfm(void) { register int i, j; register struct adobeinfo *ai; header = tfmdata; cksum = checksum(); header[0] = cksum; header[1] = 0xa00000; /* 10pt design size */ makebcpl(header+2, codingscheme, 39); makebcpl(header+12, fontname, 19); lh = 17; charinfo = header + lh; for (i=0; i<256 && adobeptrs[i]==NULL; i++); bc = i; for (i=255; i>=0 && adobeptrs[i]==NULL; i--); ec = i; if (ec < bc) error("! no Adobe characters"); width = charinfo + (ec - bc + 1); width[0] = 0; nw++; for (i=bc; i<=ec; i++) if (0 != (ai=adobeptrs[i])) { width[nw]=ai->width; for (j=1; width[j]!=ai->width; j++); ai->wptr = j; if (j==nw) nw++; } if (nw>256) error("! 256 chars with different widths"); depth = width + nw; depth[0] = 0; nd = 1; for (i=bc; i<=ec; i++) if (0 != (ai=adobeptrs[i])) { depth[nd] = -ai->lly; for (j=0; depth[j]!=-ai->lly; j++); ai->dptr = j; if (j==nd) nd++; } if (nd > 16) { remap(depth, nd, 16); nd = 16; for (i=bc; i<=ec; i++) if (0 != (ai=adobeptrs[i])) ai->dptr = unsort[ai->dptr]; } height = depth + nd; height[0] = 0; nh = 1; for (i=bc; i<=ec; i++) if (0 != (ai=adobeptrs[i])) { height[nh]=ai->ury; for (j=0; height[j]!=ai->ury; j++); ai->hptr = j; if (j==nh) nh++; } if (nh > 16) { remap(height, nh, 16); nh = 16; for (i=bc; i<=ec; i++) if (0 != (ai=adobeptrs[i])) ai->hptr = unsort[ai->hptr]; } italic = height + nh; italic[0] = 0; ni = 1; for (i=bc; i<=ec; i++) if (0 != (ai=adobeptrs[i])) { italic[ni] = ai->urx - ai->width; if (italic[ni]<0) italic[ni] = 0; for (j=0; italic[j]!=italic[ni]; j++); ai->iptr = j; if (j==ni) ni++; } if (ni > 64) { remap(italic, ni, 64); ni = 64; for (i=bc; i<=ec; i++) if (0 != (ai=adobeptrs[i])) ai->iptr = unsort[ai->iptr]; } for (i=bc; i<=ec; i++) if (0 != (ai=adobeptrs[i])) charinfo[i-bc] = ((long)(ai->wptr)<<24) + ((long)(ai->hptr)<<20) + ((long)(ai->dptr)<<16) + ((long)(ai->iptr)<<10); ligkern = italic + ni; nl = 0; /* ligatures and kerns omitted from raw Adobe font */ kern = ligkern + nl; nk = 0; newslant = (double)slant - efactor * tan(italicangle*(3.1415926535/180.0)); tparam = kern + nk; tparam[0] = (long)(FIXFACTOR * newslant + 0.5); tparam[1] = scale((long)fontspace); tparam[2] = (fixedpitch ? 0 : scale((long)(300*efactor+0.5))); tparam[3] = (fixedpitch ? 0 : scale((long)(100*efactor+0.5))); tparam[4] = scale((long)xheight); tparam[5] = scale((long)(1000*efactor+0.5)); np = 6; } static void writesarr(long *what, int len) { register long *p; int i; p = what; i = len; while (i) { *p = scale(*p); scale(*p); /* need this kludge for some compilers */ p++; i--; } writearr(what, len); } static void writetfm(void) { lf = 6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np; write16((short)lf); write16((short)lh); write16((short)bc); write16((short)ec); write16((short)nw); write16((short)nh); write16((short)nd); write16((short)ni); write16((short)nl); write16((short)nk); write16((short)ne); write16((short)np); writearr(header, lh); writearr(charinfo, ec-bc+1); writesarr(width, nw); writesarr(height, nh); writesarr(depth, nd); writesarr(italic, ni); writearr(ligkern, nl); writesarr(kern, nk); writearr(tparam, np); } /* OK, the TFM file is done! Now for our next trick, the VPL file. */ /* For TeX we want to compute a character height that works properly with accents. We only do this if the xheight has a reasonable value (>50), and only if the -a flag is not given. Furthermore, we never return a height less than the height of the original character. The idea of this routine is to adjust the heights of bare characters (say, t) so that the result of using the \accent primitive (say, \"t) is the same as a precomposed tdieresis character. This is done in the vpl output, so the vf+tfm generated from the vpl are affected, but not the original tfm. This idea is reasonable, but the downside is that changing the height of the base character at all, but especially downward, has undesirable consequences: when putting a rule over the character or constructing a math bar-over accent, for example, the rule will be too close to (or too far from, if the height is increased) the character. Furthermore, these days, except when using the original CM fonts, precomposed accent glyphs are widely available and used. And afm2tfm is not used much with the original CM fonts, as they don't come with afm files :). In any case, there is one situation where adjusting the height of the base character by looking at the standalone accent is definitely wrong: the dcaron, tcaron, and [Ll]caron glyphs. In these cases, the usual check-above shape is replaced by (approximately) an apostrophe to the right of the character. Therefore looking at the standalone check glyph is inapplicable. In general, it seems wrong to ever decrease the height from the original, since that will almost certainly result in crashes with \bar and the like. That seems worse than a mispositioned accent. So the routine returns the original height if the heuristic would decrease it. Nevertheless, despite all the above, the original behavior has been around for so many years that it seems bad to change it now. So adjustments upward are still made, unless -a is given. The following list of accents doesn't need to be complete. */ const char *accents[] = { "acute", "tilde", "caron", "dieresis", NULL }; static int texheight(struct adobeinfo *ai) { const char **p; struct adobeinfo *aci, *acci; /* The `ai' argument is the base letter, e.g., "d". `aci' is the accent, e.g., "caron". `acci' is the pre-composed accented letter, e.g., "dcaron". */ /* If -a was given, or the xheight is too small, or the character name is more than one letter long, return the original height. */ if (noaccentheightadjust || xheight <= 50 || *(ai->adobename + 1)) return ai->ury; for (p = accents; *p; p++) { /* otherwise we look for accented letters */ aci = findadobe(*p); if (aci) { strcpy(buffer, ai->adobename); strcat(buffer, *p); acci = findadobe(buffer); if (acci) { /* height heuristic since \accent assumes x-height positioning */ int guess = acci->ury - aci->ury + xheight; if (guess > ai->ury) { /* ignore guess if it reduces height */ return guess; } } } } /* didn't find anything to adjust, return original */ return ai->ury; } /* modified tgr to eliminate varargs problems */ #define vout(s) fprintf(vplout, s) int level; /* the depth of parenthesis nesting in VPL file being written */ static void vlevout(void) { register int l = level; while (l--) vout(" "); } static void vlevnlout(void) { vout("\n"); vlevout(); } #define voutln(str) {fprintf(vplout,"%s\n",str);vlevout();} #define voutln2(f,s) {fprintf(vplout,f,s);vlevnlout();} #define voutln3(f,a,b) {fprintf(vplout,f,a,b);vlevnlout();} #define voutln4(f,a,b,c) {fprintf(vplout,f,a,b,c);vlevnlout();} static void vleft(void) { level++; vout("("); } static void vright(void) { level--; voutln(")"); } int forceoctal = 0; char vcharbuf[100]; static char * vchar(int c) { if (forceoctal == 0 && ISALNUM (c)) sprintf(vcharbuf,"C %c", #ifndef VMCMS c); #else ascii2ebcdic[c]); #endif else sprintf(vcharbuf,"O %o", (unsigned)c); return vcharbuf; } char vnamebuf[1000]; static char * vname(int c) { if (!forceoctal && ISALNUM (c)) { vnamebuf[0] = 0; } else if (c >= 0 && c < 256) { snprintf (vnamebuf, sizeof (vnamebuf), " (comment %s)", texptrs[c]->adobename); } return vnamebuf; } static void writevpl(void) { register int i, j, k; register struct adobeinfo *ai; register struct lig *nlig; register struct kern *nkern; register struct pcc *npcc; struct adobeinfo *asucc, *asub, *api; struct adobeptr *kern_eq; int xoff, yoff, ht; char unlabeled; voutln2("(VTITLE Created by %s)", titlebuf); voutln("(COMMENT Please edit that VTITLE if you edit this file)"); snprintf(obuffer, sizeof(obuffer), "TeX-%s%s%s%s", outname, (efactor==1.0? "" : "-E"), (slant==0.0? "" : "-S"), (makevpl==1? "" : "-CSC")); if (strlen(obuffer)>19) { /* too long, will retain first 9 and last 10 */ register char *p, *q; for (p = &obuffer[9], q = &obuffer[strlen(obuffer)-10]; p<&obuffer[19]; p++, q++) *p = *q; obuffer[19] = '\0'; } voutln2("(FAMILY %s)" , obuffer); { char tbuf[300]; const char *base_encoding = #ifndef VMCMS codingscheme; #else ebcodingscheme; #endif if (strcmp (outencoding->name, base_encoding) == 0) { snprintf(tbuf, sizeof(tbuf), "%s", outencoding->name); } else { snprintf(tbuf, sizeof(tbuf), "%s + %s", base_encoding, outencoding->name); } if (strlen(tbuf) > 39) { error("Coding scheme too long; shortening to 39 characters."); tbuf[39] = 0; } voutln2("(CODINGSCHEME %s)", tbuf); } voutln("(DESIGNSIZE R 10.0)"); voutln("(DESIGNUNITS R 1000)"); voutln("(COMMENT DESIGNSIZE (1 em) IS IN POINTS)"); voutln("(COMMENT OTHER DIMENSIONS ARE MULTIPLES OF DESIGNSIZE/1000)"); /* Let vptovf compute the checksum. */ /* voutln2("(CHECKSUM O %lo)",cksum ^ 0xffffffff); */ if (boundarychar >= 0) voutln2("(BOUNDARYCHAR O %lo)", (unsigned long)boundarychar); vleft(); voutln("FONTDIMEN"); if (newslant) voutln2("(SLANT R %f)", newslant); voutln2("(SPACE D %d)", fontspace); if (! fixedpitch) { voutln2("(STRETCH D %d)", transform(200,0)); voutln2("(SHRINK D %d)", transform(100,0)); } voutln2("(XHEIGHT D %d)", xheight); voutln2("(QUAD D %d)", transform(1000,0)); voutln2("(EXTRASPACE D %d)", fixedpitch ? fontspace : transform(111, 0)); vright(); vleft(); voutln("MAPFONT D 0"); voutln2("(FONTNAME %s)", outname); /* voutln2("(FONTCHECKSUM O %lo)", (unsigned long)cksum); */ vright(); if (makevpl>1) { vleft(); voutln("MAPFONT D 1"); voutln2("(FONTNAME %s)", outname); voutln2("(FONTAT D %d)", (int)(1000.0*capheight+0.5)); /* voutln2("(FONTCHECKSUM O %lo)", (unsigned long)cksum); */ vright(); } for (i=0; i<256 && texptrs[i]==NULL; i++); bc = i; for (i=255; i>=0 && texptrs[i]==NULL; i--); ec = i; vleft(); voutln("LIGTABLE"); ai = findadobe("||"); unlabeled = 1; for (nlig=ai->ligs; nlig; nlig=nlig->next) if (0 != (asucc=findadobe(nlig->succ))) { if (0 != (asub=findadobe(nlig->sub))) if (asucc->texnum>=0) if (asub->texnum>=0) { if (unlabeled) { voutln("(LABEL BOUNDARYCHAR)"); unlabeled = 0; } for (j = asucc->texnum; j >= 0; j = nexttex[j]) { voutln4("(%s %s O %o)", vplligops[nlig->op], vchar(j), (unsigned)asub->texnum); } } } if (! unlabeled) voutln("(STOP)"); for (i=bc; i<=ec; i++) if ((ai=texptrs[i]) && ai->texnum == i) { unlabeled = 1; if (uppercase[i]==NULL) /* omit ligatures from smallcap lowercase */ for (nlig=ai->ligs; nlig; nlig=nlig->next) if (0 != (asucc=findadobe(nlig->succ))) if (0 != (asub=findadobe(nlig->sub))) if (asucc->texnum>=0) if (asub->texnum>=0) { if (unlabeled) { for (j = ai->texnum; j >= 0; j = nexttex[j]) voutln3("(LABEL %s)%s", vchar(j), vname(j)); unlabeled = 0; } for (j = asucc->texnum; j >= 0; j = nexttex[j]) { voutln4("(%s %s O %o)", vplligops[nlig->op], vchar(j), (unsigned)asub->texnum); if (nlig->boundleft) break; } } for (nkern = (uppercase[i] ? uppercase[i]->kerns : ai->kerns); nkern; nkern=nkern->next) if (0 != (asucc=findadobe(nkern->succ))) for (j = asucc->texnum; j >= 0; j = nexttex[j]) { if (uppercase[j]==NULL) { if (unlabeled) { for (k = ai->texnum; k >= 0; k = nexttex[k]) voutln3("(LABEL %s)%s", vchar(k), vname(k)); unlabeled = 0; } /* If other characters have the same kerns as this one, output the label here. This makes the TFM file much smaller than if we output all the kerns again under a different label. */ for (kern_eq = ai->kern_equivs; kern_eq; kern_eq = kern_eq->next) { k = kern_eq->ch->texnum; if (k >= 0 && k < 256) voutln3("(LABEL %s)%s", vchar(k), vname(k)); } ai->kern_equivs = 0; /* Only output those labels once. */ if (uppercase[i]) { if (lowercase[j]) { for (k=lowercase[j]->texnum; k >= 0; k = nexttex[k]) voutln4("(KRN %s R %.1f)%s", vchar(k), capheight*nkern->delta, vname(k)); } else voutln4("(KRN %s R %.1f)%s", vchar(j), capheight*nkern->delta, vname(j)); } else { voutln4("(KRN %s R %d)%s", vchar(j), nkern->delta, vname(j)); if (lowercase[j]) for (k=lowercase[j]->texnum; k >= 0; k = nexttex[k]) voutln4("(KRN %s R %.1f)%s", vchar(k), capheight*nkern->delta, vname(k)); } } } if (! unlabeled) voutln("(STOP)"); } vright(); for (i=bc; i<=ec; i++) if (0 != (ai=texptrs[i])) { vleft(); fprintf(vplout, "CHARACTER %s", vchar(i)); if (*vcharbuf=='C') { voutln(""); } else voutln2(" (comment %s)", ai->adobename); if (uppercase[i]) { ai=uppercase[i]; voutln2("(CHARWD R %.1f)", capheight * (ai->width)); if (0 != (ht=texheight(ai))) voutln2("(CHARHT R %.1f)", capheight * ht); if (ai->lly) voutln2("(CHARDP R %.1f)", -capheight * ai->lly); if (ai->urx > ai->width) voutln2("(CHARIC R %.1f)", capheight * (ai->urx - ai->width)); } else { voutln2("(CHARWD R %d)", ai->width); if (0 != (ht=texheight(ai))) voutln2("(CHARHT R %d)", ht); if (ai->lly) voutln2("(CHARDP R %d)", -ai->lly); if (ai->urx > ai->width) voutln2("(CHARIC R %d)", ai->urx - ai->width); } if (ai->adobenum != i || uppercase[i]) { vleft(); voutln("MAP"); if (uppercase[i]) voutln("(SELECTFONT D 1)"); if (ai->pccs && ai->adobenum < 0) { xoff = 0; yoff = 0; for (npcc = ai->pccs; npcc; npcc=npcc->next) if (0 != (api=findadobe(npcc->partname))) if (api->texnum>=0) { if (npcc->xoffset != xoff) { if (uppercase[i]) { voutln2("(MOVERIGHT R %.1f)", capheight * (npcc->xoffset - xoff)); } else voutln2("(MOVERIGHT R %d)", npcc->xoffset - xoff); xoff = npcc->xoffset; } if (npcc->yoffset != yoff) { if (uppercase[i]) { voutln2("(MOVEUP R %.1f)", capheight * (npcc->yoffset - yoff)); } else voutln2("(MOVEUP R %d)", npcc->yoffset - yoff); yoff = npcc->yoffset; } voutln2("(SETCHAR O %o)", (unsigned)api->adobenum); xoff += texptrs[api->texnum]->width; } } else voutln2("(SETCHAR O %o)", (unsigned)ai->adobenum); vright(); } vright(); } if (level) error("! I forgot to match the parentheses"); } #ifdef KPATHSEA static void version(FILE *f) { fputs ("afm2tfm(k) (dvips(k) 2021.1) 8.4\n", f); fprintf (f, "%s\n", kpathsea_version_string); fputs ("Copyright 2021 Radical Eye Software.\n\ There is NO warranty. You may redistribute this software\n\ under the terms of the GNU General Public License\n\ and the Dvips copyright.\n\ For more information about these matters, see the files\n\ named COPYING and afm2tfm.c.\n\ Original author of afm2tfm: T. Rokicki.\n", f); } #define USAGE "\ Convert an Adobe font metric file to TeX font metric format.\n\ \n\ -a omit heuristic adjustment of heights for accents\n\ -c REAL use REAL for height of small caps made with -V [0.8]\n\ -e REAL widen (extend) characters by a factor of REAL\n\ -O use octal for all character codes in the vpl file\n\ -p ENCFILE read/download ENCFILE for the PostScript encoding\n\ -s REAL oblique (slant) characters by REAL, generally <<1\n\ -t ENCFILE read ENCFILE for the encoding of the vpl file\n\ -T ENCFILE equivalent to -p ENCFILE -t ENCFILE\n\ -u output only characters from encodings, nothing extra\n\ -v FILE[.vpl] make a VPL file for conversion to VF\n\ -V SCFILE[.vpl] like -v, but synthesize smallcaps as lowercase\n\ --help print this message and exit.\n\ --version print version number and exit.\n\ " static void usage(FILE *f) { fputs ("Usage: afm2tfm FILE[.afm] [OPTION]... [FILE[.tfm]]\n", f); fputs (USAGE, f); putc ('\n', f); fputs (kpse_bug_address, f); } #else /* ! KPATHSEA */ static void usage(FILE *f) { fprintf(f, "afm2tfm 8.1, Copyright 1990-97 by Radical Eye Software\n"); fprintf(f, "Usage: afm2tfm foo[.afm] [-O] [-u] [-v|-V bar[.vpl]]\n"); fprintf(f, " [-e expansion] [-s slant] [-c capheight]\n"); fprintf(f, " [-p|-t|-T encodingfile] [foo[.tfm]]\n"); } #endif #define CHECKARG3 if (argc < 4) { usage(stderr); exit(1); } static void openfiles(int argc, char **argv) { #ifndef KPATHSEA register int lastext; #else const char *q; #endif register int i; const char *p; int arginc; tfmout = (FILE *)NULL; if (argc == 1) { usage(stdout); exit(0); } #if defined(MSDOS) || defined(OS2) || defined(ATARIST) /* Make VPL file identical to that created under Unix */ snprintf(titlebuf, sizeof(titlebuf), "afm2tfm %s", argv[1]); #else #ifdef VMCMS /* Make VPL file identical to that created under Unix */ snprintf(titlebuf, sizeof(titlebuf), "afm2tfm %s", argv[1]); #else snprintf(titlebuf, sizeof(titlebuf), "%s %s", argv[0], argv[1]); #endif #endif if(strlen(argv[1]) >= MAXNAME - 4) error("! too long input file name"); strcpy(inname, argv[1]); #ifdef KPATHSEA if (find_suffix(inname) == NULL) strcat(inname, ".afm"); #else lastext = -1; for (i=0; inname[i]; i++) if (inname[i] == '.') lastext = i; else if (inname[i] == '/' || inname[i] == ':') lastext = -1; if (lastext == -1) strcat(inname, ".afm"); #endif while (argc>2 && *argv[2]=='-') { arginc = 2; i = argv[2][1]; if (i == '/') i = argv[2][2] - 32; /* /a ==> A for VMS */ switch (i) { case 'V': makevpl++; case 'v': makevpl++; CHECKARG3 if(strlen(argv[3]) >= MAXNAME - 4) error("! too long output VPL file name"); strcpy(outname, argv[3]); #ifdef KPATHSEA if (find_suffix(outname) == NULL) strcat(outname, ".vpl"); #else lastext = -1; for (i=0; outname[i]; i++) if (outname[i] == '.') lastext = i; else if (outname[i] == '/' || outname[i] == ':') lastext = -1; if (lastext == -1) strcat(outname, ".vpl"); #endif #ifndef VMCMS #ifndef ATARIST if ((vplout=fopen(outname, WRITEBIN))==NULL) #else if ((vplout=fopen(outname, "w"))==NULL) #endif #else if ((vplout=fopen(outname, "w"))==NULL) #endif error("! can't open vpl output file"); break; case 'a': noaccentheightadjust = 1; arginc = 1; break; case 'c': CHECKARG3 if (sscanf(argv[3], "%f", &capheight)==0 || capheight<0.01) error("! Bad small caps height"); break; case 'e': CHECKARG3 if (sscanf(argv[3], "%f", &efactor)==0 || efactor<0.01) error("! Bad extension factor"); efactorparam = argv[3]; break; case 'O': forceoctal = 1; arginc = 1; break; case 'P': case 'p': CHECKARG3 inenname = argv[3]; break; case 's': CHECKARG3 if (sscanf(argv[3], "%f", &slant)==0) error("! Bad slant parameter"); slantparam = argv[3]; break; case 'T': CHECKARG3 inenname = outenname = argv[3]; break; case 't': CHECKARG3 outenname = argv[3]; break; case 'u': pedantic = 1; arginc = 1; break; default: fprintf(stderr, "Unknown option %s %s ignored.\n", argv[2], argv[3]); } for (i=0; i3 || (argc==3 && *argv[2]=='-')) { error("! need at most two non-option arguments"); usage(stderr); } if (argc == 2) strcpy(outname, inname); else strcpy(outname, argv[2]); #ifdef KPATHSEA if ((p = find_suffix(outname)) != NULL) outname[p-outname-1] = 0; strcat(outname, ".tfm"); if (tfmout == NULL && (tfmout=fopen(outname, WRITEBIN))==NULL) error("! can't open tfm output file"); /* * Now we strip off any directory information, so we only use the * base name in the vf file. */ if (p == NULL) p = find_suffix(outname); outname[p-outname-1] = 0; q = xbasename(outname); strcpy(tmpstr, q); /* be careful, q and outname are overlapping */ strcpy(outname, tmpstr); #else lastext = -1; for (i=0; outname[i]; i++) if (outname[i] == '.') lastext = i; else if (outname[i] == '/' || outname[i] == ':' || outname[i] == '\\') lastext = -1; if (argc == 2) { outname[lastext] = 0; lastext = -1; } if (lastext == -1) { lastext = strlen(outname); strcat(outname, ".tfm"); } if (tfmout == NULL && (tfmout=fopen(outname, WRITEBIN))==NULL) error("! can't open tfm output file"); outname[lastext] = 0; /* * Now we strip off any directory information, so we only use the * base name in the vf file. We accept any of /, :, or \ as directory * delimiters, so none of these are available for use inside the * base name; this shouldn't be a problem. */ for (i=0, lastext=0; outname[i]; i++) if (outname[i] == '/' || outname[i] == ':' || outname[i] == '\\') lastext = i + 1; if (lastext) strcpy(outname, outname + lastext); #endif } /* * Some routines to remove kerns that match certain patterns. */ static struct kern * rmkernmatch(struct kern *k, char *s) { struct kern *nkern; while (k && strcmp(k->succ, s)==0) k = k->next; if (k) { for (nkern = k; nkern; nkern = nkern->next) while (nkern->next && strcmp(nkern->next->succ, s)==0) nkern->next = nkern->next->next; } return k; } /* * Recursive to one level. */ static void rmkern(char *s1, char *s2, struct adobeinfo *ai) { if (ai == 0) { if (strcmp(s1, "*") == 0) { for (ai=adobechars; ai; ai = ai->next) rmkern(s1, s2, ai); return; } else { ai = findadobe(s1); if (ai == 0) return; } } if (strcmp(s2, "*")==0) ai->kerns = 0; /* drop them on the floor */ else ai->kerns = rmkernmatch(ai->kerns, s2); } /* Make the kerning for character S1 equivalent to that for S2. If either S1 or S2 do not exist, do nothing. If S1 already has kerning, do nothing. */ static void addkern(char *s1, char *s2) { struct adobeinfo *ai1 = findadobe (s1); struct adobeinfo *ai2 = findadobe (s2); if (ai1 && ai2 && !ai1->kerns) { /* Put the new one at the head of the list, since order is immaterial. */ struct adobeptr *ap = (struct adobeptr *) mymalloc((unsigned long)sizeof(struct adobeptr)); ap->next = ai2->kern_equivs; ap->ch = ai1; ai2->kern_equivs = ap; } } int sawligkern; /* * Reads a ligkern line, if this is one. Assumes the first character * passed is `%'. */ static void checkligkern(char *s) { char *oparam = param; char *mlist[5]; int n; s++; while (*s && *s <= ' ') s++; if (strncmp(s, "LIGKERN", 7)==0) { sawligkern = 1; s += 7; while (*s && *s <= ' ') s++; param = s; while (*param) { for (n=0; n<5;) { if (*param == 0) break; mlist[n] = paramstring(); if (strcmp(mlist[n], ";") == 0) break; n++; } if (n > 4) error("! too many parameters in lig kern data"); if (n < 3) error("! too few parameters in lig kern data"); if (n == 3 && strcmp(mlist[1], "{}") == 0) { /* rmkern command */ rmkern(mlist[0], mlist[2], (struct adobeinfo *)0); } else if (n == 3 && strcmp(mlist[1], "<>") == 0) { /* addkern */ addkern(mlist[0], mlist[2]); } else if (n == 3 && strcmp(mlist[0], "||") == 0 && strcmp(mlist[1], "=") == 0) { /* bc command */ struct adobeinfo *ai = findadobe("||"); if (boundarychar != -1) error("! multiple boundary character commands?"); if (sscanf(mlist[2], "%d", &n) != 1) error("! expected number assignment for boundary char"); if (n < 0 || n > 255) error("! boundary character number must be 0..255"); boundarychar = n; if (ai == 0) error("! internal error: boundary char"); ai->texnum = n; /* prime the pump, so to speak, for lig/kerns */ } else if (n == 4) { int op = -1; struct adobeinfo *ai; for (n=0; encligops[n]; n++) if (strcmp(mlist[2], encligops[n])==0) { op = n; break; } if (op < 0) error("! bad ligature op specified"); if (0 != (ai = findadobe(mlist[0]))) { struct lig *lig; if (findadobe(mlist[2])) /* remove coincident kerns */ rmkern(mlist[0], mlist[1], ai); if (strcmp(mlist[3], "||") == 0) error("! you can't lig to the boundary character!"); if (! fixedpitch) { /* fixed pitch fonts get *0* ligs */ for (lig=ai->ligs; lig; lig = lig->next) if (strcmp(lig->succ, mlist[1]) == 0) break; /* we'll re-use this structure */ if (lig == 0) { lig = newlig(); lig->succ = newstring(mlist[1]); lig->next = ai->ligs; ai->ligs = lig; } lig->sub = newstring(mlist[3]); lig->op = op; if (strcmp(mlist[1], "||")==0) { lig->boundleft = 1; if (strcmp(mlist[0], "||")==0) error("! you can't lig boundarychar boundarychar!"); } else lig->boundleft = 0; } } } else error("! bad form in LIGKERN command"); } } param = oparam; } /* * Here we get a token from the AFM file. We parse just as much PostScript * as we expect to find in an encoding file. We allow commented lines and * names like 0, .notdef, _foo_. We do not allow //abc. */ char smbuffer[100]; /* for tokens */ static char * gettoken(void) { char *p, *q; while (1) { while (param == 0 || *param == 0) { if (texlive_getline() == 0) error("! premature end in encoding file"); for (p=buffer; *p; p++) if (*p == '%') { if (ignoreligkern == 0) checkligkern(p); *p = 0; break; } } while (*param && *param <= ' ') param++; if (*param) { if (*param == '[' || *param == ']' || *param == '{' || *param == '}') { smbuffer[0] = *param++; smbuffer[1] = 0; return smbuffer; } else if (*param == '/' || *param == '-' || *param == '_' || *param == '.' || ('0' <= *param && *param <= '9') || ('a' <= *param && *param <= 'z') || ('A' <= *param && *param <= 'Z')) { smbuffer[0] = *param; for (p=param+1, q=smbuffer+1; *p == '-' || *p == '_' || *p == '.' || ('0' <= *p && *p <= '9') || ('a' <= *p && *p <= 'z') || ('A' <= *p && *p <= 'Z'); p++, q++) *q = *p; *q = 0; param = p; return smbuffer; } } } } static void getligkerndefaults(void) { int i; for (i=0; staticligkern[i]; i++) { strcpy(buffer, staticligkern[i]); strcpy(obuffer, staticligkern[i]); param = buffer; checkligkern(buffer); } } /* * This routine reads in an encoding file, given the name. It returns * the final total structure. It performs a number of consistency checks. */ static struct encoding * readencoding(char *enc) { char *p; int i; struct encoding *e = (struct encoding *)mymalloc((unsigned long)sizeof(struct encoding)); sawligkern = 0; if (afmin) error("! oops; internal afmin error"); if (enc) { #ifdef KPATHSEA afmin = kpse_open_file(enc, kpse_enc_format); #else afmin = fopen(enc, "r"); #endif SET_BINARY(fileno(afmin)); param = 0; if (afmin == 0) #ifdef KPATHSEA FATAL1 ("couldn't open encoding file `%s'", enc); #else error("! couldn't open that encoding file"); #endif p = gettoken(); if (*p != '/' || p[1] == 0) error("! first token in encoding must be literal encoding name"); e->name = newstring(p+1); p = gettoken(); if (strcmp(p, "[")) error("! second token in encoding must be mark ([) token"); for (i=0; i<256; i++) { p = gettoken(); if (*p != '/' || p[1] == 0) error("! tokens 3 to 257 in encoding must be literal names"); e->vec[i] = newstring(p+1); } p = gettoken(); if (strcmp(p, "]")) error("! token 258 in encoding must be make-array (])"); while (texlive_getline()) { for (p=buffer; *p; p++) if (*p == '%') { if (ignoreligkern == 0) checkligkern(p); *p = 0; break; } } fclose(afmin); afmin = 0; if (ignoreligkern == 0 && sawligkern == 0) getligkerndefaults(); } else { e = &staticencoding; getligkerndefaults(); } param = 0; return e; } /* * This routine prints out the line that needs to be added to psfonts.map. */ static void conspsfonts(void) { #ifndef VMCMS printf("%s %s", outname, fontname); #else /* VM/CMS: fontname is ascii, so we use ebfontname */ printf("%s %s", outname, ebfontname); #endif if (slantparam || efactorparam || inenname) { printf(" \""); if (slantparam) printf(" %s SlantFont", slantparam); if (efactorparam) printf(" %s ExtendFont", efactorparam); if (inenname) printf(" %s ReEncodeFont", inencoding->name); printf(" \""); if (inenname) printf(" <%s", inenname); } printf("\n"); } #ifndef VMS int #endif main(int argc, char **argv) { int i; #ifdef KPATHSEA kpse_set_program_name (argv[0], "afm2tfm"); if (argc == 1) { fputs ("afm2tfm: Need at least one file argument.\n", stderr); fputs ("Try `afm2tfm --help' for more information.\n", stderr); exit(1); } if (argc == 2) { if (strcmp (argv[1], "--help") == 0) { usage (stdout); exit (0); } else if (strcmp (argv[1], "--version") == 0) { version (stdout); exit (0); } } #endif /* KPATHSEA */ for (i=0; i<256; i++) nexttex[i] = -1; /* encoding chains have length 0 */ tfmdata = (long *)mymalloc((unsigned long)40000L); openfiles(argc, argv); readadobe(); if (fontspace == 0) { struct adobeinfo *ai; if (0 != (ai = findadobe("space"))) fontspace = ai->width; else if (adobeptrs[32]) fontspace = adobeptrs[32]->width; else fontspace = transform(500, 0); } handlereencoding(); buildtfm(); writetfm(); conspsfonts(); if (makevpl) { assignchars(); if (makevpl>1) upmap(); writevpl(); } return 0; /*NOTREACHED*/ }