ttf_words.c: TTF-TEXT entry point, hmtx advance widths (item 4.3.7e)

Punch list §25 item 4.3.7e complete. New src/word_source/ttf_words.c
registers TTF-TEXT ( c-addr u x y size color -- ): lazily loads the v1
default font capsule + raster cache once, decodes UTF-8 (C
reimplementation mirroring capsules/fabric.4th's DECODE-UTF8 exactly),
looks up each glyph's cached bitmap, blits via fb_put_pixel, advances
the pen by the glyph's real hmtx advance width scaled to pixels.

Necessary plumbing: ttf_parse() now also locates hhea/hmtx, and
ttf_glyph_advance_width() reads a glyph's advance width -- required for
this item's own "proportional spacing correct" acceptance clause, no
advance-width data existed anywhere else in the parser. Verified in
tools/ttftest.c: A/a/0/space all read advance_width=600, correctly
uniform since JetBrainsMono-Regular.ttf is monospace.

(x,y) is raster pixel space (top-left origin, Y-down), deliberately not
the stroke font TEXT's Cartesian Y-up convention -- recorded explicitly
in ttf_words.h, not conflated.

Verified live, amd64, screendump: injected
S" Hi 4.3.7e!" 200 200 28 16777215 TTF-TEXT over a serial socket after
boot, no error, captured a screendump showing the string rendered
legibly with correct mixed-case/digit/punctuation glyphs and even
spacing. TTF-TEXT is this item's permanent deliverable, not a
throwaway probe. Compile-checked clean on all three architectures.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-10 23:11:40 -04:00
co-authored by Claude Sonnet 5
parent 2db4603d04
commit 715ebcc57e
8 changed files with 319 additions and 2 deletions
+29
View File
@@ -270,6 +270,34 @@ static void test_raster_cache(const ttf_font_t *font) {
}
}
/* Advance width (hmtx, needed for 4.3.7e's proportional spacing): no
* independent reference for this specific font's values, but
* JetBrainsMono is monospace, so every printable glyph's advance width
* should be identical and nonzero -- a real structural property, not an
* arbitrary assumption. */
static void test_advance_width(const ttf_font_t *font) {
static const uint32_t cps[] = {0x0041, 0x0061, 0x0030, 0x0020};
uint16_t first = 0;
size_t i;
for (i = 0; i < sizeof(cps) / sizeof(cps[0]); i++) {
uint32_t gid = ttf_codepoint_to_glyph(font, cps[i]);
uint16_t adv = ttf_glyph_advance_width(font, gid);
printf("U+%04X advance_width=%u\n", cps[i], adv);
if (i == 0) {
first = adv;
if (adv == 0) {
printf(" FAIL advance_width: U+%04X is 0\n", cps[i]);
failures++;
}
} else if (adv != first) {
printf(" FAIL advance_width: U+%04X=%u != U+%04X=%u (JetBrainsMono is monospace)\n",
cps[i], adv, cps[0], first);
failures++;
}
}
}
int main(int argc, char **argv) {
FILE *fp;
long size;
@@ -337,6 +365,7 @@ int main(int argc, char **argv) {
test_rasterize(&font, 0x0061, "'a'");
test_raster_cache(&font);
test_advance_width(&font);
free(buf);