/* StarForth — Steady-State Virtual Machine Runtime Copyright (c) 2023–2025 Robert A. James All rights reserved. This file is part of the StarForth project. Licensed under the StarForth License, Version 1.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at: https://github.com/star.4th@proton.me/StarForth/LICENSE.txt This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND, express or implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement. See the License for the specific language governing permissions and limitations under the License. */ /** * ttf.h - TrueType font parser core (Freestanding) * * FABRIC.md item 4.3.7. Reads a TTF's sfnt directory plus head/maxp/loca/ * glyf/cmap tables, resolving a Unicode codepoint to a glyph index and its * outline header (contour count, bounding box). Does NOT extract outline * points or rasterize — that is 4.3.7a/4.3.7c. No floating point; all * fields read here are raw integers straight from the font's own * big-endian on-disk format (see FABRIC.md §27.7 decision #2 for why the * Q48.16-vs-float call was made, and why it doesn't bind this file, which * never scales anything). * * cmap: only format 4 (Windows/Unicode BMP) subtables are resolved. This * covers ASCII and all of the BMP, which is what the v1 glyph repertoire * (§27.6.4) needs. Format 12 (supplementary planes) is deferred — no * v1 glyph requires it. */ #ifndef STARKERNEL_TTF_H #define STARKERNEL_TTF_H #include #include #include "q48_16.h" #ifdef __cplusplus extern "C" { #endif #define TTF_OK 0 #define TTF_ERR_BAD_SFNT -1 #define TTF_ERR_TABLE_MISSING -2 #define TTF_ERR_BAD_TABLE -3 #define TTF_ERR_BAD_GLYPH_INDEX -4 #define TTF_ERR_OUT_OF_BOUNDS -5 #define TTF_ERR_TOO_MANY_POINTS -6 #define TTF_ERR_TOO_MANY_CONTOURS -7 #define TTF_ERR_TOO_DEEP -8 /* composite glyph nesting exceeded TTF_MAX_COMPOSITE_DEPTH */ #define TTF_ERR_UNSUPPORTED -9 /* e.g. a non-identity composite transform or point-matched * component args — see ttf.c's file header comment; not a * malformed font, just a code path this parser doesn't * implement yet */ /* Recursion guard for nested composite glyphs (a component referencing a * component). The TTF spec doesn't hard-cap this; this is a defensive * limit for freestanding/kernel-stack safety. */ #define TTF_MAX_COMPOSITE_DEPTH 8 /** Glyph index returned for "no mapping" by ttf_codepoint_to_glyph(). */ #define TTF_GLYPH_MISSING 0 /** * Parsed font handle. Borrows the caller's buffer (does not copy or own * it) — the buffer must outlive the ttf_font_t. */ typedef struct { const uint8_t *data; uint32_t size; uint32_t head_off; uint32_t maxp_off; uint32_t loca_off; uint32_t loca_len; uint32_t glyf_off; uint32_t glyf_len; uint16_t units_per_em; int16_t index_to_loc_format; /* 0 = Offset16 (x2), 1 = Offset32 */ uint16_t num_glyphs; /* Selected cmap subtable (format 4 only, see file header comment). */ uint32_t cmap_subtable_off; int has_cmap; } ttf_font_t; /** Raw glyf record header, per §27.6/4.3.7's "done when" clause. */ typedef struct { int16_t num_contours; /* >= 0 simple glyph, < 0 composite glyph */ int16_t x_min; int16_t y_min; int16_t x_max; int16_t y_max; uint32_t glyf_offset; /* absolute file offset of this glyph's record */ uint32_t glyf_length; /* bytes; 0 for an empty glyph (e.g. space) */ } ttf_glyph_header_t; /** * ttf_parse - Locate and validate the sfnt directory and the head/maxp/ * loca/glyf tables (cmap is optional; ttf_codepoint_to_glyph() fails * cleanly if absent). Does not copy `data` — `out` borrows it. * * @param data Whole .ttf file contents * @param size Length of data in bytes * @param out Parsed handle to populate * @return TTF_OK, or a TTF_ERR_* code */ int ttf_parse(const uint8_t *data, uint32_t size, ttf_font_t *out); /** * ttf_codepoint_to_glyph - Resolve a Unicode codepoint via the font's * format-4 cmap subtable. * * @return glyph index, or TTF_GLYPH_MISSING if unmapped or no cmap */ uint32_t ttf_codepoint_to_glyph(const ttf_font_t *font, uint32_t codepoint); /** * ttf_glyph_header - Read a glyph's outline header (contour count, * bounding box) via loca + glyf. Does not extract contour points. * * @param glyph_index As returned by ttf_codepoint_to_glyph() * @param out Header to populate * @return TTF_OK, or a TTF_ERR_* code */ int ttf_glyph_header(const ttf_font_t *font, uint32_t glyph_index, ttf_glyph_header_t *out); /** One outline point, in raw font design units (NOT scaled by unitsPerEm — * that's the caller's job, same convention as ttf_glyph_header_t's bbox), * expressed in Q48.16. Two's-complement negative values are expected and * correct for q48_add/q48_sub and for q48_from_u64-style left-shift * conversion; this module never calls q48_mul/q48_div on outline * coordinates (see ttf.c's file header comment for why). */ typedef struct { q48_16_t x, y; uint8_t on_curve; } ttf_point_t; /** Simple- and composite-glyph outline, flattened to one point list plus * per-contour end indices (TrueType convention: contour_ends[c] is the * index of the LAST point of contour c, inclusive; points are shared * across contours only in the sense that contour c+1 starts right after * contour_ends[c]). Caller supplies both backing arrays — this module * never allocates. */ typedef struct { ttf_point_t *points; uint32_t max_points; uint32_t point_count; uint16_t *contour_ends; uint32_t max_contours; uint32_t contour_count; } ttf_outline_t; /** * ttf_glyph_outline - Extract a glyph's outline (simple or composite, * recursively resolving composite components) into caller-supplied * buffers. * * Composite components with a non-identity transform (any scale/rotation/ * skew, i.e. anything but a pure (dx,dy) translation) or with * point-matched (rather than xy-offset) placement args return * TTF_ERR_UNSUPPORTED rather than silently producing a wrong outline — * see ttf.c's file header comment for why, and check that limitation * before relying on this for an arbitrary font. * * @return TTF_OK, or a TTF_ERR_* code (including TTF_ERR_TOO_MANY_POINTS/ * _CONTOURS if a caller buffer is too small, and TTF_ERR_TOO_DEEP * if composite nesting exceeds TTF_MAX_COMPOSITE_DEPTH) */ 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 #endif /* STARKERNEL_TTF_H */