ttf.c: glyph outline extraction, simple and composite (item 4.3.7a)

ttf_glyph_outline() decodes simple-glyph flag/coordinate runs and
recursively resolves composite components into a caller-supplied point/
contour-end buffer, in Q48.16. Composite scale/rotation/skew transforms
are rejected with TTF_ERR_UNSUPPORTED rather than mis-rendered, since the
shared q48_mul/q48_div are unsigned-only; translation-only composites
(the only kind the v1 glyph repertoire uses) apply cleanly via q48_add.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Robert Allan James
2026-08-10 21:45:04 -04:00
co-authored by Claude Sonnet 5
parent 5f6cc054d4
commit 70b6918279
4 changed files with 471 additions and 6 deletions
+57
View File
@@ -55,6 +55,18 @@ extern "C" {
#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
@@ -125,6 +137,51 @@ uint32_t ttf_codepoint_to_glyph(const ttf_font_t *font, uint32_t codepoint);
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 __cplusplus
}
#endif