ttf.c/ttf.h/ttftest.c: TrueType parser core -- sfnt/head/maxp/loca/glyf/cmap (item 4.3.7)
Freestanding C module resolving a Unicode codepoint (cmap format 4) to a glyph index and its outline header (contour count, bounding box), verified against an independent from-scratch Python reference reader via tools/ttftest.c. Not yet wired into the boot path or capsule system. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
b9b7cb2c6a
commit
5f6cc054d4
+126
@@ -0,0 +1,126 @@
|
||||
/*
|
||||
StarForth — Steady-State Virtual Machine Runtime
|
||||
|
||||
Copyright (c) 2023–2025 Robert A. James
|
||||
All rights reserved.
|
||||
|
||||
Licensed under the StarForth License, Version 1.0.
|
||||
*/
|
||||
|
||||
/**
|
||||
* ttftest.c — Host-side unit test for src/starkernel/hal/ttf.c (FABRIC.md
|
||||
* item 4.3.7). No QEMU needed, same pattern as tools/README.md's fbtest.c
|
||||
* entry (that file is itself missing from tools/ — stale doc, reported
|
||||
* separately, not fixed here).
|
||||
*
|
||||
* Expected values below were independently computed by a from-scratch
|
||||
* Python/struct reference reader (not this codebase's parser), against
|
||||
* JetBrainsMono-Regular.ttf.
|
||||
*
|
||||
* Usage: ./ttftest <path-to-ttf>
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "../include/starkernel/ttf.h"
|
||||
|
||||
typedef struct {
|
||||
uint32_t codepoint;
|
||||
uint32_t expect_glyph;
|
||||
int16_t expect_contours;
|
||||
int16_t expect_xmin, expect_ymin, expect_xmax, expect_ymax;
|
||||
uint32_t expect_glyf_offset;
|
||||
uint32_t expect_glyf_length;
|
||||
} expect_t;
|
||||
|
||||
static const expect_t EXPECTED[] = {
|
||||
/* cp glyph nc xmin ymin xmax ymax off len */
|
||||
{0x0041, 1, 2, 50, 0, 550, 730, 35536, 112}, /* 'A' */
|
||||
{0x0061, 198, 2, 67, -10, 508, 560, 46968, 244}, /* 'a' */
|
||||
{0x0030, 737, 3, 80, -10, 520, 740, 92152, 184}, /* '0' */
|
||||
{0x002E, 908, 1, 218, -10, 382, 152, 110044, 72}, /* '.' */
|
||||
{0x0021, 913, 2, 225, -5, 375, 730, 110632, 108}, /* '!' */
|
||||
{0x0040, 1151, 2, 45, -180, 560, 740, 144400, 224}, /* '@' */
|
||||
{0x0020, 821, 0, 0, 0, 0, 0, 99448, 0}, /* space, empty glyph */
|
||||
};
|
||||
|
||||
static int failures = 0;
|
||||
|
||||
static void check_eq_i(const char *what, uint32_t cp, long got, long want) {
|
||||
if (got != want) {
|
||||
printf(" FAIL U+%04X %s: got %ld want %ld\n", cp, what, got, want);
|
||||
failures++;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
FILE *fp;
|
||||
long size;
|
||||
uint8_t *buf;
|
||||
ttf_font_t font;
|
||||
int rc;
|
||||
size_t i;
|
||||
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "usage: %s <path-to-ttf>\n", argv[0]);
|
||||
return 2;
|
||||
}
|
||||
|
||||
fp = fopen(argv[1], "rb");
|
||||
if (!fp) {
|
||||
perror("fopen");
|
||||
return 2;
|
||||
}
|
||||
fseek(fp, 0, SEEK_END);
|
||||
size = ftell(fp);
|
||||
fseek(fp, 0, SEEK_SET);
|
||||
buf = malloc((size_t) size);
|
||||
if (!buf || fread(buf, 1, (size_t) size, fp) != (size_t) size) {
|
||||
fprintf(stderr, "read failed\n");
|
||||
return 2;
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
rc = ttf_parse(buf, (uint32_t) size, &font);
|
||||
if (rc != TTF_OK) {
|
||||
printf("ttf_parse failed: rc=%d\n", rc);
|
||||
return 1;
|
||||
}
|
||||
printf("parsed: unitsPerEm=%u numGlyphs=%u indexToLocFormat=%d has_cmap=%d\n",
|
||||
font.units_per_em, font.num_glyphs, font.index_to_loc_format, font.has_cmap);
|
||||
|
||||
for (i = 0; i < sizeof(EXPECTED) / sizeof(EXPECTED[0]); i++) {
|
||||
const expect_t *e = &EXPECTED[i];
|
||||
uint32_t gid = ttf_codepoint_to_glyph(&font, e->codepoint);
|
||||
ttf_glyph_header_t hdr;
|
||||
int hrc;
|
||||
|
||||
printf("U+%04X -> glyph %u (want %u)\n", e->codepoint, gid, e->expect_glyph);
|
||||
check_eq_i("glyph", e->codepoint, gid, e->expect_glyph);
|
||||
|
||||
hrc = ttf_glyph_header(&font, gid, &hdr);
|
||||
if (hrc != TTF_OK) {
|
||||
printf(" FAIL U+%04X ttf_glyph_header rc=%d\n", e->codepoint, hrc);
|
||||
failures++;
|
||||
continue;
|
||||
}
|
||||
check_eq_i("num_contours", e->codepoint, hdr.num_contours, e->expect_contours);
|
||||
check_eq_i("x_min", e->codepoint, hdr.x_min, e->expect_xmin);
|
||||
check_eq_i("y_min", e->codepoint, hdr.y_min, e->expect_ymin);
|
||||
check_eq_i("x_max", e->codepoint, hdr.x_max, e->expect_xmax);
|
||||
check_eq_i("y_max", e->codepoint, hdr.y_max, e->expect_ymax);
|
||||
check_eq_i("glyf_offset", e->codepoint, hdr.glyf_offset, e->expect_glyf_offset);
|
||||
check_eq_i("glyf_length", e->codepoint, hdr.glyf_length, e->expect_glyf_length);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
|
||||
if (failures) {
|
||||
printf("%d check(s) FAILED\n", failures);
|
||||
return 1;
|
||||
}
|
||||
printf("all checks passed\n");
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user