589 lines
16 KiB
C
589 lines
16 KiB
C
/*
|
|
* vplaux.c
|
|
*
|
|
* This file is part of the ttf2pk package.
|
|
*
|
|
* Copyright 1997-1999 by
|
|
* Frederic Loyer <loyer@ensta.fr>
|
|
* Werner Lemberg <wl@gnu.org>
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
|
|
#include "ttf2tfm.h"
|
|
#include "newobj.h"
|
|
#include "ttfenc.h"
|
|
#include "texenc.h"
|
|
#include "tfmaux.h"
|
|
#include "vplaux.h"
|
|
#include "errormsg.h"
|
|
#include "case.h"
|
|
|
|
|
|
#undef PI
|
|
#define PI 3.14159265358979323846264338327
|
|
|
|
|
|
#define vout(s) fprintf(out, s)
|
|
|
|
#define voutln(str) {fprintf(out, "%s\n", str); vlevout(level);}
|
|
#define voutln2(f, s) {fprintf(out, f, s); vlevnlout(level);}
|
|
#define voutln3(f, a, b) {fprintf(out, f, a, b); vlevnlout(level);}
|
|
#define voutln4(f, a, b, c) {fprintf(out, f, a, b, c); vlevnlout(level);}
|
|
|
|
|
|
static char vcharbuf[6];
|
|
static char vnamebuf[100];
|
|
|
|
/* the depth of parenthesis nesting in VPL file being written */
|
|
static int level;
|
|
|
|
|
|
static FILE *out;
|
|
|
|
|
|
static void
|
|
vlevout(register int l)
|
|
{
|
|
while (l--)
|
|
vout(" ");
|
|
}
|
|
|
|
|
|
static void
|
|
vlevnlout(int level)
|
|
{
|
|
vout("\n");
|
|
vlevout(level);
|
|
}
|
|
|
|
|
|
static void
|
|
vleft(int *levelp)
|
|
{
|
|
(*levelp)++;
|
|
vout("(");
|
|
}
|
|
|
|
|
|
static void
|
|
vright(int *levelp)
|
|
{
|
|
(*levelp)--;
|
|
voutln(")");
|
|
}
|
|
|
|
|
|
static char *
|
|
vchar(int c,
|
|
char *buf,
|
|
Boolean forceoctal)
|
|
{
|
|
if (forceoctal == 0 && isalnum(c))
|
|
(void)sprintf(buf, "C %c", c);
|
|
else
|
|
(void)sprintf(buf, "O %o", (unsigned)c);
|
|
return buf;
|
|
}
|
|
|
|
|
|
static char *
|
|
vname(int c,
|
|
char *buf,
|
|
ttfinfo **array,
|
|
Boolean forceoctal)
|
|
{
|
|
if (!forceoctal && isalnum(c))
|
|
buf[0] = '\0';
|
|
else
|
|
sprintf(buf, " (comment %s)", array[c]->adobename);
|
|
return buf;
|
|
}
|
|
|
|
|
|
static int
|
|
texheight(register ttfinfo *ti,
|
|
ttfinfo *ac,
|
|
int xh)
|
|
{
|
|
register char **p;
|
|
register ttfinfo *aci, *acci;
|
|
char buffer[200];
|
|
|
|
|
|
if (xh <= 50 || *(ti->adobename + 1))
|
|
return ti->ury; /* that was the simple case */
|
|
|
|
for (p = accents; *p; p++) /* otherwise we look for accented letters. */
|
|
/* We even check glyphs not in any encoding */
|
|
if (NULL != (aci = findadobe(*p, ac)))
|
|
{
|
|
strcpy(buffer, ti->adobename);
|
|
strcat(buffer, *p);
|
|
if (NULL != (acci = findadobe(buffer, ac)))
|
|
return acci->ury - aci->ury + xh;
|
|
}
|
|
return ti->ury;
|
|
}
|
|
|
|
|
|
/*
|
|
* Compute uppercase mapping, when making a small caps font.
|
|
*/
|
|
|
|
void
|
|
upmap(Font *fnt)
|
|
{
|
|
register ttfinfo *ti, *Ti;
|
|
register char *p, *q;
|
|
register pcc *np, *nq;
|
|
int i, j;
|
|
char lwr[50];
|
|
|
|
|
|
for (Ti = fnt->charlist; Ti; Ti = Ti->next)
|
|
{
|
|
p = Ti->adobename;
|
|
if (isupper(*p))
|
|
{
|
|
q = lwr;
|
|
for (; *p; p++)
|
|
*q++ = tolower(*p);
|
|
*q = '\0';
|
|
|
|
if (NULL != (ti = findmappedadobe(lwr, fnt->inencptrs)))
|
|
{
|
|
for (i = ti->outcode; i >= 0; i = fnt->nextout[i])
|
|
fnt->uppercase[i] = Ti;
|
|
for (i = Ti->outcode; i >= 0; i = fnt->nextout[i])
|
|
fnt->lowercase[i] = ti;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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 (NULL != (ti = findmappedadobe("germandbls", fnt->inencptrs)))
|
|
if (NULL != (Ti = findmappedadobe("S", fnt->inencptrs)))
|
|
/* we also construct SS */
|
|
{
|
|
for (i = ti->outcode; i >= 0; i = fnt->nextout[i])
|
|
fnt->uppercase[i] = ti;
|
|
ti->incode = -1;
|
|
ti->width = Ti->width << 1;
|
|
ti->llx = Ti->llx;
|
|
ti->lly = Ti->lly;
|
|
ti->urx = Ti->width + Ti->urx;
|
|
ti->ury = Ti->ury;
|
|
ti->kerns = Ti->kerns;
|
|
|
|
np = newpcc();
|
|
np->partname = "S";
|
|
nq = newpcc();
|
|
nq->partname = "S";
|
|
nq->xoffset = Ti->width;
|
|
np->next = nq;
|
|
ti->pccs = np;
|
|
ti->constructed = True;
|
|
}
|
|
|
|
for (i = 0; casetable[i].upper; i++)
|
|
{
|
|
if ((ti = findmappedadobe(casetable[i].lower, fnt->inencptrs)))
|
|
for (j = ti->outcode; j >= 0; j = fnt->nextout[j])
|
|
fnt->uppercase[j] = findmappedadobe(casetable[i].upper,
|
|
fnt->inencptrs);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
|
|
void
|
|
writevpl(Font *fnt, char makevpl, Boolean forceoctal)
|
|
{
|
|
register int i, j, k;
|
|
register ttfinfo *ti;
|
|
register lig *nlig;
|
|
register kern *nkern;
|
|
register pcc *npcc;
|
|
ttfinfo *asucc, *asub, *api;
|
|
ttfptr *kern_eq;
|
|
int xoff, yoff, ht;
|
|
int bc, ec;
|
|
char buf[200];
|
|
char header[256];
|
|
Boolean unlabeled;
|
|
float Slant;
|
|
|
|
|
|
out = fnt->vplout;
|
|
|
|
header[0] = '\0';
|
|
strncat(header, "Created by `", 12);
|
|
strncat(header, fnt->titlebuf, 255 - 12 - 1);
|
|
strncat(header, "'", 1);
|
|
|
|
voutln2("(VTITLE %s)", header);
|
|
voutln("(COMMENT Please change VTITLE if you edit this file)");
|
|
(void)sprintf(buf, "TeX-%s%s%s%s",
|
|
fnt->fullname,
|
|
(fnt->efactor == 1.0 ? "" : "-E"),
|
|
(fnt->slant == 0.0 ? "" : "-S"),
|
|
(makevpl == 1 ? "" : "-CSC"));
|
|
|
|
if (strlen(buf) > 19) /* too long, will retain first 9 and last 10 chars */
|
|
{
|
|
register char *p, *q;
|
|
|
|
|
|
for (p = &buf[9], q = &buf[strlen(buf)-10]; p < &buf[19];
|
|
p++, q++)
|
|
*p = *q;
|
|
buf[19] = '\0';
|
|
}
|
|
voutln2("(FAMILY %s)", buf);
|
|
|
|
{
|
|
char tbuf[300];
|
|
char *base_encoding = fnt->codingscheme;
|
|
|
|
|
|
if (strcmp(fnt->outencoding->name, base_encoding) == 0)
|
|
sprintf(tbuf, "%s", fnt->outencoding->name);
|
|
else
|
|
sprintf(tbuf, "%s + %s", base_encoding, fnt->outencoding->name);
|
|
|
|
if (strlen(tbuf) > 39)
|
|
{
|
|
warning("Coding scheme too long; shortening to 39 characters");
|
|
tbuf[39] = '\0';
|
|
}
|
|
voutln2("(CODINGSCHEME %s)", tbuf);
|
|
}
|
|
|
|
{
|
|
long t, sc;
|
|
char *s;
|
|
int n, pos;
|
|
|
|
|
|
s = header;
|
|
n = strlen(s);
|
|
t = ((long)n) << 24;
|
|
sc = 16;
|
|
pos = 18;
|
|
|
|
voutln(
|
|
"(COMMENT The following `HEADER' lines are equivalent to the string)");
|
|
voutln2("(COMMENT \"%s\")", header);
|
|
|
|
while (n > 0)
|
|
{
|
|
t |= ((long)(*(unsigned char *)s++)) << sc;
|
|
sc -= 8;
|
|
if (sc < 0)
|
|
{
|
|
voutln3("(HEADER D %d O %lo)", pos, t);
|
|
t = 0;
|
|
sc = 24;
|
|
pos++;
|
|
}
|
|
n--;
|
|
}
|
|
if (t)
|
|
voutln3("(HEADER D %d O %lo)", pos, t);
|
|
}
|
|
|
|
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)");
|
|
|
|
#if 0
|
|
/* Let vptovf compute the checksum. */
|
|
voutln2("(CHECKSUM O %lo)", cksum ^ 0xFFFFFFFF);
|
|
#endif
|
|
|
|
if (fnt->boundarychar >= 0)
|
|
voutln2("(BOUNDARYCHAR O %lo)", (unsigned long)fnt->boundarychar);
|
|
|
|
vleft(&level);
|
|
voutln("FONTDIMEN");
|
|
|
|
Slant = fnt->slant - fnt->efactor * tan(fnt->italicangle * (PI / 180.0));
|
|
|
|
if (Slant)
|
|
voutln2("(SLANT R %f)", Slant);
|
|
voutln2("(SPACE D %d)", fnt->fontspace);
|
|
if (!fnt->fixedpitch)
|
|
{
|
|
voutln2("(STRETCH D %d)", transform(200, 0, fnt->efactor, fnt->slant));
|
|
voutln2("(SHRINK D %d)", transform(100, 0, fnt->efactor, fnt->slant));
|
|
}
|
|
voutln2("(XHEIGHT D %d)", fnt->xheight);
|
|
voutln2("(QUAD D %d)", transform(1000, 0, fnt->efactor, fnt->slant));
|
|
voutln2("(EXTRASPACE D %d)",
|
|
fnt->fixedpitch ? fnt->fontspace :
|
|
transform(111, 0, fnt->efactor, fnt->slant));
|
|
vright(&level);
|
|
|
|
vleft(&level);
|
|
voutln("MAPFONT D 0");
|
|
voutln2("(FONTNAME %s)", fnt->fullname);
|
|
#if 0
|
|
voutln2("(FONTCHECKSUM O %lo)", (unsigned long)cksum);
|
|
#endif
|
|
vright(&level);
|
|
|
|
if (makevpl > 1)
|
|
{
|
|
vleft(&level);
|
|
voutln("MAPFONT D 1");
|
|
voutln2("(FONTNAME %s)", fnt->fullname);
|
|
voutln2("(FONTAT D %d)", (int)(1000.0 * fnt->capheight + 0.5));
|
|
#if 0
|
|
voutln2("(FONTCHECKSUM O %lo)", (unsigned long)cksum);
|
|
#endif
|
|
vright(&level);
|
|
}
|
|
|
|
for (i = 0; i <= 0xFF && fnt->outencptrs[i] == NULL; i++)
|
|
;
|
|
bc = i;
|
|
for (i = 0xFF; i >= 0 && fnt->outencptrs[i] == NULL; i--)
|
|
;
|
|
ec = i;
|
|
|
|
vleft(&level);
|
|
voutln("LIGTABLE");
|
|
ti = findadobe("||", fnt->charlist);
|
|
unlabeled = True;
|
|
for (nlig = ti->ligs; nlig; nlig = nlig->next)
|
|
if (NULL != (asucc = findmappedadobe(nlig->succ, fnt->inencptrs)))
|
|
{
|
|
if (NULL != (asub = findmappedadobe(nlig->sub, fnt->inencptrs)))
|
|
if (asucc->outcode >= 0)
|
|
if (asub->outcode >= 0)
|
|
{
|
|
if (unlabeled)
|
|
{
|
|
voutln("(LABEL BOUNDARYCHAR)");
|
|
unlabeled = False;
|
|
}
|
|
for (j = asucc->outcode; j >= 0; j = fnt->nextout[j])
|
|
voutln4("(%s %s O %o)", vplligops[nlig->op],
|
|
vchar(j, vcharbuf, forceoctal),
|
|
(unsigned)asub->outcode);
|
|
}
|
|
}
|
|
if (!unlabeled)
|
|
voutln("(STOP)");
|
|
|
|
for (i = bc; i <= ec; i++)
|
|
if ((ti = fnt->outencptrs[i]) && ti->outcode == i)
|
|
{
|
|
unlabeled = True;
|
|
if (fnt->uppercase[i] == NULL)
|
|
/* omit ligatures from smallcap lowercase */
|
|
for (nlig = ti->ligs; nlig; nlig = nlig->next)
|
|
if (NULL != (asucc = findmappedadobe(nlig->succ, fnt->inencptrs)))
|
|
if (NULL != (asub = findmappedadobe(nlig->sub, fnt->inencptrs)))
|
|
if (asucc->outcode >= 0)
|
|
if (asub->outcode >= 0)
|
|
{
|
|
if (unlabeled)
|
|
{
|
|
for (j = ti->outcode; j >= 0; j = fnt->nextout[j])
|
|
voutln3("(LABEL %s)%s",
|
|
vchar(j, vcharbuf, forceoctal),
|
|
vname(j, vnamebuf,
|
|
fnt->outencptrs, forceoctal));
|
|
unlabeled = False;
|
|
}
|
|
for (j = asucc->outcode; j >= 0; j = fnt->nextout[j])
|
|
{
|
|
voutln4("(%s %s O %o)", vplligops[nlig->op],
|
|
vchar(j, vcharbuf, forceoctal),
|
|
(unsigned)asub->outcode);
|
|
if (nlig->boundleft)
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (nkern = (fnt->uppercase[i] ? fnt->uppercase[i]->kerns : ti->kerns);
|
|
nkern; nkern=nkern->next)
|
|
if (NULL != (asucc = findmappedadobe(nkern->succ, fnt->inencptrs)))
|
|
for (j = asucc->outcode; j >= 0; j = fnt->nextout[j])
|
|
{
|
|
if (fnt->uppercase[j] == NULL)
|
|
{
|
|
if (unlabeled)
|
|
{
|
|
for (k = ti->outcode; k >= 0; k = fnt->nextout[k])
|
|
voutln3("(LABEL %s)%s",
|
|
vchar(k, vcharbuf, forceoctal),
|
|
vname(k, vnamebuf, fnt->outencptrs, forceoctal));
|
|
unlabeled = False;
|
|
}
|
|
|
|
/*
|
|
* 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 = ti->kern_equivs; kern_eq;
|
|
kern_eq = kern_eq->next)
|
|
{
|
|
k = kern_eq->ch->outcode;
|
|
if (k >= 0 && k <= 0xFF)
|
|
voutln3("(LABEL %s)%s",
|
|
vchar(k, vcharbuf, forceoctal),
|
|
vname(k, vnamebuf, fnt->outencptrs, forceoctal));
|
|
}
|
|
ti->kern_equivs = NULL; /* Only output those labels once. */
|
|
|
|
if (fnt->uppercase[i])
|
|
{
|
|
if (fnt->lowercase[j])
|
|
{
|
|
for (k = fnt->lowercase[j]->outcode; k >= 0;
|
|
k = fnt->nextout[k])
|
|
voutln4("(KRN %s R %.1f)%s",
|
|
vchar(k, vcharbuf, forceoctal),
|
|
fnt->capheight * nkern->delta,
|
|
vname(k, vnamebuf, fnt->outencptrs, forceoctal));
|
|
}
|
|
else
|
|
voutln4("(KRN %s R %.1f)%s",
|
|
vchar(j, vcharbuf, forceoctal),
|
|
fnt->capheight * nkern->delta,
|
|
vname(j, vnamebuf, fnt->outencptrs, forceoctal));
|
|
}
|
|
else
|
|
{
|
|
voutln4("(KRN %s R %d)%s",
|
|
vchar(j, vcharbuf, forceoctal),
|
|
nkern->delta,
|
|
vname(j, vnamebuf, fnt->outencptrs, forceoctal));
|
|
if (fnt->lowercase[j])
|
|
for (k = fnt->lowercase[j]->outcode; k >= 0;
|
|
k = fnt->nextout[k])
|
|
voutln4("(KRN %s R %.1f)%s",
|
|
vchar(k, vcharbuf, forceoctal),
|
|
fnt->capheight * nkern->delta,
|
|
vname(k, vnamebuf, fnt->outencptrs, forceoctal));
|
|
}
|
|
}
|
|
}
|
|
if (!unlabeled)
|
|
voutln("(STOP)");
|
|
}
|
|
vright(&level);
|
|
|
|
for (i = bc; i <= ec; i++)
|
|
if (NULL != (ti = fnt->outencptrs[i]))
|
|
{
|
|
vleft(&level);
|
|
fprintf(out, "CHARACTER %s%s\n ",
|
|
vchar(i, vcharbuf, forceoctal),
|
|
vname(i, vnamebuf, fnt->outencptrs, forceoctal));
|
|
|
|
if (fnt->uppercase[i])
|
|
{
|
|
ti = fnt->uppercase[i];
|
|
voutln2("(CHARWD R %.1f)", fnt->capheight * (ti->width));
|
|
if (0 != (ht = texheight(ti, fnt->charlist, fnt->xheight)))
|
|
voutln2("(CHARHT R %.1f)", fnt->capheight * ht);
|
|
if (ti->lly)
|
|
voutln2("(CHARDP R %.1f)", -fnt->capheight * ti->lly);
|
|
if (ti->urx > ti->width)
|
|
voutln2("(CHARIC R %.1f)", fnt->capheight * (ti->urx - ti->width));
|
|
}
|
|
else
|
|
{
|
|
voutln2("(CHARWD R %d)", ti->width);
|
|
if (0 != (ht = texheight(ti, fnt->charlist, fnt->xheight)))
|
|
voutln2("(CHARHT R %d)", ht);
|
|
if (ti->lly)
|
|
voutln2("(CHARDP R %d)", -ti->lly);
|
|
if (ti->urx > ti->width)
|
|
voutln2("(CHARIC R %d)", ti->urx - ti->width);
|
|
}
|
|
|
|
if (ti->incode != i || fnt->uppercase[i] || ti->constructed)
|
|
{
|
|
vleft(&level);
|
|
voutln("MAP");
|
|
if (fnt->uppercase[i])
|
|
voutln("(SELECTFONT D 1)");
|
|
|
|
if (ti->pccs && (ti->incode < 0 || ti->constructed))
|
|
{
|
|
xoff = 0;
|
|
yoff = 0;
|
|
|
|
for (npcc = ti->pccs; npcc; npcc = npcc->next)
|
|
if (NULL != (api = findmappedadobe(npcc->partname,
|
|
fnt->inencptrs)))
|
|
if (api->outcode >= 0)
|
|
{
|
|
if (npcc->xoffset != xoff)
|
|
{
|
|
if (fnt->uppercase[i])
|
|
{
|
|
voutln2("(MOVERIGHT R %.1f)",
|
|
fnt->capheight * (npcc->xoffset - xoff));
|
|
}
|
|
else
|
|
voutln2("(MOVERIGHT R %d)", npcc->xoffset - xoff);
|
|
|
|
xoff = npcc->xoffset;
|
|
}
|
|
|
|
if (npcc->yoffset != yoff)
|
|
{
|
|
if (fnt->uppercase[i])
|
|
{
|
|
voutln2("(MOVEUP R %.1f)",
|
|
fnt->capheight * (npcc->yoffset - yoff));
|
|
}
|
|
else
|
|
voutln2("(MOVEUP R %d)", npcc->yoffset - yoff);
|
|
|
|
yoff = npcc->yoffset;
|
|
}
|
|
|
|
voutln2("(SETCHAR O %o)", (unsigned)api->incode);
|
|
xoff += fnt->outencptrs[api->outcode]->width;
|
|
}
|
|
}
|
|
else
|
|
voutln2("(SETCHAR O %o)", (unsigned)ti->incode);
|
|
vright(&level);
|
|
}
|
|
vright(&level);
|
|
}
|
|
|
|
if (level)
|
|
oops("I forgot to match the parentheses.");
|
|
}
|
|
|
|
|
|
/* end */
|