580 lines
12 KiB
C
580 lines
12 KiB
C
/*
|
|
* tfmaux.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 <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#include "ttf2tfm.h"
|
|
#include "newobj.h"
|
|
#include "tfmaux.h"
|
|
#include "errormsg.h"
|
|
|
|
|
|
#undef PI
|
|
#define PI 3.14159265358979323846264338327
|
|
|
|
|
|
struct sf /* we need this for subfont ligatures */
|
|
{
|
|
long sf_code;
|
|
int position;
|
|
};
|
|
|
|
|
|
static long nextd; /* smallest value that will give a different mincover */
|
|
|
|
|
|
static int lf, lh, nw, nh, nd, ni, nl, nk, ne, np;
|
|
static int bc, ec;
|
|
|
|
static long *header, *charinfo,
|
|
*width, *height, *depth,
|
|
*ligkern, *kerns, *tparam,
|
|
*italic;
|
|
|
|
|
|
static int source[257]; /* utility variables for sorting tfm arrays */
|
|
static int unsort[257];
|
|
|
|
|
|
/*
|
|
* A simple function for sorting sf_array (in inverse order)
|
|
*/
|
|
|
|
static int
|
|
compare_sf(const void *a, const void *b)
|
|
{
|
|
return (int)(((struct sf *)b)->sf_code - ((struct sf *)a)->sf_code);
|
|
}
|
|
|
|
|
|
/*
|
|
* The next routine simply scales something.
|
|
* Input is in TFM units per em. Output is in FIXFACTORths of units
|
|
* per em. We use 1 em = 1000 TFM units.
|
|
*/
|
|
|
|
static long
|
|
scale(long what)
|
|
{
|
|
return ((what / 1000) * FIXFACTOR) +
|
|
(((what % 1000) * FIXFACTOR) + 500) / 1000;
|
|
}
|
|
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
|
|
|
|
/*
|
|
* Tells how many clusters result, given max difference d.
|
|
*/
|
|
|
|
static int
|
|
mincover(long *what,
|
|
register long 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,
|
|
int *source,
|
|
int *unsort)
|
|
{
|
|
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] < d; j++)
|
|
{
|
|
what[j] = what[j+1];
|
|
source[j] = source[j+1];
|
|
}
|
|
what[j] = d;
|
|
source[j] = i;
|
|
}
|
|
|
|
i = mincover(what, 0L);
|
|
d = nextd;
|
|
while (mincover(what, d + d) > newn)
|
|
d += d;
|
|
while (mincover(what, d) > newn)
|
|
d = nextd;
|
|
|
|
i = 1;
|
|
j = 0;
|
|
while (i < oldn)
|
|
{
|
|
j++;
|
|
l = what[i];
|
|
unsort[source[i]] = j;
|
|
while (what[++i] <= l + d)
|
|
{
|
|
unsort[source[i]] = j;
|
|
if (i - j == oldn - newn)
|
|
d = 0;
|
|
}
|
|
what[j] = (l + what[i-1])/2;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
write16(register short what,
|
|
register FILE *out)
|
|
{
|
|
(void)fputc(what >> 8, out);
|
|
(void)fputc(what & 0xFF, out);
|
|
}
|
|
|
|
|
|
void
|
|
writearr(register long *p,
|
|
register int n,
|
|
register FILE *out)
|
|
{
|
|
while (n)
|
|
{
|
|
write16((short)(*p >> 16), out);
|
|
write16((short)(*p & 65535), out);
|
|
p++;
|
|
n--;
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
writesarr(long *what,
|
|
int len,
|
|
FILE *out)
|
|
{
|
|
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, out);
|
|
}
|
|
|
|
|
|
static long *
|
|
makebcpl(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--;
|
|
}
|
|
if (t)
|
|
*p++ = t;
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
static long
|
|
checksum(ttfinfo **array)
|
|
{
|
|
int i;
|
|
unsigned long s1 = 0, s2 = 0;
|
|
char *p;
|
|
ttfinfo *ti;
|
|
|
|
|
|
for (i = 0; i < 256; i++)
|
|
if (NULL != (ti = array[i]))
|
|
{
|
|
s1 = ((s1 << 1) ^ (s1 >> 31)) ^ ti->width; /* cyclic left shift */
|
|
s1 &= 0xFFFFFFFF; /* in case we're on a 64-bit machine */
|
|
|
|
for (p = ti->adobename; *p; p++)
|
|
s2 = (s2 * 3) + *p;
|
|
}
|
|
|
|
s1 = (s1 << 1) ^ s2;
|
|
return s1;
|
|
}
|
|
|
|
|
|
int
|
|
transform(register int x, register int y,
|
|
float ef, float sl)
|
|
{
|
|
register double acc;
|
|
|
|
|
|
acc = ef * x + sl * y;
|
|
return (int)(acc >= 0 ? floor(acc + 0.5) : ceil(acc - 0.5));
|
|
}
|
|
|
|
|
|
int
|
|
buildtfm(Font *fnt)
|
|
{
|
|
register int i, j;
|
|
register ttfinfo *ti;
|
|
int byte1, old_byte1, byte2;
|
|
long cksum;
|
|
double Slant;
|
|
char buffer[256];
|
|
struct sf sf_array[256];
|
|
|
|
|
|
if (fnt->subfont_ligs)
|
|
{
|
|
for (i = 0; i < 256; i++)
|
|
{
|
|
ti = fnt->inencptrs[i];
|
|
if (ti)
|
|
{
|
|
sf_array[i].sf_code = ti->charcode;
|
|
sf_array[i].position = i;
|
|
}
|
|
else
|
|
{
|
|
sf_array[i].sf_code = -1;
|
|
sf_array[i].position = -1;
|
|
}
|
|
}
|
|
/* we sort the subfont character codes before we build a ligkern list */
|
|
qsort(sf_array, 256, sizeof (struct sf), compare_sf);
|
|
|
|
/* we need to create dummy characters for the ligatures in case the
|
|
character slots of the affected codes are unused */
|
|
i = 0;
|
|
while (i < 256 && sf_array[i].sf_code > -1)
|
|
{
|
|
byte1 = sf_array[i].sf_code >> 8;
|
|
byte2 = sf_array[i].sf_code & 0xFF;
|
|
if (!fnt->inencptrs[byte1])
|
|
{
|
|
ti = newchar(fnt);
|
|
ti->llx = ti->lly = 0;
|
|
ti->urx = ti->ury = 0;
|
|
ti->width = 0;
|
|
fnt->inencptrs[byte1] = ti;
|
|
ti->incode = byte1;
|
|
ti->adobename = ".dummy";
|
|
}
|
|
if (!fnt->inencptrs[byte2])
|
|
{
|
|
ti = newchar(fnt);
|
|
ti->llx = ti->lly = 0;
|
|
ti->urx = ti->ury = 0;
|
|
ti->width = 0;
|
|
fnt->inencptrs[byte2] = ti;
|
|
ti->incode = byte2;
|
|
ti->adobename = ".dummy";
|
|
}
|
|
i++;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i <= 0xFF && fnt->inencptrs[i] == NULL; i++)
|
|
;
|
|
bc = i;
|
|
for (i = 0xFF; i >= 0 && fnt->inencptrs[i] == NULL; i--)
|
|
;
|
|
ec = i;
|
|
|
|
if (ec < bc)
|
|
{
|
|
if (fnt->sfdname)
|
|
return 0;
|
|
else
|
|
oops("No TTF characters.");
|
|
}
|
|
|
|
header = (long *)mymalloc(40000L);
|
|
cksum = checksum(fnt->inencptrs);
|
|
header[0] = cksum;
|
|
header[1] = 0xA00000; /* 10pt design size */
|
|
|
|
(void)makebcpl(header + 2, fnt->codingscheme, 39);
|
|
(void)makebcpl(header + 12, fnt->fullname, 19);
|
|
|
|
/* 4 bytes are left free for the unused FACE value */
|
|
|
|
buffer[0] = '\0';
|
|
strncat(buffer, "Created by `", 12);
|
|
strncat(buffer, fnt->titlebuf, 255 - 12 - 1);
|
|
strncat(buffer, "'", 1);
|
|
charinfo = makebcpl(header + 18, buffer, 255);
|
|
lh = charinfo - header;
|
|
|
|
width = charinfo + (ec - bc + 1);
|
|
width[0] = 0;
|
|
nw = 1;
|
|
|
|
for (i = bc; i <= ec; i++)
|
|
if (NULL != (ti = fnt->inencptrs[i]))
|
|
{
|
|
width[nw] = ti->width;
|
|
for (j = 1; width[j] != ti->width; j++)
|
|
;
|
|
ti->wptr = j;
|
|
if (j == nw)
|
|
nw++;
|
|
}
|
|
if (nw > 256)
|
|
oops("256 chars with different widths.");
|
|
|
|
depth = width + nw;
|
|
depth[0] = 0;
|
|
nd = 1;
|
|
|
|
for (i = bc; i <= ec; i++)
|
|
if (NULL != (ti = fnt->inencptrs[i]))
|
|
{
|
|
depth[nd] = -ti->lly;
|
|
for (j = 0; depth[j] != -ti->lly; j++)
|
|
;
|
|
ti->dptr = j;
|
|
if (j == nd)
|
|
nd++;
|
|
}
|
|
|
|
if (nd > 16)
|
|
{
|
|
remap(depth, nd, 16, source, unsort);
|
|
nd = 16;
|
|
for (i = bc; i <= ec; i++)
|
|
if (NULL != (ti = fnt->inencptrs[i]))
|
|
ti->dptr = unsort[ti->dptr];
|
|
}
|
|
|
|
height = depth + nd;
|
|
height[0] = 0;
|
|
nh = 1;
|
|
|
|
for (i = bc; i <= ec; i++)
|
|
if (NULL != (ti = fnt->inencptrs[i]))
|
|
{
|
|
height[nh] = ti->ury;
|
|
for (j = 0; height[j] != ti->ury; j++)
|
|
;
|
|
ti->hptr = j;
|
|
if (j == nh)
|
|
nh++;
|
|
}
|
|
|
|
if (nh > 16)
|
|
{
|
|
remap(height, nh, 16, source, unsort);
|
|
nh = 16;
|
|
for (i = bc; i <= ec; i++)
|
|
if (NULL != (ti = fnt->inencptrs[i]))
|
|
ti->hptr = unsort[ti->hptr];
|
|
}
|
|
|
|
italic = height + nh;
|
|
italic[0] = 0;
|
|
ni = 1;
|
|
|
|
for (i = bc; i <= ec; i++)
|
|
if (NULL != (ti = fnt->inencptrs[i]))
|
|
{
|
|
italic[ni] = ti->urx - ti->width;
|
|
if (italic[ni] < 0)
|
|
italic[ni] = 0;
|
|
for (j = 0; italic[j] != italic[ni]; j++)
|
|
;
|
|
ti->iptr = j;
|
|
if (j == ni)
|
|
ni++;
|
|
}
|
|
|
|
if (ni > 64)
|
|
{
|
|
remap(italic, ni, 64, source, unsort);
|
|
ni = 64;
|
|
for (i = bc; i <= ec; i++)
|
|
if (NULL != (ti = fnt->inencptrs[i]))
|
|
ti->iptr = unsort[ti->iptr];
|
|
}
|
|
|
|
for (i = bc; i <= ec; i++)
|
|
if (NULL != (ti = fnt->inencptrs[i]))
|
|
charinfo[i - bc] = ((long)(ti->wptr) << 24) +
|
|
((long)(ti->hptr) << 20) +
|
|
((long)(ti->dptr) << 16) +
|
|
((long)(ti->iptr) << 10);
|
|
else
|
|
charinfo[i - bc] = 0;
|
|
|
|
ligkern = italic + ni;
|
|
nl = 0;
|
|
|
|
if (fnt->subfont_ligs)
|
|
{
|
|
/* Now we build the ligature list. The ligature consisting of character
|
|
code byte1 + byte2 should yield the actual character. The fonts of
|
|
the HLaTeX package for Korean use this mechanism. */
|
|
|
|
old_byte1 = -1;
|
|
while (nl < 256 && sf_array[nl].sf_code > -1)
|
|
{
|
|
byte1 = sf_array[nl].sf_code >> 8;
|
|
byte2 = sf_array[nl].sf_code & 0xFF;
|
|
if (byte1 != old_byte1)
|
|
{
|
|
charinfo[byte1 - bc] += 0x100L + /* set the lig tag */
|
|
nl; /* set the position in array */
|
|
if (old_byte1 > -1)
|
|
ligkern[nl - 1] |= 0x80000000L; /* set the STOP byte in previous
|
|
ligkern command */
|
|
}
|
|
|
|
ligkern[nl] = ((long)byte2 << 16) +
|
|
(long)sf_array[nl].position;
|
|
old_byte1 = byte1;
|
|
nl++;
|
|
}
|
|
ligkern[nl - 1] |= 0x80000000L;
|
|
}
|
|
|
|
kerns = ligkern + nl;
|
|
nk = 0; /* kerns are omitted from raw TeX font */
|
|
|
|
Slant = fnt->slant - fnt->efactor * tan(fnt->italicangle * (PI / 180.0));
|
|
|
|
tparam = kerns + nk;
|
|
tparam[0] = (long)(FIXFACTOR * Slant + 0.5);
|
|
tparam[1] = scale((long)fnt->fontspace);
|
|
tparam[2] = (fnt->fixedpitch ? 0 : scale((long)(300 * fnt->efactor + 0.5)));
|
|
tparam[3] = (fnt->fixedpitch ? 0 : scale((long)(100 * fnt->efactor + 0.5)));
|
|
tparam[4] = scale((long)fnt->xheight);
|
|
tparam[5] = scale((long)(1000 * fnt->efactor + 0.5));
|
|
np = 6;
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
void
|
|
writetfm(Font *fnt)
|
|
{
|
|
FILE *out;
|
|
char *tfm_name;
|
|
int len = 0;
|
|
|
|
|
|
if (fnt->tfm_path)
|
|
len += strlen(fnt->tfm_path);
|
|
len += strlen(fnt->fullname);
|
|
len += strlen(fnt->tfm_ext);
|
|
len++;
|
|
|
|
tfm_name = (char *)mymalloc(len);
|
|
tfm_name[0] = '\0';
|
|
if (fnt->tfm_path)
|
|
strcat(tfm_name, fnt->tfm_path);
|
|
strcat(tfm_name, fnt->fullname);
|
|
strcat(tfm_name, fnt->tfm_ext);
|
|
|
|
if ((out = fopen(tfm_name, "wb")) == NULL)
|
|
oops("Cannot open tfm file `%s'.", tfm_name);
|
|
|
|
free(tfm_name);
|
|
|
|
|
|
lf = 6 + lh + (ec - bc + 1) + nw + nh + nd + ni + nl + nk + ne + np;
|
|
|
|
write16(lf, out);
|
|
write16(lh, out);
|
|
write16(bc, out);
|
|
write16(ec, out);
|
|
write16(nw, out);
|
|
write16(nh, out);
|
|
write16(nd, out);
|
|
write16(ni, out);
|
|
write16(nl, out);
|
|
write16(nk, out);
|
|
write16(ne, out);
|
|
write16(np, out);
|
|
writearr(header, lh, out);
|
|
writearr(charinfo, ec - bc + 1, out);
|
|
writesarr(width, nw, out);
|
|
writesarr(height, nh, out);
|
|
writesarr(depth, nd, out);
|
|
writesarr(italic, ni, out);
|
|
writearr(ligkern, nl, out);
|
|
writesarr(kerns, nk, out);
|
|
writearr(tparam, np, out);
|
|
|
|
free(header);
|
|
fclose(out);
|
|
}
|
|
|
|
|
|
/* end */
|