From 2acf27f665f8a926d244efa58f6ef2c92d65e8c4 Mon Sep 17 00:00:00 2001 From: eunhhu Date: Sun, 27 Sep 2026 08:56:47 +0100 Subject: Store symbol outlines as point chains, and keep glyph advances whole Format 2 (VEGASYM2) writes each contour as a chain of 4-byte points, on or off the curve as in TrueType: endpoints are shared, lines have no control point, and two off points imply the on point between them. A curve is still had from one point and its two neighbours, so a GPU can point at curves by point index and upload the table as it is. Cubics (CFF, SVG curves and arcs) become quadratic splines the cu2qu way, joins left implied, within a unit. Files are about half of format 1: 1.3-1.4x a TrueType font's glyf (the whole subset font's size), 2.0-2.9x CFF, 2.6x smaller than the SVG icons. Opening checks each symbol's first and last point instead of every curve: 0.34 ms rather than 19 ms for 11,172 Hangul syllables. Points decode without branching on their flags (9.7 ns a point), still 1.2-10x cheaper than FreeType's outline. Glyph advances were half their width since outlines are doubled for FreeType's implied points; they now match hmtx, and the check says so. --- tests/check.py | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) (limited to 'tests/check.py') diff --git a/tests/check.py b/tests/check.py index c3bcfe2..288bd37 100644 --- a/tests/check.py +++ b/tests/check.py @@ -949,7 +949,7 @@ def symbols(tool, work): assert refused.returncode != 0 and refused.stdout == '', (name, refused.stdout) return refused.stderr.strip() - why = [damaged('short', good[:-1]), damaged('magic', b'VEGASYM2' + good[8:])] + why = [damaged('short', good[:-1]), damaged('magic', b'VEGASYM9' + good[8:])] swapped = bytearray(good) swapped[records:records + 24], swapped[records + 24:records + 48] = good[records + 24:records + 48], good[records:records + 24] why.append(damaged('order', swapped)) @@ -962,6 +962,16 @@ def symbols(tool, work): counted = bytearray(good) counted[12:16] = (count + 1).to_bytes(4, 'little') why.append(damaged('count', counted)) + # A symbol's first point starts a contour on the curve, and its last is + # on it, so an off point's neighbours never leave the symbol. + points = records + 24 * count + first, length = (int.from_bytes(good[records + k:records + k + 4], 'little') for k in (4, 8)) + unmoved = bytearray(good) + unmoved[points + 4 * first + 2] &= 0xFE + why.append(damaged('unmoved', unmoved)) + open_end = bytearray(good) + open_end[points + 4 * (first + length - 1)] |= 1 + why.append(damaged('open', open_end)) assert len(set(why)) == len(why), why report = {'icons': len(info['symbols'])} @@ -980,13 +990,14 @@ def symbols(tool, work): except ImportError: return report face = TTFont(font) - outlines, cmap = face.getGlyphSet(), face.getBestCmap() + outlines, cmap, metrics = face.getGlyphSet(), face.getBestCmap(), face['hmtx'] shown = json.loads(run(tool, 'info', glyphs).stdout) - k = shown['units'] / face['head'].unitsPerEm + assert shown['units'] == face['head'].unitsPerEm, shown['units'] # a TrueType font's points stay whole for symbol in shown['symbols']: pen = AreaPen() outlines[cmap[symbol['code']]].draw(pen) - assert abs(abs(pen.value) * k * k - abs(symbol['area'])) < 1, (chr(symbol['code']), pen.value, symbol) + assert abs(abs(pen.value) - abs(symbol['area'])) < 1, (chr(symbol['code']), pen.value, symbol) + assert symbol['advance'] == metrics[cmap[symbol['code']]][0], (chr(symbol['code']), symbol) report['exact_glyphs'] = len(shown['symbols']) return report -- cgit v1.2.3