X-Git-Url: https://git.distorted.org.uk/~mdw/sgt/halibut/blobdiff_plain/4691e240b46160d0b8470f6d51f9f4740f5ac75a..refs/heads/master:/in_sfnt.c diff --git a/in_sfnt.c b/in_sfnt.c index 0d51ff9..218bcbf 100644 --- a/in_sfnt.c +++ b/in_sfnt.c @@ -132,6 +132,7 @@ sfnt_decode offsubdir_decode[] = { #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 @@ -364,6 +365,18 @@ sfnt_decode kern_f0_pair_decode[] = { { 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; @@ -462,7 +475,7 @@ static char *sfnt_psname(font_info *fi) { namerecord *nr; if (!sfnt_findtable(sf, TAG_name, &ptr, &end)) { - error(err_sfntnotable, &sf->pos, "name"); + err_sfntnotable(&sf->pos, "name"); return NULL; } ptr = decode(t_name_decode, ptr, end, &name); @@ -483,7 +496,7 @@ static char *sfnt_psname(font_info *fi) { } } } - error(err_sfntnopsname, &sf->pos); + err_sfntnopsname(&sf->pos); return NULL; } @@ -493,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) { @@ -503,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) * @@ -515,63 +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)) { - error(err_sfntnotable, &sf->pos, "post"); - return; - } - ptr = decode(t_post_decode, ptr, end, &post); - if (ptr == NULL) { - error(err_sfntbadtable, &sf->pos, "post"); - return; - } + 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; - 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: - error(err_sfnttablevers, &sf->pos, "post"); - return; + for (i = 0; i < sf->nglyphs; i++) + sf->glyphsbyindex[i] = genglyph(i); } /* Construct glyphsbyname */ sf->glyphsbyname = snewn(sf->nglyphs, unsigned short); @@ -580,6 +620,42 @@ 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); } glyph sfnt_indextoglyph(sfnt *sf, unsigned idx) { @@ -614,31 +690,31 @@ void sfnt_getmetrics(font_info *fi) { 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)) { - error(err_sfntnotable, &sf->pos, "hhea"); + err_sfntnotable(&sf->pos, "hhea"); return; } if (decode(t_hhea_decode, ptr, end, &hhea) == NULL) { - error(err_sfntbadtable, &sf->pos, "hhea"); + err_sfntbadtable(&sf->pos, "hhea"); return; } if ((hhea.version & 0xffff0000) != 0x00010000) { - error(err_sfnttablevers, &sf->pos, "hhea"); + err_sfnttablevers(&sf->pos, "hhea"); return; } fi->ascent = hhea.ascent; fi->descent = hhea.descent; if (hhea.metricDataFormat != 0) { - error(err_sfnttablevers, &sf->pos, "hmtx"); + err_sfnttablevers(&sf->pos, "hmtx"); return; } if (!sfnt_findtable(sf, TAG_hmtx, &ptr, &end)) { - error(err_sfntnotable, &sf->pos, "hmtx"); + err_sfntnotable(&sf->pos, "hmtx"); return; } hmtx = snewn(hhea.numOfLongHorMetrics, unsigned); if (decoden(longhormetric_decode, ptr, end, hmtx, sizeof(*hmtx), hhea.numOfLongHorMetrics) == NULL) { - error(err_sfntbadtable, &sf->pos, "hmtx"); + err_sfntbadtable(&sf->pos, "hmtx"); return; } for (i = 0; i < sf->nglyphs; i++) { @@ -667,7 +743,7 @@ void sfnt_getmetrics(font_info *fi) { fi->descent = OS_2.sTypoDescender * FUNITS_PER_PT / sf->head.unitsPerEm; return; bados2: - error(err_sfntbadtable, &sf->pos, "OS/2"); + err_sfntbadtable(&sf->pos, "OS/2"); } /* @@ -737,16 +813,17 @@ static void sfnt_getkern(font_info *fi) { } return; bad: - error(err_sfntbadtable, &sf->pos, "kern"); + 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; @@ -760,7 +837,7 @@ 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)) { - error(err_sfntnotable, &sf->pos, "cmap"); + err_sfntnotable(&sf->pos, "cmap"); } base = ptr; ptr = decode(t_cmap_decode, ptr, end, &cmap); @@ -772,7 +849,9 @@ void sfnt_getmap(font_info *fi) { for (i = 0; i < cmap.numTables; i++) { if (!decode(uint16_decode, (char *)base + esd[i].offset, end, &format)) goto bad; - if ((esd[i].platformID == 0 && esd[i].encodingID == 3) || + 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, @@ -806,17 +885,28 @@ 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) goto bad; + 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) goto bad; + if (idx > sf->nglyphs) { + err_sfntbadglyph(&sf->pos, k); + continue; + } fi->bmp[k] = sfnt_indextoglyph(sf, idx); } } @@ -827,10 +917,10 @@ void sfnt_getmap(font_info *fi) { } } } - error(err_sfntnounicmap, &sf->pos); + err_sfntnounicmap(&sf->pos); return; bad: - error(err_sfntbadtable, &sf->pos, "cmap"); + err_sfntbadtable(&sf->pos, "cmap"); } void read_sfnt_file(input *in) { @@ -839,6 +929,7 @@ void read_sfnt_file(input *in) { FILE *fp = in->currfp; font_info *fi = snew(font_info); void *ptr, *end; + t_maxp maxp; fi->name = NULL; fi->widths = newtree234(width_cmp); @@ -868,28 +959,41 @@ void read_sfnt_file(input *in) { sf->nglyphs = 0; ptr = decode(offsubdir_decode, sf->data, sf->end, &sf->osd); if (ptr == NULL) { - error(err_sfntbadhdr, &sf->pos); + err_sfntbadhdr(&sf->pos); return; } sf->td = snewn(sf->osd.numTables, tabledir); ptr = decoden(tabledir_decode, ptr, sf->end, sf->td, sizeof(*sf->td), sf->osd.numTables); if (ptr == NULL) { - error(err_sfntbadhdr, &sf->pos); + err_sfntbadhdr(&sf->pos); return; } if (!sfnt_findtable(sf, TAG_head, &ptr, &end)) { - error(err_sfntnotable, &sf->pos, "head"); + err_sfntnotable(&sf->pos, "head"); return; } if (decode(t_head_decode, ptr, end, &sf->head) == NULL) { - error(err_sfntbadtable, &sf->pos, "head"); + err_sfntbadtable(&sf->pos, "head"); return; } if ((sf->head.version & 0xffff0000) != 0x00010000) { - error(err_sfnttablevers, &sf->pos, "head"); + 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); @@ -957,13 +1061,13 @@ void sfnt_writeps(font_info const *fi, FILE *ofp) { breaks[i] = sf->td[i].offset; } if (!sfnt_findtable(sf, TAG_glyf, &glyfptr, &glyfend)) { - error(err_sfntnotable, &sf->pos, "glyf"); + 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)) { - error(err_sfntnotable, &sf->pos, "loca"); + err_sfntnotable(&sf->pos, "loca"); return; } loca = snewn(sf->nglyphs, unsigned); @@ -996,7 +1100,7 @@ void sfnt_writeps(font_info const *fi, FILE *ofp) { fprintf(ofp, "end /%s exch definefont\n", fi->name); return; badloca: - error(err_sfntbadtable, &sf->pos, "loca"); + err_sfntbadtable(&sf->pos, "loca"); } void sfnt_data(font_info *fi, char **bufp, size_t *lenp) {