aboutsummaryrefslogtreecommitdiffstats
path: root/text/shape.c
diff options
context:
space:
mode:
authoreunhhu <[email protected]>2026-09-28 16:04:05 +0100
committereunhhu <[email protected]>2026-09-28 16:04:05 +0100
commit960fa21762484d3014a6778a7673f15acf2d34bb (patch)
tree9e9a38a919b3cb8ed82c84524b7dd72b289ce49e /text/shape.c
parentddbbc39cc65946f59298eab546c906ad3dea6841 (diff)
downloadvega-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.c44
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;
}