X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/halibut/blobdiff_plain/47d8870658c716c4d6511f2e67865355c39ad077..refs/heads/master:/in_sfnt.c?ds=sidebyside
diff --git a/in_sfnt.c b/in_sfnt.c
index fe10c46..218bcbf 100644
--- a/in_sfnt.c
+++ b/in_sfnt.c
@@ -12,6 +12,9 @@
*
* The TrueType Reference Manual:
*
+ *
+ * Microsoft typography specifications:
+ *
*/
#include
@@ -121,12 +124,15 @@ sfnt_decode offsubdir_decode[] = {
};
#define sfnt_00010000 0x00010000
+#define TAG_OS_2 0x4f532f32
#define TAG_cmap 0x636d6170
#define TAG_glyf 0x676c7966
#define TAG_head 0x68656164
#define TAG_hhea 0x68686561
#define TAG_hmtx 0x686d7478
+#define TAG_kern 0x6b65726e
#define TAG_loca 0x6c6f6361
+#define TAG_maxp 0x6d617870
#define TAG_name 0x6e616d65
#define TAG_post 0x706f7374
#define sfnt_true 0x74727565
@@ -147,6 +153,55 @@ sfnt_decode tabledir_decode[] = {
{ d_end }
};
+/* OS/2 and Windows compatibility table */
+typedef struct t_OS_2_Tag t_OS_2;
+struct t_OS_2_Tag {
+ unsigned version;
+ int sTypoAscender, sTypoDescender;
+ int sxHeight, sCapHeight;
+};
+sfnt_decode t_OS_2_v0_decode[] = {
+ { d_uint16, offsetof(t_OS_2, version) },
+ { d_skip(66) }, /* xAvgCharWidth, usWeightClass, usWidthClass, fsType, */
+ /* ySubscriptXSize, ySubscriptYSize, ySubscriptXOffset, */
+ /* ySubscriptYOffset, ySuperscriptXSize, ySuperscriptYSize, */
+ /* ySuperscriptXOffset, ySupercriptYOffset, sFamilyClass, panose, */
+ /* ulUnicodeRange1, ulUnicodeRange2, ulUnicodeRange3, ulUnicodeRange4, */
+ /* achVendID, fsSelection, usFirstCharIndex, usLastCharIndex */
+ { d_end }
+};
+sfnt_decode t_OS_2_v1_decode[] = {
+ { d_uint16, offsetof(t_OS_2, version) },
+ { d_skip(66) }, /* xAvgCharWidth, usWeightClass, usWidthClass, fsType, */
+ /* ySubscriptXSize, ySubscriptYSize, ySubscriptXOffset, */
+ /* ySubscriptYOffset, ySuperscriptXSize, ySuperscriptYSize, */
+ /* ySuperscriptXOffset, ySupercriptYOffset, sFamilyClass, panose, */
+ /* ulUnicodeRange1, ulUnicodeRange2, ulUnicodeRange3, ulUnicodeRange4, */
+ /* achVendID, fsSelection, usFirstCharIndex, usLastCharIndex */
+ { d_int16, offsetof(t_OS_2, sTypoAscender) },
+ { d_int16, offsetof(t_OS_2, sTypoDescender) },
+ { d_skip(14) }, /* sTypoLineGap, usWinAscent, usWinDescent, */
+ /* ulCodePageRange1, ulCodePageRange2 */
+ { d_end }
+};
+sfnt_decode t_OS_2_v2_decode[] = {
+ { d_uint16, offsetof(t_OS_2, version) },
+ { d_skip(66) }, /* xAvgCharWidth, usWeightClass, usWidthClass, fsType, */
+ /* ySubscriptXSize, ySubscriptYSize, ySubscriptXOffset, */
+ /* ySubscriptYOffset, ySuperscriptXSize, ySuperscriptYSize, */
+ /* ySuperscriptXOffset, ySupercriptYOffset, sFamilyClass, panose, */
+ /* ulUnicodeRange1, ulUnicodeRange2, ulUnicodeRange3, ulUnicodeRange4, */
+ /* achVendID, fsSelection, usFirstCharIndex, usLastCharIndex */
+ { d_int16, offsetof(t_OS_2, sTypoAscender) },
+ { d_int16, offsetof(t_OS_2, sTypoDescender) },
+ { d_skip(14) }, /* sTypoLineGap, usWinAscent, usWinDescent, */
+ /* ulCodePageRange1, ulCodePageRange2 */
+ { d_int16, offsetof(t_OS_2, sxHeight) },
+ { d_int16, offsetof(t_OS_2, sCapHeight) },
+ { d_skip(6) }, /* usDefaultChar, usBreakChar, usMaxContext */
+ { d_end }
+};
+
/* Character to Glyph ('cmap') table */
typedef struct t_cmap_Tag t_cmap;
struct t_cmap_Tag {
@@ -238,6 +293,90 @@ sfnt_decode longhormetric_decode[] = {
{ d_end }
};
+/* Kerning ('kern') table */
+typedef struct t_kern_Tag t_kern;
+struct t_kern_Tag {
+ unsigned version;
+ unsigned nTables;
+};
+sfnt_decode t_kern_v0_decode[] = {
+ { d_uint16, offsetof(t_kern, version) },
+ { d_uint16, offsetof(t_kern, nTables) },
+ { d_end }
+};
+typedef struct kern_v0_subhdr_Tag kern_v0_subhdr;
+struct kern_v0_subhdr_Tag {
+ unsigned version;
+ unsigned length;
+ unsigned coverage;
+};
+sfnt_decode kern_v0_subhdr_decode[] = {
+ { d_uint16, offsetof(kern_v0_subhdr, version) },
+ { d_uint16, offsetof(kern_v0_subhdr, length) },
+ { d_uint16, offsetof(kern_v0_subhdr, coverage) },
+ { d_end }
+};
+#define KERN_V0_HORIZ 0x0001
+#define KERN_V0_MINIMUM 0x0002
+#define KERN_V0_CROSSSTREAM 0x0004
+#define KERN_V0_OVERRIDE 0x0008
+#define KERN_V0_FORMAT 0xff00
+#define KERN_V0_FORMAT_0 0x0000
+sfnt_decode t_kern_v1_decode[] = {
+ { d_uint32, offsetof(t_kern, version) },
+ { d_uint32, offsetof(t_kern, nTables) },
+ { d_end }
+};
+typedef struct kern_v1_subhdr_Tag kern_v1_subhdr;
+struct kern_v1_subhdr_Tag {
+ unsigned length;
+ unsigned coverage;
+};
+sfnt_decode kern_v1_subhdr_decode[] = {
+ { d_uint32, offsetof(kern_v1_subhdr, length) },
+ { d_uint16, offsetof(kern_v1_subhdr, coverage) },
+ { d_skip(2) }, /* tupleIndex */
+ { d_end }
+};
+#define KERN_V1_VERTICAL 0x8000
+#define KERN_V1_CROSSSTREAM 0x4000
+#define KERN_V1_VARIATION 0x2000
+#define KERN_V1_FORMAT 0x00ff
+#define KERN_V1_FORMAT_0 0x0000
+typedef struct kern_f0_Tag kern_f0;
+struct kern_f0_Tag {
+ unsigned nPairs;
+};
+sfnt_decode kern_f0_decode[] = {
+ { d_uint16, offsetof(kern_f0, nPairs) },
+ { d_skip(6) }, /* searchRange, entrySelector, rangeShift */
+ { d_end }
+};
+typedef struct kern_f0_pair_Tag kern_f0_pair;
+struct kern_f0_pair_Tag {
+ unsigned left;
+ unsigned right;
+ int value;
+};
+sfnt_decode kern_f0_pair_decode[] = {
+ { d_uint16, offsetof(kern_f0_pair, left) },
+ { d_uint16, offsetof(kern_f0_pair, right) },
+ { d_int16, offsetof(kern_f0_pair, value) },
+ { d_end }
+};
+
+/* Maximum profile ('maxp') table */
+typedef struct t_maxp_Tag t_maxp;
+struct t_maxp_Tag {
+ unsigned version;
+ unsigned numGlyphs;
+};
+sfnt_decode t_maxp_decode[] = {
+ { d_uint32, offsetof(t_maxp, version) },
+ { d_uint16, offsetof(t_maxp, numGlyphs) },
+ { d_end }
+};
+
/* Naming ('name') table */
typedef struct t_name_Tag t_name;
typedef struct namerecord_Tag namerecord;
@@ -299,11 +438,11 @@ typedef struct {
unsigned short index;
} glyphmap;
-typedef struct sfnt_Tag sfnt;
struct sfnt_Tag {
void *data;
size_t len;
void *end;
+ filepos pos;
offsubdir osd;
tabledir *td;
t_head head;
@@ -335,8 +474,10 @@ static char *sfnt_psname(font_info *fi) {
char *psname;
namerecord *nr;
- if (!sfnt_findtable(sf, TAG_name, &ptr, &end))
- abort();
+ if (!sfnt_findtable(sf, TAG_name, &ptr, &end)) {
+ err_sfntnotable(&sf->pos, "name");
+ return NULL;
+ }
ptr = decode(t_name_decode, ptr, end, &name);
name.nameRecord = snewn(name.count, namerecord);
ptr = decoden(namerecord_decode, ptr, sf->end, name.nameRecord,
@@ -355,6 +496,7 @@ static char *sfnt_psname(font_info *fi) {
}
}
}
+ err_sfntnopsname(&sf->pos);
return NULL;
}
@@ -364,6 +506,9 @@ static int glyphsbyname_cmp(void const *a, void const *b) {
glyph gb = cmp_glyphsbyindex[*(unsigned short *)b];
if (ga < gb) return -1;
if (ga > gb) return 1;
+ /* For de-duping, we'd prefer to have the first glyph stay first */
+ if (*(unsigned short *)a < *(unsigned short *)b) return -1;
+ if (*(unsigned short *)a > *(unsigned short *)b) return 1;
return 0;
}
static int glyphsbyname_cmp_search(void const *a, void const *b) {
@@ -374,6 +519,14 @@ static int glyphsbyname_cmp_search(void const *a, void const *b) {
return 0;
}
+/* Generate an name for a glyph that doesn't have one. */
+static glyph genglyph(unsigned idx) {
+ char buf[11];
+ if (idx == 0) return glyph_intern(".notdef");
+ sprintf(buf, "glyph%u", idx);
+ return glyph_intern(buf);
+}
+
/*
* Extract data from the 'post' table (mostly glyph mappings)
*
@@ -386,57 +539,79 @@ static void sfnt_mapglyphs(font_info *fi) {
void *ptr, *end;
unsigned char *sptr;
char tmp[256];
- glyph *extraglyphs;
- unsigned nextras, i, g;
+ glyph *extraglyphs, prev, this;
+ unsigned nextras, i, g, suflen;
sf->glyphsbyname = sf->glyphsbyindex = NULL;
- if (!sfnt_findtable(sf, TAG_post, &ptr, &end))
- abort();
- ptr = decode(t_post_decode, ptr, end, &post);
+ if (sfnt_findtable(sf, TAG_post, &ptr, &end)) {
+ ptr = decode(t_post_decode, ptr, end, &post);
+ if (ptr == NULL) {
+ err_sfntbadtable(&sf->pos, "post");
+ goto noglyphs;
+ }
- sf->minmem = post.minMemType42;
- sf->maxmem = post.maxMemType42;
- fi->italicangle = post.italicAngle / 65536.0;
- if (ptr == NULL) abort();
- switch (post.format) {
- case 0x00010000:
- sf->nglyphs = 258;
- sf->glyphsbyindex = (glyph *)tt_std_glyphs;
- break;
- case 0x00020000:
- if ((char *)ptr + 2 > (char *)end) return;
- decode_uint16(ptr, &sf->nglyphs);
- ptr = (char *)ptr + 2;
- if ((char *)ptr + 2*sf->nglyphs > (char *)end) return;
- nextras = 0;
- for (sptr = (unsigned char *)ptr + 2*sf->nglyphs;
- sptr < (unsigned char *)end;
- sptr += *sptr+1)
- nextras++;
- extraglyphs = snewn(nextras, glyph);
- i = 0;
- for (sptr = (unsigned char *)ptr + 2*sf->nglyphs;
- sptr < (unsigned char *)end;
- sptr += *sptr+1) {
- memcpy(tmp, sptr + 1, *sptr);
- tmp[*sptr] = 0;
- assert(i < nextras);
- extraglyphs[i++] = glyph_intern(tmp);
+ sf->minmem = post.minMemType42;
+ sf->maxmem = post.maxMemType42;
+ fi->italicangle = post.italicAngle / 65536.0;
+ switch (post.format) {
+ case 0x00010000:
+ if (sf->nglyphs != 258) {
+ err_sfntbadtable(&sf->pos, "post");
+ break;
+ }
+ sf->glyphsbyindex = (glyph *)tt_std_glyphs;
+ break;
+ case 0x00020000:
+ if ((char *)ptr + 2 > (char *)end) {
+ err_sfntbadtable(&sf->pos, "post");
+ break;
+ }
+ ptr = (char *)ptr + 2;
+ if ((char *)ptr + 2*sf->nglyphs > (char *)end) {
+ err_sfntbadtable(&sf->pos, "post");
+ break;
+ }
+ nextras = 0;
+ for (sptr = (unsigned char *)ptr + 2*sf->nglyphs;
+ sptr < (unsigned char *)end;
+ sptr += *sptr+1)
+ nextras++;
+ extraglyphs = snewn(nextras, glyph);
+ i = 0;
+ for (sptr = (unsigned char *)ptr + 2*sf->nglyphs;
+ sptr < (unsigned char *)end;
+ sptr += *sptr+1) {
+ memcpy(tmp, sptr + 1, *sptr);
+ tmp[*sptr] = 0;
+ assert(i < nextras);
+ extraglyphs[i++] = glyph_intern(tmp);
+ }
+ sf->glyphsbyindex = snewn(sf->nglyphs, glyph);
+ for (i = 0; i < sf->nglyphs; i++) {
+ decode_uint16((char *)ptr + 2*i, &g);
+ if (g <= 257)
+ sf->glyphsbyindex[i] = tt_std_glyphs[g];
+ else if (g < 258 + nextras)
+ sf->glyphsbyindex[i] = extraglyphs[g - 258];
+ else {
+ err_sfntbadtable(&sf->pos, "post");
+ sf->glyphsbyindex[i] = genglyph(i);
+ }
+ }
+ sfree(extraglyphs);
+ break;
+ case 0x00030000:
+ break;
+ default:
+ err_sfnttablevers(&sf->pos, "post");
+ break;
}
+ }
+ noglyphs:
+ if (!sf->glyphsbyindex) {
sf->glyphsbyindex = snewn(sf->nglyphs, glyph);
- for (i = 0; i < sf->nglyphs; i++) {
- decode_uint16((char *)ptr + 2*i, &g);
- if (g <= 257)
- sf->glyphsbyindex[i] = tt_std_glyphs[g];
- else if (g < 258 + nextras)
- sf->glyphsbyindex[i] = extraglyphs[g - 258];
- else
- sf->glyphsbyindex[i] = NOGLYPH;
- }
- sfree(extraglyphs);
- break;
- default:
- abort();
+ for (i = 0; i < sf->nglyphs; i++)
+ sf->glyphsbyindex[i] = genglyph(i);
}
/* Construct glyphsbyname */
sf->glyphsbyname = snewn(sf->nglyphs, unsigned short);
@@ -445,13 +620,53 @@ static void sfnt_mapglyphs(font_info *fi) {
cmp_glyphsbyindex = sf->glyphsbyindex;
qsort(sf->glyphsbyname, sf->nglyphs, sizeof(*sf->glyphsbyname),
glyphsbyname_cmp);
+ /*
+ * It's possible for fonts to specify the same name for multiple
+ * glyphs, which would make one of them inaccessible. Check for
+ * that, and rename all but one of each set.
+ *
+ * To ensure that we don't clash with any existing glyph names,
+ * our renaming involves appending the glyph number formatted with
+ * enough leading zeroes to make it longer than any all-digit
+ * suffix that already exists in the font.
+ */
+ suflen = 4;
+ for (i = 0; i < sf->nglyphs; i++) {
+ char const *p;
+ p = strrchr(glyph_extern(sfnt_indextoglyph(sf, i)), '.');
+ if (p && !(p+1)[strspn(p+1, "0123456789")] && strlen(p+1) > suflen)
+ suflen = strlen(p+1);
+ }
+ suflen++;
+ prev = sfnt_indextoglyph(sf, sf->glyphsbyname[0]);
+ for (i = 1; i < sf->nglyphs; i++) {
+ if (prev == (this = sfnt_indextoglyph(sf, sf->glyphsbyname[i]))) {
+ char const *basename;
+ char *buf;
+ basename = glyph_extern(this);
+ buf = snewn(strlen(basename) + 2 + suflen, char);
+ strcpy(buf, basename);
+ sprintf(buf + strlen(basename), ".%0*hu", suflen,
+ sf->glyphsbyname[i]);
+ sf->glyphsbyindex[sf->glyphsbyname[i]] = glyph_intern(buf);
+ sfree(buf);
+ }
+ prev = this;
+ }
+ /* We may have renamed some glyphs, so re-sort the array. */
+ qsort(sf->glyphsbyname, sf->nglyphs, sizeof(*sf->glyphsbyname),
+ glyphsbyname_cmp);
}
-static glyph sfnt_indextoglyph(sfnt *sf, unsigned short idx) {
+glyph sfnt_indextoglyph(sfnt *sf, unsigned idx) {
return sf->glyphsbyindex[idx];
}
-static unsigned short sfnt_glyphtoindex(sfnt *sf, glyph g) {
+unsigned sfnt_nglyphs(sfnt *sf) {
+ return sf->nglyphs;
+}
+
+unsigned sfnt_glyphtoindex(sfnt *sf, glyph g) {
cmp_glyphsbyindex = sf->glyphsbyindex;
return *(unsigned short *)bsearch(&g, sf->glyphsbyname, sf->nglyphs,
sizeof(*sf->glyphsbyname),
@@ -459,31 +674,49 @@ static unsigned short sfnt_glyphtoindex(sfnt *sf, glyph g) {
}
/*
- * Get data from 'hhea' and 'hmtx' tables
+ * Get data from 'hhea', 'hmtx', and 'OS/2' tables
*/
void sfnt_getmetrics(font_info *fi) {
sfnt *sf = fi->fontfile;
t_hhea hhea;
+ t_OS_2 OS_2;
void *ptr, *end;
unsigned i, j;
unsigned *hmtx;
- if (!sfnt_findtable(sf, TAG_hhea, &ptr, &end))
- abort();
- if (decode(t_hhea_decode, ptr, end, &hhea) == NULL)
- abort();
- if ((hhea.version & 0xffff0000) != 0x00010000)
- abort();
+ /* First, the bounding box from the 'head' table. */
+ fi->fontbbox[0] = sf->head.xMin * FUNITS_PER_PT / sf->head.unitsPerEm;
+ fi->fontbbox[1] = sf->head.yMin * FUNITS_PER_PT / sf->head.unitsPerEm;
+ fi->fontbbox[2] = sf->head.xMax * FUNITS_PER_PT / sf->head.unitsPerEm;
+ fi->fontbbox[3] = sf->head.yMax * FUNITS_PER_PT / sf->head.unitsPerEm;
+ if (!sfnt_findtable(sf, TAG_hhea, &ptr, &end)) {
+ err_sfntnotable(&sf->pos, "hhea");
+ return;
+ }
+ if (decode(t_hhea_decode, ptr, end, &hhea) == NULL) {
+ err_sfntbadtable(&sf->pos, "hhea");
+ return;
+ }
+ if ((hhea.version & 0xffff0000) != 0x00010000) {
+ err_sfnttablevers(&sf->pos, "hhea");
+ return;
+ }
fi->ascent = hhea.ascent;
fi->descent = hhea.descent;
- if (hhea.metricDataFormat != 0)
- abort();
- if (!sfnt_findtable(sf, TAG_hmtx, &ptr, &end))
- abort();
+ if (hhea.metricDataFormat != 0) {
+ err_sfnttablevers(&sf->pos, "hmtx");
+ return;
+ }
+ if (!sfnt_findtable(sf, TAG_hmtx, &ptr, &end)) {
+ err_sfntnotable(&sf->pos, "hmtx");
+ return;
+ }
hmtx = snewn(hhea.numOfLongHorMetrics, unsigned);
if (decoden(longhormetric_decode, ptr, end, hmtx, sizeof(*hmtx),
- hhea.numOfLongHorMetrics) == NULL)
- abort();
+ hhea.numOfLongHorMetrics) == NULL) {
+ err_sfntbadtable(&sf->pos, "hmtx");
+ return;
+ }
for (i = 0; i < sf->nglyphs; i++) {
glyph_width *w = snew(glyph_width);
w->glyph = sfnt_indextoglyph(sf, i);
@@ -491,14 +724,106 @@ void sfnt_getmetrics(font_info *fi) {
w->width = hmtx[j] * UNITS_PER_PT / sf->head.unitsPerEm;
add234(fi->widths, w);
}
+ /* Now see if the 'OS/2' table has any useful metrics */
+ if (!sfnt_findtable(sf, TAG_OS_2, &ptr, &end))
+ return;
+ if (decode(uint16_decode, ptr, end, &OS_2.version) == NULL)
+ goto bados2;
+ if (OS_2.version >= 2) {
+ if (decode(t_OS_2_v2_decode, ptr, end, &OS_2) == NULL)
+ goto bados2;
+ fi->xheight = OS_2.sxHeight * FUNITS_PER_PT / sf->head.unitsPerEm;
+ fi->capheight = OS_2.sCapHeight * FUNITS_PER_PT / sf->head.unitsPerEm;
+ } else if (OS_2.version == 1) {
+ if (decode(t_OS_2_v1_decode, ptr, end, &OS_2) == NULL)
+ goto bados2;
+ } else
+ return;
+ fi->ascent = OS_2.sTypoAscender * FUNITS_PER_PT / sf->head.unitsPerEm;
+ fi->descent = OS_2.sTypoDescender * FUNITS_PER_PT / sf->head.unitsPerEm;
+ return;
+ bados2:
+ err_sfntbadtable(&sf->pos, "OS/2");
+}
+
+/*
+ * Get kerning data from a 'kern' table
+ *
+ * 'kern' tables have two gratuitously different header formats, one
+ * used by Apple and one by Microsoft. Happily, the kerning tables
+ * themselves use the same formats. Halibut only supports simple kern
+ * pairs for horizontal kerning of horizontal text, and ignores
+ * everything else.
+ */
+static void sfnt_getkern(font_info *fi) {
+ sfnt *sf = fi->fontfile;
+ t_kern kern;
+ unsigned version, i, j;
+ void *ptr, *end;
+
+ if (!sfnt_findtable(sf, TAG_kern, &ptr, &end))
+ return;
+ if (!decode(uint16_decode, ptr, end, &version))
+ goto bad;
+ if (version == 0)
+ ptr = decode(t_kern_v0_decode, ptr, end, &kern);
+ else if (version == 1)
+ ptr = decode(t_kern_v1_decode, ptr, end, &kern);
+ else return;
+ if (ptr == NULL) goto bad;
+ for (i = 0; i < kern.nTables; i++) {
+ kern_f0 f0;
+ kern_pair *kerns;
+ if (version == 0) {
+ kern_v0_subhdr sub;
+ ptr = decode(kern_v0_subhdr_decode, ptr, end, &sub);
+ if (ptr == NULL) goto bad;
+ if (sub.version != 0 ||
+ (sub.coverage & (KERN_V0_HORIZ | KERN_V0_MINIMUM |
+ KERN_V0_CROSSSTREAM | KERN_V0_FORMAT)) !=
+ (KERN_V0_HORIZ | KERN_V0_FORMAT_0)) {
+ ptr = (char *)ptr + sub.length - 6;
+ continue;
+ }
+ } else {
+ kern_v1_subhdr sub;
+ ptr = decode(kern_v1_subhdr_decode, ptr, end, &sub);
+ if (ptr == NULL) goto bad;
+ if ((sub.coverage & (KERN_V1_VERTICAL | KERN_V1_CROSSSTREAM |
+ KERN_V1_VARIATION | KERN_V1_FORMAT)) !=
+ KERN_V0_FORMAT_0) {
+ ptr = (char *)ptr + sub.length - 8;
+ continue;
+ }
+ }
+ ptr = decode(kern_f0_decode, ptr, end, &f0);
+ if (ptr == NULL) goto bad;
+ kerns = snewn(f0.nPairs, kern_pair);
+ for (j = 0; j < f0.nPairs; j++) {
+ kern_f0_pair p;
+ kern_pair *kp = kerns + j;
+ ptr = decode(kern_f0_pair_decode, ptr, end, &p);
+ if (ptr == NULL) goto bad;
+ if (p.left >= sf->nglyphs || p.right >= sf->nglyphs) goto bad;
+ kp->left = sfnt_indextoglyph(sf, p.left);
+ kp->right = sfnt_indextoglyph(sf, p.right);
+ kp->kern = p.value * UNITS_PER_PT / (int)sf->head.unitsPerEm;
+ add234(fi->kerns, kp);
+ }
+ }
+ return;
+ bad:
+ err_sfntbadtable(&sf->pos, "kern");
+ return;
}
/*
* Get mapping data from 'cmap' table
*
- * We look for either a (0, 3), or (3, 1) table, both of these being
- * versions of UCS-2. We only handle format 4 of this table, since
- * that seems to be the only one in use.
+ * We look for either a (0, 0), (0, 2), (0, 3), or (3, 1) table, all
+ * of these being versions of UCS-2. We ignore (0, 1), since it's
+ * Unicode 1.1 with precomposed Hangul syllables. We only handle
+ * format 4 of this table, since that seems to be the only one in use.
*/
void sfnt_getmap(font_info *fi) {
sfnt *sf = fi->fontfile;
@@ -511,24 +836,27 @@ void sfnt_getmap(font_info *fi) {
for (i = 0; i < lenof(fi->bmp); i++)
fi->bmp[i] = 0xFFFF;
- if (!sfnt_findtable(sf, TAG_cmap, &ptr, &end))
- abort();
+ if (!sfnt_findtable(sf, TAG_cmap, &ptr, &end)) {
+ err_sfntnotable(&sf->pos, "cmap");
+ }
base = ptr;
ptr = decode(t_cmap_decode, ptr, end, &cmap);
- if (ptr == NULL) abort();
+ if (ptr == NULL) goto bad;
esd = snewn(cmap.numTables, encodingrec);
ptr = decoden(encodingrec_decode, ptr, end, esd, sizeof(*esd),
cmap.numTables);
- if (ptr == NULL) abort();
+ if (ptr == NULL) goto bad;
for (i = 0; i < cmap.numTables; i++) {
if (!decode(uint16_decode, (char *)base + esd[i].offset, end, &format))
- abort();
- if ((esd[i].platformID == 0 && esd[i].encodingID == 3) ||
+ goto bad;
+ if ((esd[i].platformID == 0 && esd[i].encodingID == 0) ||
+ (esd[i].platformID == 0 && esd[i].encodingID == 2) ||
+ (esd[i].platformID == 0 && esd[i].encodingID == 3) ||
(esd[i].platformID == 3 && esd[i].encodingID == 1)) {
/* UCS-2 encoding */
if (!decode(uint16_decode, (char *)base + esd[i].offset, end,
&format))
- abort();
+ goto bad;
if (format == 4) {
unsigned *data, *endCode, *startCode, *idDelta, *idRangeOffset;
unsigned *glyphIndexArray;
@@ -537,12 +865,12 @@ void sfnt_getmap(font_info *fi) {
ptr = decode(cmap4_decode, (char *)base + esd[i].offset, end,
&cmap4);
- if (!ptr) abort();
+ if (!ptr) goto bad;
segcount = cmap4.segCountX2 / 2;
nword = cmap4.length / 2 - 7;
data = snewn(nword, unsigned);
if (!decoden(uint16_decode, ptr, (char *)ptr + nword * 2,
- data, sizeof(*data), nword)) abort();
+ data, sizeof(*data), nword)) goto bad;
endCode = data;
startCode = data + segcount + 1;
idDelta = startCode + segcount;
@@ -557,27 +885,42 @@ void sfnt_getmap(font_info *fi) {
for (k = startCode[j]; k <= endCode[j]; k++) {
idx = (k + idDelta[j]) & 0xffff;
if (idx != 0) {
- if (idx > sf->nglyphs) abort();
+ if (idx > sf->nglyphs) {
+ err_sfntbadglyph(&sf->pos, k);
+ continue;
+ }
fi->bmp[k] = sfnt_indextoglyph(sf, idx);
}
}
} else {
unsigned startidx = idRangeOffset[j]/2 - segcount + j;
for (k = startCode[j]; k <= endCode[j]; k++) {
+ if (startidx + k - startCode[j] >=
+ nglyphindex) {
+ err_sfntbadglyph(&sf->pos, k);
+ continue;
+ }
idx = glyphIndexArray[startidx + k - startCode[j]];
if (idx != 0) {
idx = (idx + idDelta[j]) & 0xffff;
- if (idx > sf->nglyphs) abort();
+ if (idx > sf->nglyphs) {
+ err_sfntbadglyph(&sf->pos, k);
+ continue;
+ }
fi->bmp[k] = sfnt_indextoglyph(sf, idx);
}
}
}
}
-
sfree(data);
+ return;
}
}
}
+ err_sfntnounicmap(&sf->pos);
+ return;
+ bad:
+ err_sfntbadtable(&sf->pos, "cmap");
}
void read_sfnt_file(input *in) {
@@ -585,8 +928,8 @@ void read_sfnt_file(input *in) {
size_t off = 0, got;
FILE *fp = in->currfp;
font_info *fi = snew(font_info);
-/* size_t i; */
void *ptr, *end;
+ t_maxp maxp;
fi->name = NULL;
fi->widths = newtree234(width_cmp);
@@ -607,35 +950,58 @@ void read_sfnt_file(input *in) {
sf->len *= 2;
sf->data = sresize(sf->data, sf->len, unsigned char);
}
+ fclose(in->currfp);
sf->len = off;
sf->data = sresize(sf->data, sf->len, unsigned char);
sf->end = (char *)sf->data + sf->len;
- decode(offsubdir_decode, sf->data, sf->end, &sf->osd);
-/*
- fprintf(stderr, "scaler type = 0x%x; numTables = %u\n",
- sf->osd.scaler_type, sf->osd.numTables);
-*/
+ sf->pos = in->pos;
+ sf->pos.line = 0;
+ sf->nglyphs = 0;
+ ptr = decode(offsubdir_decode, sf->data, sf->end, &sf->osd);
+ if (ptr == NULL) {
+ err_sfntbadhdr(&sf->pos);
+ return;
+ }
sf->td = snewn(sf->osd.numTables, tabledir);
- decoden(tabledir_decode, (char *)sf->data + 12, sf->end,
- sf->td, sizeof(*sf->td), sf->osd.numTables);
-/*
- for (i = 0; i < sf->osd.numTables; i++)
- fprintf(stderr, "table tag = '%c%c%c%c'; offset = %#10x; length = %#10x\n",
- sf->td[i].tag >> 24, sf->td[i].tag >> 16, sf->td[i].tag >> 8, sf->td[i].tag, sf->td[i].offset, sf->td[i].length);
-*/
- if (!sfnt_findtable(sf, TAG_head, &ptr, &end))
- abort();
- if (decode(t_head_decode, ptr, end, &sf->head) == NULL)
- abort();
- if ((sf->head.version & 0xffff0000) != 0x00010000)
- abort();
+ ptr = decoden(tabledir_decode, ptr, sf->end, sf->td, sizeof(*sf->td),
+ sf->osd.numTables);
+ if (ptr == NULL) {
+ err_sfntbadhdr(&sf->pos);
+ return;
+ }
+ if (!sfnt_findtable(sf, TAG_head, &ptr, &end)) {
+ err_sfntnotable(&sf->pos, "head");
+ return;
+ }
+ if (decode(t_head_decode, ptr, end, &sf->head) == NULL) {
+ err_sfntbadtable(&sf->pos, "head");
+ return;
+ }
+ if ((sf->head.version & 0xffff0000) != 0x00010000) {
+ err_sfnttablevers(&sf->pos, "head");
+ return;
+ }
+ if (!sfnt_findtable(sf, TAG_maxp, &ptr, &end)) {
+ err_sfntnotable(&sf->pos, "maxp");
+ return;
+ }
+ if (decode(t_maxp_decode, ptr, end, &maxp) == NULL) {
+ err_sfntbadtable(&sf->pos, "maxp");
+ return;
+ }
+ if (maxp.version < 0x00005000 || maxp.version > 0x0001ffff) {
+ err_sfnttablevers(&sf->pos, "maxp");
+ return;
+ }
+ sf->nglyphs = maxp.numGlyphs;
fi->name = sfnt_psname(fi);
+ if (fi->name == NULL) return;
sfnt_mapglyphs(fi);
sfnt_getmetrics(fi);
+ sfnt_getkern(fi);
sfnt_getmap(fi);
fi->next = all_fonts;
all_fonts = fi;
- fclose(in->currfp);
}
static int sizecmp(const void *a, const void *b) {
@@ -656,6 +1022,7 @@ void sfnt_writeps(font_info const *fi, FILE *ofp) {
size_t *breaks, glyfoff, glyflen;
void *glyfptr, *glyfend, *locaptr, *locaend;
unsigned *loca;
+ int cc = 0;
/* XXX Unclear that this is the correct format. */
fprintf(ofp, "%%!PS-TrueTypeFont-%u-%u\n", sf->osd.scaler_type,
@@ -679,36 +1046,41 @@ void sfnt_writeps(font_info const *fi, FILE *ofp) {
(double)sf->head.yMax / sf->head.unitsPerEm);
} else {
/* Non-sensible font. */
- fprintf(ofp, "/FontBBox [0 0 0 0] readonly def");
+ fprintf(ofp, "/FontBBox [0 0 0 0] readonly def\n");
}
fprintf(ofp, "/PaintType 0 def\n");
fprintf(ofp, "/CharStrings %u dict dup begin\n", sf->nglyphs);
+ fprintf(ofp, "0 1 %u{currentfile token pop exch def}bind for\n",
+ sf->nglyphs - 1);
for (i = 0; i < sf->nglyphs; i++)
- fprintf(ofp, "/%s %u def\n",
- glyph_extern(sfnt_indextoglyph(sf, i)), i);
- fprintf(ofp, "end readonly def\n");
+ ps_token(ofp, &cc, "/%s", glyph_extern(sfnt_indextoglyph(sf, i)));
+ fprintf(ofp, "\nend readonly def\n");
fprintf(ofp, "/sfnts [<");
breaks = snewn(sf->osd.numTables + sf->nglyphs, size_t);
for (i = 0; i < sf->osd.numTables; i++) {
breaks[i] = sf->td[i].offset;
}
- if (!sfnt_findtable(sf, TAG_glyf, &glyfptr, &glyfend))
- abort();
+ if (!sfnt_findtable(sf, TAG_glyf, &glyfptr, &glyfend)) {
+ err_sfntnotable(&sf->pos, "glyf");
+ return;
+ }
glyfoff = (char *)glyfptr - (char *)sf->data;
glyflen = (char *)glyfend - (char *)glyfptr;
- if (!sfnt_findtable(sf, TAG_loca, &locaptr, &locaend))
- abort();
+ if (!sfnt_findtable(sf, TAG_loca, &locaptr, &locaend)) {
+ err_sfntnotable(&sf->pos, "loca");
+ return;
+ }
loca = snewn(sf->nglyphs, unsigned);
if (sf->head.indexToLocFormat == 0) {
if (!decoden(uint16_decode, locaptr, locaend, loca, sizeof(*loca),
- sf->nglyphs)) abort();
+ sf->nglyphs)) goto badloca;
for (i = 0; i < sf->nglyphs; i++) loca[i] *= 2;
} else {
if (!decoden(uint32_decode, locaptr, locaend, loca, sizeof(*loca),
- sf->nglyphs)) abort();
+ sf->nglyphs)) goto badloca;
}
for (i = 1; i < sf->nglyphs; i++) {
- if (loca[i] > glyflen) abort();
+ if (loca[i] > glyflen) goto badloca;
breaks[sf->osd.numTables + i - 1] = loca[i] + glyfoff;
}
breaks[sf->osd.numTables + sf->nglyphs - 1] = sf->len;
@@ -726,4 +1098,13 @@ void sfnt_writeps(font_info const *fi, FILE *ofp) {
fprintf(ofp, "00>] readonly def\n");
sfree(breaks);
fprintf(ofp, "end /%s exch definefont\n", fi->name);
+ return;
+ badloca:
+ err_sfntbadtable(&sf->pos, "loca");
+}
+
+void sfnt_data(font_info *fi, char **bufp, size_t *lenp) {
+ sfnt *sf = fi->fontfile;
+ *bufp = sf->data;
+ *lenp = sf->len;
}