/* * 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). */ #include #include #include #if defined(SYSV) || defined(VMS) || defined(__THINK__) || defined(MSDOS) || defined(OS2) || defined(ATARIST) #include #else #include #endif #include #ifdef ATARIST #include #endif /* 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 #if defined MSDOS || defined OS2 #define WRITEBIN "wb" #else #ifdef VMCMS #define WRITEBIN "wb, lrecl=1024, recfm=f" #else #define WRITEBIN "w" #endif #endif #if (defined(MSDOS) && defined(__TURBOC__)) || (defined(OS2) && defined(_MSC_VER)) #define SMALLMALLOC #endif struct encoding { char *name ; 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. */ 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, *inenname ;/* 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 ; 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. */ char *vplligops[] = { "LIG", "/LIG", "/LIG>", "LIG/", "LIG/>", "/LIG/", "/LIG/>", "/LIG/>>", 0 } ; char *encligops[] = { "=:", "|=:", "|=:>", "=:|", "=:|>", "|=:|", "|=:|>", "|=:|>>", 0 } ; struct lig { struct lig *next ; char *succ, *sub ; short op, boundleft ; } ; struct kern { struct kern *next ; char *succ ; int delta ; } ; struct adobeptr { struct adobeptr *next; struct adobeinfo *ch; }; struct pcc { struct pcc *next ; char * partname ; int xoffset, yoffset ; } ; FILE *afmin, *vplout, *tfmout ; char inname[200], outname[200] ; /* names of input and output files */ char buffer[255]; /* input buffer (modified while parsing) */ char obuffer[255] ; /* unmodified copy of input buffer */ char *param ; /* current position in input buffer */ char *fontname = "Unknown" ; char *codingscheme = "Unspecified" ; #ifdef VMCMS char *ebfontname ; char *ebcodingscheme ; #endif float italicangle = 0.0 ; char fixedpitch ; char makevpl ; char pedantic ; 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] ; void error(s) register char *s ; { (void)fprintf(stderr, "%s\n", s) ; if (obuffer[0]) { (void)fprintf(stderr, "%s\n", obuffer) ; while (param > buffer) { (void)fprintf(stderr, " ") ; param-- ; } (void)fprintf(stderr, "^\n") ; } if (*s == '!') exit(1) ; } int transform(x,y) register int x,y ; { register double acc ; acc = efactor * x + slant *y ; return (int)(acc>=0? floor(acc+0.5) : ceil(acc-0.5) ) ; } int gaetline() { register char *p ; register int c ; param = buffer ; for (p=buffer; (c=getc(afmin)) != EOF && c != '\n';)/* changed 10 to '\n' */ *p++ = c ; *p = 0 ; (void)strcpy(obuffer, buffer) ; if (p == buffer && c == EOF) return(0) ; else return(1) ; } 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) int interest(s) char *s ; { register char **p ; register int n ; for (p=interesting, n=0; *p; p++, n++) if (strcmp(s, *p)==0) return(n) ; return(NONE) ; } char * mymalloc(len) unsigned long len ; { register char *p ; int i ; #ifdef SMALLMALLOC if (len > 65500L) error("! can't allocate more than 64K!") ; #endif p = 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) ; } char * paramstring() { register char *p, *q ; p = param ; while (*p > ' ') p++ ; q = param ; if (*p != 0) *p++ = 0 ; while (*p && *p <= ' ') p++ ; param = p ; return(q) ; } int paramnum() { register char *p ; int i ; p = paramstring() ; if (sscanf(p, "%d", &i) != 1) error("! integer expected") ; return(i) ; } float paramfloat() { register char *p ; float i ; p = paramstring() ; if (sscanf(p, "%f", &i) != 1) error("! number expected") ; return(i) ; } struct adobeinfo * newchar() { 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) ; } struct kern * newkern() { register struct kern *nk ; nk = (struct kern *)mymalloc((unsigned long)sizeof(struct kern)) ; nk->next = NULL ; nk->succ = NULL ; nk->delta = 0 ; return(nk) ; } struct pcc * newpcc() { 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) ; } struct lig * newlig() { 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) ; } void expect(s) char *s ; { if (strcmp(paramstring(), s) != 0) { (void)fprintf(stderr, "%s expected: ", s) ; error("! syntax error") ; } } void handlechar() { /* 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(";") ; 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(";") ; } } struct adobeinfo * findadobe(p) 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. */ void handlekern() { /* 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 ; } } void handleconstruct() { /* 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(";") ; } } struct encoding *readencoding() ; void makeaccentligs() { 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 ; } } } void readadobe() { 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 (gaetline()) { 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! */ void handlereencoding() { if (inenname) { int i ; struct adobeinfo *ai ; 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 && 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. */ struct adobeinfo *revlist(p) struct adobeinfo *p ; { struct adobeinfo *q = 0, *t ; while (p) { t = p->next ; p->next = q ; q = p ; p = t ; } return q ; } void assignchars() { register 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 ((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->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 ; } } void upmap() { /* Compute uppercase mapping, when making a small caps font */ register struct adobeinfo *ai, *Ai ; register char *p, *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 (*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 ; void write16(what) register short what ; { (void)fputc(what >> 8, tfmout) ; (void)fputc(what & 255, tfmout) ; } void writearr(p, n) register long *p ; register int n ; { while (n) { write16((short)(*p >> 16)) ; write16((short)(*p & 65535)) ; p++ ; n-- ; } } void makebcpl(p, s, n) register long *p ; register 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 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 */ int mincover(what,d) /* tells how many clusters result, given max difference d */ register long d ; long *what ; { 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) ; } void remap(what, oldn, newn) long *what ; int oldn, 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 */ long scale(what) long what ; { return(((what / 1000) << 20) + (((what % 1000) << 20) + 500) / 1000) ; } long *header, *charinfo, *width, *height, *depth, *ligkern, *kern, *tparam, *italic ; long *tfmdata ; void buildtfm() { 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 ; } void writesarr(what, len) long *what ; int len ; { register long *p ; int i ; p = what ; i = len ; while (i) { *p = scale(*p) ; (void)scale(*p) ; /* need this kludge for some compilers */ p++ ; i-- ; } writearr(what, len) ; } void writetfm() { lf = 6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np ; write16(lf) ; write16(lh) ; write16(bc) ; write16(ec) ; write16(nw) ; write16(nh) ; write16(nd) ; write16(ni) ; write16(nl) ; write16(nk) ; write16(ne) ; write16(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. The following list of accents doesn't need to be complete. */ /* * We only do this if the xheight has a reasonable value. * (>50) */ char *accents[] = { "acute", "tilde", "caron", "dieresis", NULL} ; int texheight(ai) register struct adobeinfo *ai ; { register char **p; register struct adobeinfo *aci, *acci ; if (xheight <= 50 || *(ai->adobename + 1)) return (ai->ury) ; /* that was the simple case */ for (p=accents; *p; p++) /* otherwise we look for accented letters */ if (0 != (aci=findadobe(*p))) { strcpy(buffer,ai->adobename) ; strcat(buffer,*p) ; if (0 != (acci=findadobe(buffer))) return (acci->ury - aci->ury + xheight) ; } 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 */ void vlevout() { register int l = level ; while (l--) vout(" ") ; } void vlevnlout() { 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();} void vleft() { level++ ; vout("(") ; } void vright() { level-- ; voutln(")") ; } int forceoctal = 0 ; char vcharbuf[6] ; char *vchar(c) int c ; { if (forceoctal == 0 && isalnum (c)) (void) sprintf(vcharbuf,"C %c", #ifndef VMCMS c) ; #else ascii2ebcdic[c]) ; #endif else (void) sprintf(vcharbuf,"O %o", (unsigned)c) ; return (vcharbuf) ; } char vnamebuf[100]; char *vname (c) int c; { if (!forceoctal && isalnum (c)) { vnamebuf[0] = 0; } else { sprintf (vnamebuf, " (comment %s)", texptrs[c]->adobename); } return vnamebuf; } void writevpl() { 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)") ; (void)sprintf(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] ; char *base_encoding = #ifndef VMCMS codingscheme ; #else ebcodingscheme ; #endif if (strcmp (outencoding->name, base_encoding) == 0) { sprintf(tbuf, "%s", outencoding->name); } else { sprintf(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") ; } void usage(f) FILE *f ; { (void)fprintf(f, "afm2tfm 8.1, Copyright 1990-97 by Radical Eye Software\n") ; (void)fprintf(f, "Usage: afm2tfm foo[.afm] [-O] [-u] [-v|-V bar[.vpl]]\n") ; (void)fprintf(f, " [-e expansion] [-s slant] [-c capheight]\n") ; (void)fprintf(f, " [-p|-t|-T encodingfile] [foo[.tfm]]\n") ; } void openfiles(argc, argv) int argc ; char *argv[] ; { register int lastext ; register int i ; 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 */ (void)sprintf(titlebuf, "afm2tfm %s", argv[1]) ; #else #ifdef VMCMS /* Make VPL file identical to that created under Unix */ (void)sprintf(titlebuf, "afm2tfm %s", argv[1]) ; #else (void)sprintf(titlebuf, "%s %s", argv[0], argv[1]) ; #endif #endif (void)strcpy(inname, argv[1]) ; lastext = -1 ; for (i=0; inname[i]; i++) if (inname[i] == '.') lastext = i ; else if (inname[i] == '/' || inname[i] == ':') lastext = -1 ; if (lastext == -1) (void)strcat(inname, ".afm") ; 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++ ; (void)strcpy(outname, argv[3]) ; lastext = -1 ; for (i=0; outname[i]; i++) if (outname[i] == '.') lastext = i ; else if (outname[i] == '/' || outname[i] == ':') lastext = -1 ; if (lastext == -1) (void)strcat(outname, ".vpl") ; #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 'e': if (sscanf(argv[3], "%f", &efactor)==0 || efactor<0.01) error("! Bad extension factor") ; efactorparam = argv[3] ; break ; case 'c': if (sscanf(argv[3], "%f", &capheight)==0 || capheight<0.01) error("! Bad small caps height") ; break ; case 's': if (sscanf(argv[3], "%f", &slant)==0) error("! Bad slant parameter") ; slantparam = argv[3] ; break ; case 'P': case 'p': inenname = argv[3] ; break ; case 'T': inenname = outenname = argv[3] ; break ; case 't': outenname = argv[3] ; break ; case 'O': forceoctal = 1 ; arginc = 1 ; break ; case 'u': pedantic = 1 ; arginc = 1 ; break ; default: (void)fprintf(stderr, "Unknown option %s %s will be ignored.\n", argv[2], argv[3]) ; } for (i=0; i3 || (argc==3 && *argv[2]=='-')) { usage(stderr) ; error("! incorrect usage") ; } if (argc == 2) (void)strcpy(outname, inname) ; else (void)strcpy(outname, argv[2]) ; 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) ; (void)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) ; } /* * Some routines to remove kerns that match certain patterns. */ struct kern *rmkernmatch(k, s) 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. */ void rmkern(s1, s2, ai) char *s1, *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. */ void addkern (s1, s2) char *s1, *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 `%'. */ void checkligkern(s) 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 */ char *gettoken() { char *p, *q ; while (1) { while (param == 0 || *param == 0) { if (gaetline() == 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 ; } } } } void getligkerndefaults() { 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. */ struct encoding *readencoding(enc) 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) { afmin = fopen(enc, "r") ; param = 0 ; if (afmin == 0) error("! couldn't open that encoding file") ; 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 (gaetline()) { 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. */ void conspsfonts() { (void)printf("%s %s", outname, #ifndef VMCMS fontname) ; #else /* VM/CMS: fontname is ascii, so we use ebfontname */ ebfontname) ; #endif if (slantparam || efactorparam || inenname) { (void)printf(" \"") ; if (slantparam) (void)printf(" %s SlantFont", slantparam) ; if (efactorparam) (void)printf(" %s ExtendFont", efactorparam) ; if (inenname) (void)printf(" %s ReEncodeFont", inencoding->name) ; (void)printf(" \"") ; if (inenname) (void)printf(" <%s", inenname) ; } (void)printf("\n") ; } #ifndef VMS int #endif main(argc, argv) int argc ; char *argv[] ; { int i ; 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() ; } exit(0) ; /*NOTREACHED*/ }