From ad547a6b5986815fda458221149728d9d9ab1d87 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Thu, 25 Feb 2021 19:22:25 +0000 Subject: restore Build,TODO from r57911 git-svn-id: svn://tug.org/texlive/trunk@57915 c570f23f-e606-0410-a88d-b1316a301751 --- Build/source/texk/dvipsk/afm2tfm.c | 2170 ++++++++++++++++++++++++++++++++++++ 1 file changed, 2170 insertions(+) create mode 100644 Build/source/texk/dvipsk/afm2tfm.c (limited to 'Build/source/texk/dvipsk/afm2tfm.c') diff --git a/Build/source/texk/dvipsk/afm2tfm.c b/Build/source/texk/dvipsk/afm2tfm.c new file mode 100644 index 00000000000..47e0e6c4a1f --- /dev/null +++ b/Build/source/texk/dvipsk/afm2tfm.c @@ -0,0 +1,2170 @@ +/* $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*/ +} + -- cgit v1.2.3