mkcapsule.c already ingests arbitrary non-.4th files as raw byte blobs (validate_forth_blocks only applies to .4th filenames), so no hex/base64 text-encoding or tool changes are needed -- corrects the design premise in FABRIC.md's 4.3.7b item text (see the FABRIC.md correction note this commit carries). ttf_load_from_capsule() resolves the font capsule by name, validates its content hash, and points ttf_font_t at the capsule arena bytes directly, zero-copy. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
216 lines
7.9 KiB
C
216 lines
7.9 KiB
C
/*
|
||
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 <stdint.h>
|
||
#include <stddef.h>
|
||
#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 */ |