diff --git a/FABRIC.md b/FABRIC.md index 0319c69..b92b594 100644 --- a/FABRIC.md +++ b/FABRIC.md @@ -4753,10 +4753,31 @@ document and committing that amendment as its own item.* > kernel-boot change yet" posture as 4.3.7 — no FORTH/capsule wiring exists for this module, > so no three-arch QEMU acceptance applies here either. -- [ ] **4.3.7b — Font data ingestion.** `.ttf` bytes encoded (hex or base64 — pick one, record +- [x] **4.3.7b — Font data ingestion.** `.ttf` bytes encoded (hex or base64 — pick one, record why) into capsule blocks per the resolved design decision (§27.7), decoded into a `kmalloc` buffer at capsule load time. *Done when:* a font capsule loads cleanly (`mkcapsule --lint`), and the decoded in-memory bytes checksum-match the original `.ttf` file. *Refs:* §27.7. + > **Done 2026-08-10, on the corrected design (§27.7's 2026-08-10 correction, not the + > hex/base64 premise this item's own text was written against — see that note for the + > full story of why).** `capsules/fonts/JetBrainsMono-Regular.ttf` added; `mkcapsule.c` + > ingests it unmodified as capsule `fonts:JetBrainsMono-Regular.ttf` (raw bytes, no text + > encoding, no tool changes needed — it already handles arbitrary files). Verified two ways: + > `mkcapsule --lint capsules/` passes clean (29 `.4th` files, 0 violations — the `.ttf` + > isn't `.4th` so lint correctly skips it rather than misapplying text-block rules to it); + > and a byte-exact round-trip check — built `capsule_generated.c`, extracted the + > `capsule_arena[]` bytes at the font's descriptor `offset`/`length` (270224 bytes) back out + > with a throwaway script, `cmp`'d against the source `.ttf`: identical. + > + > No `kmalloc` copy, since none is needed: `ttf_load_from_capsule()` (new, in `ttf.c`/`ttf.h`, + > `#ifdef __STARKERNEL__`-gated) resolves the capsule via `capsule_find_by_name()`, validates + > its content hash via `capsule_validate(..., verify_hash=1)`, and points `ttf_font_t` at the + > `capsule_arena` bytes directly (`capsule_get_payload()`) — zero-copy, since the arena is + > already a `const` array baked into the kernel image and `ttf_parse()` only ever reads + > through a `const uint8_t*`. Compiles clean (`-Wall -Werror -Wextra`) both with and without + > `-D__STARKERNEL__`, against the real amd64 `Makefile.starkernel` flags including + > `-include include/starforth_config.h -DPARITY_MODE=0`. Not callable from FORTH yet — no + > capsule-birth/init.4th wiring exists for it; that's 4.3.7e's job once `TTF-TEXT` needs it. + > Same "not a kernel-boot change" posture as 4.3.7/4.3.7a; no three-arch QEMU run applies. - [ ] **4.3.7c — Rasterization.** Bézier curve flattening to line segments (reusing the existing `LINE`/Bresenham primitive where practical), then fill. Antialiasing approach is @@ -5561,4 +5582,28 @@ font (not chosen yet — needs a licensing check before any specific font is emb SIL OFL 1.1 (permissive, embedding/redistribution allowed) — resolves the licensing-check blocker above. +**Correction 2026-08-10, superseding both the paragraph above and decision #3:** the +"open question" recorded above was wrong — a grep-shallow check (`grep -n "blob\|binary"`) +missed the actual mechanism. A full read of `tools/mkcapsule.c` shows raw-binary-blob +ingestion already exists, end to end, and decision #3's hex/base64-encoding premise is +unnecessary: + +- `process_file()` reads *any* regular file under `capsules/` (recursively, via `nftw`) into + a `CapsuleEntry` as raw bytes. `validate_forth_blocks()` — the 64-char/16-line text check — + only runs when the filename ends in `.4th`; every other file is accepted as-is, unvalidated + and unmodified. +- `generate_output()`'s payload-arena emission (`const uint8_t capsule_arena[] = { 0x%02X, ... + }`) is a plain byte array, not a string literal — binary-safe, no NUL-termination + assumption, already exercises correctly on non-text content. +- Kernel-side, `capsule_find_by_name()` (`include/starkernel/capsule.h`) returns a + `CapsuleDesc*` (`offset`/`length` into the arena) independent of `capsule_exec_payload()` + (`capsule_loader.h`) — lookup and FORTH execution are already separate calls. A caller can + fetch a capsule's raw bytes by name and simply never call `capsule_exec_payload()` on it. + +So: drop `.ttf` bytes into `capsules/` as-is (e.g. `capsules/fonts/JetBrainsMono-Regular.ttf` +→ capsule name `fonts:JetBrainsMono-Regular.ttf`), fetch via `capsule_find_by_name()`, done — +no hex/base64 text-encoding, no `mkcapsule.c` changes, no parallel asset mechanism. Decision +#3 is superseded by this. 4.3.7b's remaining work is just wiring a `kmalloc` copy (or +direct-arena reference — 4.3.7b's own call) behind that lookup. + *Refs:* §25.5 (punch list), GAP-B2. diff --git a/capsules/fonts/JetBrainsMono-Regular.ttf b/capsules/fonts/JetBrainsMono-Regular.ttf new file mode 100644 index 0000000..711830e Binary files /dev/null and b/capsules/fonts/JetBrainsMono-Regular.ttf differ diff --git a/capsules/fonts/README.md b/capsules/fonts/README.md new file mode 100644 index 0000000..0c51cab --- /dev/null +++ b/capsules/fonts/README.md @@ -0,0 +1,30 @@ +# capsules/fonts/ + +Font capsule for FABRIC.md items 4.3.7/4.3.7b (TrueType rendering, adjunct +to the stroke font — §27.7). + +| File | License | Provenance | +|------|---------|------------| +| `JetBrainsMono-Regular.ttf` | SIL Open Font License, Version 1.1 | The JetBrains Mono Project Authors, copyright 2020 (https://github.com/JetBrains/JetBrainsMono). License string confirmed by reading the font's own embedded `name` table directly (`strings -e b JetBrainsMono-Regular.ttf`) — not taken on trust from the filename. | + +Chosen as the v1 test/default TTF font 2026-08-10 (FABRIC.md §27.7 "Update +2026-08-10"), resolving that item's licensing-check blocker. + +**Embedded as a raw-binary capsule, not text-encoded.** `tools/mkcapsule.c`'s +`process_file()` reads any regular file under `capsules/` as raw bytes and +only runs the 64-char/16-line `.4th` text-block validation +(`validate_forth_blocks()`) when the filename ends in `.4th` — everything +else, including this `.ttf`, is stored byte-for-byte in the generated +`capsule_arena[]` and content-hashed (xxHash64) exactly like every other +capsule. Capsule name: `fonts:JetBrainsMono-Regular.ttf` (colon-separated +relative path, per `build_capsule_name()`). Fetch it with +`capsule_find_by_name()` (`include/starkernel/capsule.h`) — that returns +`offset`/`length` into the arena without ever routing through +`capsule_exec_payload()`, so nothing tries to interpret the font bytes as +FORTH source. This superseded FABRIC.md §27.7 decision #3 (hex/base64 +text-encoding) — see that section's 2026-08-10 correction note. + +No SIL OFL 1.1 full license text is bundled here yet — only the font's own +embedded attribution string. Add the full `OFL.txt` alongside this file if/ +when redistribution requirements are checked in detail (OFL requires the +license text to accompany the font). diff --git a/include/starkernel/ttf.h b/include/starkernel/ttf.h index 82d3606..2fed5ec 100644 --- a/include/starkernel/ttf.h +++ b/include/starkernel/ttf.h @@ -182,6 +182,33 @@ typedef struct { */ int ttf_glyph_outline(const ttf_font_t *font, uint32_t glyph_index, ttf_outline_t *out); +#ifdef __STARKERNEL__ +#include "capsule.h" + +/** + * ttf_load_from_capsule - Resolve a font capsule by name and parse it, + * zero-copy (FABRIC.md item 4.3.7b). `out` borrows the capsule payload + * directly from `arena` — no kmalloc, no decode step, since the capsule + * is already a raw-byte match of the source `.ttf` (see + * capsules/fonts/README.md and FABRIC.md §27.7's 2026-08-10 correction: + * capsule storage needs no hex/base64 text-encoding, `tools/mkcapsule.c` + * already embeds arbitrary files as raw bytes). Validates the capsule's + * content hash (`capsule_validate(..., verify_hash=1)`) before parsing. + * + * @param capsule_name Colon-separated capsule name, e.g. + * "fonts:JetBrainsMono-Regular.ttf" + * @return TTF_OK, TTF_ERR_TABLE_MISSING if the capsule isn't found, or a + * capsule-validation/ttf_parse TTF_ERR_* code + */ +int ttf_load_from_capsule( + const CapsuleDirHeader *dir, + const CapsuleDesc *descs, + const CapsuleNameEntry *names, + const uint8_t *arena, + const char *capsule_name, + ttf_font_t *out); +#endif /* __STARKERNEL__ */ + #ifdef __cplusplus } #endif diff --git a/src/starkernel/hal/ttf.c b/src/starkernel/hal/ttf.c index a9f34f3..073da9f 100644 --- a/src/starkernel/hal/ttf.c +++ b/src/starkernel/hal/ttf.c @@ -521,4 +521,39 @@ int ttf_glyph_outline(const ttf_font_t *font, uint32_t glyph_index, ttf_outline_ out->point_count = 0; out->contour_count = 0; return decode_glyph_r(font, glyph_index, out, 0); -} \ No newline at end of file +} + +/* =========================================================================== + * 4.3.7b — font data ingestion (capsule-backed, zero-copy) + * =========================================================================== + */ + +#ifdef __STARKERNEL__ + +int ttf_load_from_capsule( + const CapsuleDirHeader *dir, + const CapsuleDesc *descs, + const CapsuleNameEntry *names, + const uint8_t *arena, + const char *capsule_name, + ttf_font_t *out) { + const CapsuleDesc *cap; + const uint8_t *payload; + CapsuleValidateResult vr; + + if (!dir || !descs || !names || !arena || !capsule_name || !out) + return TTF_ERR_BAD_TABLE; + + cap = capsule_find_by_name(dir, descs, names, capsule_name); + if (!cap) return TTF_ERR_TABLE_MISSING; + + vr = capsule_validate(cap, arena, dir->arena_size, 1); + if (vr != CAPSULE_VALID) return TTF_ERR_BAD_TABLE; + + payload = capsule_get_payload(cap, arena); + if (!payload) return TTF_ERR_BAD_TABLE; + + return ttf_parse(payload, (uint32_t) cap->length, out); +} + +#endif /* __STARKERNEL__ */ \ No newline at end of file