diff options
| author | eunhhu <[email protected]> | 2026-09-28 12:48:45 +0100 |
|---|---|---|
| committer | eunhhu <[email protected]> | 2026-09-28 12:48:45 +0100 |
| commit | c38380464d29c4502c98d996236ccf890392fe86 (patch) | |
| tree | 2c510e29207427fd105f5bd87425deb5b01b208e /symbols/symbols.c | |
| parent | 16d84b2530e154795a825fbf2b7e028e2f209b71 (diff) | |
| download | vega-c38380464d29c4502c98d996236ccf890392fe86.tar.gz vega-c38380464d29c4502c98d996236ccf890392fe86.zip | |
Shape text as HarfBuzz does, from a symbol file, checked against HarfBuzz
The build tool compiles a font's shaping plans the way HarfBuzz's planner
does (features, stages, masks, lookups per script and direction) and
carries them with the font's character map, advances and GDEF, GSUB and
GPOS tables in a section of the symbol file (VEGASYM4); glyphs shaping
reaches become symbols of their own. A small engine ports HarfBuzz 10's
default, Arabic and Hebrew shaping step for step and reads the tables in
place, every read bounds-checked. The build refuses a file whose shaping
differs from HarfBuzz's on random runs: DejaVu Sans agrees on 600,000
runs in six scripts both ways, and every installed font the tool accepts
agrees too, at 1.05-1.18 times HarfBuzz's time.
Diffstat (limited to 'symbols/symbols.c')
| -rw-r--r-- | symbols/symbols.c | 19 |
1 files changed, 15 insertions, 4 deletions
diff --git a/symbols/symbols.c b/symbols/symbols.c index 2ee5d5e..71e617c 100644 --- a/symbols/symbols.c +++ b/symbols/symbols.c @@ -4,7 +4,9 @@ #include <stdlib.h> #include <string.h> -static const unsigned char magic[8] = {'V', 'E', 'G', 'A', 'S', 'Y', 'M', '3'}; +/* VEGASYM3, or VEGASYM4: flags in the header (VEGA_SYMBOLS_SHAPING) and + * what they name after the points. */ +static const unsigned char magic[7] = {'V', 'E', 'G', 'A', 'S', 'Y', 'M'}; static uint16_t u16(const unsigned char *p) { return (uint16_t)(p[0] | p[1] << 8); } static int16_t i16(const unsigned char *p) { return (int16_t)u16(p); } @@ -28,10 +30,19 @@ VegaPoint vega_symbols_point(const VegaSymbols *s, uint32_t index) { #define REFUSE(...) do { snprintf(error, error_size, __VA_ARGS__); return false; } while (0) bool vega_symbols_open(VegaSymbols *s, const unsigned char *bytes, size_t size, char *error, size_t error_size) { - if (size < VEGA_SYMBOLS_HEADER || memcmp(bytes, magic, sizeof(magic))) REFUSE("not a symbol file"); - *s = (VegaSymbols){bytes, size, u16(bytes + 8), i16(bytes + 24), i16(bytes + 26), u32(bytes + 12), u32(bytes + 16)}; + if (size < VEGA_SYMBOLS_HEADER || memcmp(bytes, magic, sizeof(magic)) || (bytes[7] != '3' && bytes[7] != '4')) + REFUSE("not a symbol file"); + *s = (VegaSymbols){bytes, size, u16(bytes + 8), i16(bytes + 24), i16(bytes + 26), u32(bytes + 12), u32(bytes + 16), NULL, 0}; uint64_t expected = VEGA_SYMBOLS_HEADER + (uint64_t)s->symbols * VEGA_SYMBOL_BYTES + (uint64_t)s->points * VEGA_POINT_BYTES; - if (u16(bytes + 10) || !s->units || s->ascent < s->descent) REFUSE("header"); + unsigned flags = u16(bytes + 10); + if ((flags && bytes[7] == '3') || (flags & ~(unsigned)VEGA_SYMBOLS_SHAPING) || !s->units || s->ascent < s->descent) + REFUSE("header"); + if (flags & VEGA_SYMBOLS_SHAPING) { + if (expected > size || size - expected < 8) REFUSE("size: no room for the shaping section"); + s->shaping = bytes + expected; + s->shaping_size = u32(s->shaping + 4); + expected += s->shaping_size; + } if (u32(bytes + 20) != size || expected != size) REFUSE("size: %zu bytes for %u symbols and %u points", size, s->symbols, s->points); for (uint32_t i = 0; i < s->symbols; ++i) { VegaSymbol symbol = vega_symbol_at(s, i); |
