diff options
| author | eunhhu <[email protected]> | 2026-09-28 16:04:05 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-28 16:04:05 +0100 |
| commit | 960fa21762484d3014a6778a7673f15acf2d34bb (patch) | |
| tree | 9e9a38a919b3cb8ed82c84524b7dd72b289ce49e /text/shape.c | |
| parent | ddbbc39cc65946f59298eab546c906ad3dea6841 (diff) | |
| download | vega-960fa21762484d3014a6778a7673f15acf2d34bb.tar.gz vega-960fa21762484d3014a6778a7673f15acf2d34bb.zip | |
Map characters as HarfBuzz maps them, not as it lists them
The shaping section's character map came from hb_face_collect_unicodes.
HarfBuzz's own suite showed where that list and its mapping part: it cuts
a format 13 map short (AdobeBlank2's whole Unicode to one glyph), and a
symbol map (3,0) it searches through the private use area, Latin and, in an
old Arabic font, Arabic too. Now every code point is asked with
hb_font_get_nominal_glyph (about 30 ms a font) and the map carried as
groups: codes going to consecutive glyphs, or all to one (12 bytes each,
so a format 13 map stays small).
HarfBuzz's suite: 5011 of 5199 cases the same (1355 before). The installed
fonts (213) and the Noto ones (21) still shape as HarfBuzz does on random
runs.
Diffstat (limited to 'text/shape.c')
| -rw-r--r-- | text/shape.c | 44 |
1 files changed, 26 insertions, 18 deletions
diff --git a/text/shape.c b/text/shape.c index 0ea2968..926632d 100644 --- a/text/shape.c +++ b/text/shape.c @@ -180,22 +180,29 @@ bool vega_shaping_open(VegaShaping *s, const unsigned char *bytes, size_t size, s->size = size; s->upem = le16(bytes + 8); s->glyph_count = le16(bytes + 10); - s->cmap_count = le32(bytes + 12); + s->cmap_groups = le32(bytes + 12); uint32_t cmap = le32(bytes + 16), advances = le32(bytes + 20); if (!s->upem) REFUSE("units per em"); - if (!within(size, cmap, (uint64_t)s->cmap_count * 8)) REFUSE("character map"); + if (!within(size, cmap, (uint64_t)s->cmap_groups * 12)) REFUSE("character map"); s->cmap = bytes + cmap; - for (uint32_t i = 0; i < s->cmap_count; ++i) { - uint32_t code = le32(s->cmap + 8 * i), glyph = le32(s->cmap + 8 * i + 4); - if ((i && code <= le32(s->cmap + 8 * i - 8)) || !glyph || glyph >= s->glyph_count) - REFUSE("character map: entry %u", i); - } - for (uint32_t i = 0; i < s->cmap_count; ++i) { - uint32_t code = le32(s->cmap + 8 * i); - if (code > 0xFFFF) break; - uint16_t **page = &s->bmp[code >> 8]; - if (!*page && !(*page = calloc(256, sizeof(**page)))) REFUSE("out of memory"); - (*page)[code & 255] = (uint16_t)le32(s->cmap + 8 * i + 4); + for (uint32_t i = 0; i < s->cmap_groups; ++i) { + const unsigned char *g = s->cmap + 12 * (size_t)i; + uint32_t first = le32(g), last = le32(g + 4), glyph = le32(g + 8) & 0x7FFFFFFF; + bool same = le32(g + 8) >> 31; + if ((i && first <= le32(g - 8)) || last < first || last > 0x10FFFF || !glyph || + (uint64_t)glyph + (same ? 0 : last - first) >= s->glyph_count) + REFUSE("character map: group %u", i); + } + for (uint32_t i = 0; i < s->cmap_groups; ++i) { + const unsigned char *g = s->cmap + 12 * (size_t)i; + uint32_t first = le32(g), last = le32(g + 4), glyph = le32(g + 8) & 0x7FFFFFFF; + bool same = le32(g + 8) >> 31; + if (first > 0xFFFF) break; + for (uint32_t code = first; code <= last && code <= 0xFFFF; ++code) { + uint16_t **page = &s->bmp[code >> 8]; + if (!*page && !(*page = calloc(256, sizeof(**page)))) REFUSE("out of memory"); + (*page)[code & 255] = (uint16_t)(same ? glyph : glyph + (code - first)); + } } if (!within(size, advances, 2 * (uint64_t)s->glyph_count)) REFUSE("advances"); s->advances = bytes + advances; @@ -272,12 +279,13 @@ void vega_shaping_close(VegaShaping *s) { uint32_t vega_shaping_glyph(const VegaShaping *s, uint32_t code) { if (code <= 0xFFFF) return s->bmp[code >> 8] ? s->bmp[code >> 8][code & 255] : 0; - uint32_t low = 0, high = s->cmap_count; + uint32_t low = 0, high = s->cmap_groups; while (low < high) { - uint32_t middle = (low + high) / 2, c = le32(s->cmap + 8 * (size_t)middle); - if (c < code) low = middle + 1; - else if (c > code) high = middle; - else return le32(s->cmap + 8 * (size_t)middle + 4); + uint32_t middle = (low + high) / 2; + const unsigned char *g = s->cmap + 12 * (size_t)middle; + if (le32(g + 4) < code) low = middle + 1; + else if (le32(g) > code) high = middle; + else return le32(g + 8) >> 31 ? le32(g + 8) & 0x7FFFFFFF : le32(g + 8) + (code - le32(g)); } return 0; } |
