std1.0.0-beta-7 SafeC docs

Source gui/gui_text.sc

View in the repository

// SafeC Standard Library — GUI: the TrueType/OpenType reader (see
// gui_text.h). Every read is bounds-checked against the font's size, so a
// truncated or malicious file yields missing glyphs, never a wild read.
#pragma once
#include <std/stddef.h>
#include <std/gui/gui_text.h>
#include <std/gui/gui_path.h>
#include <std/mem.h>
#include <std/math.h>
#include <std/io_file.h>
#include <std/io_traits.h>
#include <std/collections/vec.h>
#include <std/collections/string.h>

namespace std {

// ── big-endian reads ──────────────────────────────────────────────────────

static unsigned int __tt_u8(const &GuiTypeface f, unsigned long o) {
    if (o >= f.size) { return 0U; }
    unsafe { return (unsigned int)((const unsigned char*)f.data)[o]; }
}

static unsigned int __tt_u16(const &GuiTypeface f, unsigned long o) {
    if (o + 2UL > f.size) { return 0U; }
    unsafe {
        const unsigned char* p = (const unsigned char*)f.data + o;
        return ((unsigned int)p[0] << 8) | (unsigned int)p[1];
    }
}

static int __tt_i16(const &GuiTypeface f, unsigned long o) {
    unsigned int v = __tt_u16(f, o);
    return v >= 0x8000U ? (int)v - 65536 : (int)v;
}

static unsigned int __tt_u32(const &GuiTypeface f, unsigned long o) {
    if (o + 4UL > f.size) { return 0U; }
    unsafe {
        const unsigned char* p = (const unsigned char*)f.data + o;
        return ((unsigned int)p[0] << 24) | ((unsigned int)p[1] << 16) | ((unsigned int)p[2] << 8) | (unsigned int)p[3];
    }
}

static int __tt_tag(const &GuiTypeface f, unsigned long o, const char* tag) {
    unsafe {
        return (__tt_u8(f, o) == (unsigned int)(unsigned char)tag[0] && __tt_u8(f, o + 1UL) == (unsigned int)(unsigned char)tag[1] &&
                __tt_u8(f, o + 2UL) == (unsigned int)(unsigned char)tag[2] && __tt_u8(f, o + 3UL) == (unsigned int)(unsigned char)tag[3]) ? 1 : 0;
    }
}

// ── loading ───────────────────────────────────────────────────────────────

static void __tt_blank(&GuiTypeface f) {
    f.data = null;
    f.size = 0UL;
    f.owned = 0;
    f.faceIndex = 0;
    f.tHead = 0U; f.tHhea = 0U; f.tHmtx = 0U; f.tMaxp = 0U; f.tCmap = 0U; f.tLoca = 0U;
    f.tGlyf = 0U; f.tKern = 0U; f.tGpos = 0U; f.tOs2 = 0U; f.tName = 0U;
    f.lenGlyf = 0U; f.lenLoca = 0U;
    f.numGlyphs = 0; f.unitsPerEm = 1000; f.indexToLocFormat = 0; f.numHMetrics = 0;
    f.ascender = 800; f.descender = -200; f.lineGap = 0; f.capHeight = 700; f.xHeight = 500;
    f.weight = 400; f.italic = 0; f.hasOutlines = 0;
    f.cmapSub = 0U; f.cmapFormat = 0;
    f.kernSub = 0U; f.kernPairs = 0;
    f.gposPairs = Vec::new();
    f.family = String::new();
    f.cache = Vec::new();
    f.cacheIndex = Vec::new();
}

// Picks the best Unicode cmap subtable: full repertoire (format 12) first.
static void __tt_pick_cmap(&GuiTypeface f) {
    unsigned long base = (unsigned long)f.tCmap;
    unsigned int n = __tt_u16(f, base + 2UL);
    int bestScore = 0;
    unsigned int i = 0U;
    while (i < n) {
        unsigned long rec = base + 4UL + (unsigned long)i * 8UL;
        unsigned int plat = __tt_u16(f, rec);
        unsigned int enc = __tt_u16(f, rec + 2UL);
        unsigned long off = base + (unsigned long)__tt_u32(f, rec + 4UL);
        unsigned int fmt = __tt_u16(f, off);
        int score = 0;
        int unicode = (plat == 0U) || (plat == 3U && (enc == 1U || enc == 10U));
        if (unicode && fmt == 12U) { score = 5; }
        else if (unicode && fmt == 4U) { score = 4; }
        else if (unicode && fmt == 6U) { score = 3; }
        else if (plat == 3U && enc == 0U && fmt == 4U) { score = 2; }   // symbol fonts
        else if (fmt == 0U || fmt == 6U) { score = 1; }
        if (score > bestScore) {
            bestScore = score;
            f.cmapSub = (unsigned int)off;
            f.cmapFormat = (int)fmt;
        }
        i = i + 1U;
    }
}

static void __tt_find_kern(&GuiTypeface f) {
    if (f.tKern == 0U) { return; }
    unsigned long base = (unsigned long)f.tKern;
    if (__tt_u16(f, base) != 0U) { return; }       // Apple's version-1 'kern': not read
    unsigned int n = __tt_u16(f, base + 2UL);
    unsigned long off = base + 4UL;
    unsigned int i = 0U;
    while (i < n) {
        unsigned int len = __tt_u16(f, off + 2UL);
        unsigned int coverage = __tt_u16(f, off + 4UL);
        unsigned int format = coverage >> 8;
        // horizontal, not minimum/cross-stream, format 0
        if (format == 0U && (coverage & 1U) != 0U && (coverage & 6U) == 0U) {
            f.kernSub = (unsigned int)off;
            f.kernPairs = (int)__tt_u16(f, off + 6UL);
            return;
        }
        if (len < 6U) { return; }
        off = off + (unsigned long)len;
        i = i + 1U;
    }
}

// The PairPos subtables of the GPOS lookups the 'kern' feature uses.
static void __tt_find_gpos(&GuiTypeface f) {
    if (f.tGpos == 0U) { return; }
    unsigned long base = (unsigned long)f.tGpos;
    unsigned long featList = base + (unsigned long)__tt_u16(f, base + 6UL);
    unsigned long lookList = base + (unsigned long)__tt_u16(f, base + 8UL);
    unsigned int nfeat = __tt_u16(f, featList);
    unsigned int nlook = __tt_u16(f, lookList);
    // lookups referenced by any 'kern' feature (deduplicated)
    struct Vec<unsigned int> wanted = Vec::new();
    unsigned int i = 0U;
    while (i < nfeat) {
        unsigned long rec = featList + 2UL + (unsigned long)i * 6UL;
        if (__tt_tag(f, rec, "kern")) {
            unsigned long ft = featList + (unsigned long)__tt_u16(f, rec + 4UL);
            unsigned int cnt = __tt_u16(f, ft + 2UL);
            unsigned int k = 0U;
            while (k < cnt) {
                unsigned int li = __tt_u16(f, ft + 4UL + (unsigned long)k * 2UL);
                int seen = 0;
                unsigned long w = 0UL;
                while (w < wanted.len) { if (*wanted.at(w) == li) { seen = 1; } w = w + 1UL; }
                if (!seen && li < nlook) { wanted.push(li); }
                k = k + 1U;
            }
        }
        i = i + 1U;
    }
    // Lookup order is the order of the lookup list.
    unsigned int li = 0U;
    while (li < nlook) {
        int use = 0;
        unsigned long w = 0UL;
        while (w < wanted.len) { if (*wanted.at(w) == li) { use = 1; } w = w + 1UL; }
        if (use) {
            unsigned long lk = lookList + (unsigned long)__tt_u16(f, lookList + 2UL + (unsigned long)li * 2UL);
            unsigned int type = __tt_u16(f, lk);
            unsigned int nsub = __tt_u16(f, lk + 4UL);
            unsigned int s = 0U;
            while (s < nsub) {
                unsigned long sub = lk + (unsigned long)__tt_u16(f, lk + 6UL + (unsigned long)s * 2UL);
                unsigned int st = type;
                if (type == 9U) {      // extension: the real subtable is further away
                    st = __tt_u16(f, sub + 2UL);
                    sub = sub + (unsigned long)__tt_u32(f, sub + 4UL);
                }
                if (st == 2U) { f.gposPairs.push((unsigned int)sub); }
                s = s + 1U;
            }
        }
        li = li + 1U;
    }
    wanted.free();
}

// name table: the family name (nameID 16 typographic family, else 1).
static void __tt_read_name(&GuiTypeface f) {
    if (f.tName == 0U) { return; }
    unsigned long base = (unsigned long)f.tName;
    unsigned int count = __tt_u16(f, base + 2UL);
    unsigned long strings = base + (unsigned long)__tt_u16(f, base + 4UL);
    int bestScore = 0;
    unsigned long bestOff = 0UL;
    unsigned int bestLen = 0U;
    int bestWide = 0;
    unsigned int i = 0U;
    while (i < count) {
        unsigned long rec = base + 6UL + (unsigned long)i * 12UL;
        unsigned int plat = __tt_u16(f, rec);
        unsigned int enc = __tt_u16(f, rec + 2UL);
        unsigned int lang = __tt_u16(f, rec + 4UL);
        unsigned int nameId = __tt_u16(f, rec + 6UL);
        unsigned int len = __tt_u16(f, rec + 8UL);
        unsigned int off = __tt_u16(f, rec + 10UL);
        int score = 0;
        if (nameId == 1U || nameId == 16U) {
            score = nameId == 16U ? 2 : 1;
            if (plat == 3U && (enc == 1U || enc == 10U)) { score = score + (lang == 0x409U ? 8 : 6); }
            else if (plat == 0U) { score = score + 5; }
            else if (plat == 1U && enc == 0U) { score = score + 3; }
            else { score = 0; }
        }
        if (score > bestScore) {
            bestScore = score;
            bestOff = strings + (unsigned long)off;
            bestLen = len;
            bestWide = (plat == 3U || plat == 0U) ? 1 : 0;
        }
        i = i + 1U;
    }
    if (bestScore == 0) { return; }
    f.family.clear();
    if (bestWide) {
        unsigned int k = 0U;
        while (k + 1U < bestLen) {
            unsigned int u = __tt_u16(f, bestOff + (unsigned long)k);
            k = k + 2U;
            if (u >= 0xD800U && u <= 0xDBFFU && k + 1U < bestLen) {
                unsigned int lo = __tt_u16(f, bestOff + (unsigned long)k);
                k = k + 2U;
                u = 0x10000U + ((u - 0xD800U) << 10) + (lo - 0xDC00U);
            }
            char enc[4];
            int nb = gui_utf8_encode(u, &enc[0]);
            unsafe { f.family.push_n_(&enc[0], (unsigned long)nb); }
        }
    } else {
        unsigned int k = 0U;
        while (k < bestLen) {
            char enc[4];
            int nb = gui_utf8_encode(__tt_u8(f, bestOff + (unsigned long)k), &enc[0]);
            unsafe { f.family.push_n_(&enc[0], (unsigned long)nb); }
            k = k + 1U;
        }
    }
}

int GuiTypeface::load_memory_index_n_(const &unsigned char bytes, unsigned long size, int index, int flags) {
    __tt_blank(self);
    if (size < 12UL) { return 0; }
    if ((flags & GUI_FONT_COPY) != 0) {
        unsafe {
            unsigned char* copy = (unsigned char*)alloc_raw_(size);
            if (copy == (unsigned char*)0) { return 0; }
            safe_memcpy(copy[0..size], ((const unsigned char*)bytes)[0..size]);
            self.data = (const unsigned char*)copy;
        }
        self.owned = 1;
    } else {
        self.data = bytes;
    }
    self.size = size;
    unsigned long dir = 0UL;
    if (__tt_tag(self, 0UL, "ttcf")) {
        unsigned int nfonts = __tt_u32(self, 8UL);
        if (index < 0 || (unsigned int)index >= nfonts) { unsafe { self.free(); } return 0; }   // free() leaves it empty
        dir = (unsigned long)__tt_u32(self, 12UL + (unsigned long)index * 4UL);
        self.faceIndex = index;
    }
    unsigned int version = __tt_u32(self, dir);
    int cff = __tt_tag(self, dir, "OTTO");
    if (version != 0x00010000U && !__tt_tag(self, dir, "true") && !cff) { unsafe { self.free(); } return 0; }   // free() leaves it empty
    unsigned int numTables = __tt_u16(self, dir + 4UL);
    unsigned int i = 0U;
    while (i < numTables) {
        unsigned long rec = dir + 12UL + (unsigned long)i * 16UL;
        unsigned int off = __tt_u32(self, rec + 8UL);
        unsigned int len = __tt_u32(self, rec + 12UL);
        if ((unsigned long)off + (unsigned long)len <= size) {
            if (__tt_tag(self, rec, "head")) { self.tHead = off; }
            else if (__tt_tag(self, rec, "hhea")) { self.tHhea = off; }
            else if (__tt_tag(self, rec, "hmtx")) { self.tHmtx = off; }
            else if (__tt_tag(self, rec, "maxp")) { self.tMaxp = off; }
            else if (__tt_tag(self, rec, "cmap")) { self.tCmap = off; }
            else if (__tt_tag(self, rec, "loca")) { self.tLoca = off; self.lenLoca = len; }
            else if (__tt_tag(self, rec, "glyf")) { self.tGlyf = off; self.lenGlyf = len; }
            else if (__tt_tag(self, rec, "kern")) { self.tKern = off; }
            else if (__tt_tag(self, rec, "GPOS")) { self.tGpos = off; }
            else if (__tt_tag(self, rec, "OS/2")) { self.tOs2 = off; }
            else if (__tt_tag(self, rec, "name")) { self.tName = off; }
        }
        i = i + 1U;
    }
    if (self.tHead == 0U || self.tHhea == 0U || self.tHmtx == 0U || self.tMaxp == 0U || self.tCmap == 0U) {
        unsafe { self.free(); }   // drop in place: free() leaves it empty
        return 0;
    }
    self.hasOutlines = (self.tGlyf != 0U && self.tLoca != 0U) ? 1 : 0;
    if (!self.hasOutlines && (flags & GUI_FONT_ALLOW_NO_OUTLINES) == 0) { unsafe { self.free(); } return 0; }   // free() leaves it empty
    unsigned long head = (unsigned long)self.tHead;
    self.unitsPerEm = (int)__tt_u16(self, head + 18UL);
    if (self.unitsPerEm < 16 || self.unitsPerEm > 16384) { self.unitsPerEm = 1000; }
    self.indexToLocFormat = __tt_i16(self, head + 50UL);
    self.numGlyphs = (int)__tt_u16(self, (unsigned long)self.tMaxp + 4UL);
    unsigned long hhea = (unsigned long)self.tHhea;
    self.ascender = __tt_i16(self, hhea + 4UL);
    self.descender = __tt_i16(self, hhea + 6UL);
    self.lineGap = __tt_i16(self, hhea + 8UL);
    self.numHMetrics = (int)__tt_u16(self, hhea + 34UL);
    if (self.numHMetrics < 1) { self.numHMetrics = 1; }
    self.capHeight = self.ascender * 7 / 10;
    self.xHeight = self.ascender / 2;
    if (self.tOs2 != 0U) {
        unsigned long os2 = (unsigned long)self.tOs2;
        unsigned int ver = __tt_u16(self, os2);
        self.weight = (int)__tt_u16(self, os2 + 4UL);
        unsigned int sel = __tt_u16(self, os2 + 62UL);
        self.italic = (sel & 1U) != 0U ? 1 : 0;
        if ((sel & 0x80U) != 0U) {            // USE_TYPO_METRICS
            self.ascender = __tt_i16(self, os2 + 68UL);
            self.descender = __tt_i16(self, os2 + 70UL);
            self.lineGap = __tt_i16(self, os2 + 72UL);
        }
        if (ver >= 2U) {
            int xh = __tt_i16(self, os2 + 86UL);
            int ch = __tt_i16(self, os2 + 88UL);
            if (xh > 0) { self.xHeight = xh; }
            if (ch > 0) { self.capHeight = ch; }
        }
    }
    if (self.ascender <= 0 && self.descender >= 0) { self.ascender = self.unitsPerEm * 8 / 10; self.descender = -self.unitsPerEm / 5; }
    __tt_pick_cmap(self);
    if (self.cmapSub == 0U) { unsafe { self.free(); } return 0; }   // free() leaves it empty
    __tt_find_kern(self);
    __tt_find_gpos(self);
    __tt_read_name(self);
    return 1;
}

int GuiTypeface::load_memory_index([]const unsigned char bytes_s, int index, int flags) {
    unsigned long size = (unsigned long)bytes_s.len;
    unsigned char bytes_d_[1];
    const &unsigned char bytes = &bytes_d_[0];
    if (bytes_s.len > 0UL) { unsafe { bytes = (const &unsigned char)bytes_s.ptr; } }
    return self.load_memory_index_n_(bytes, size, index, flags);
}

int GuiTypeface::load_memory_n_(const &unsigned char bytes, unsigned long size, int flags) {
    return self.load_memory_index_n_(bytes, size, 0, flags);
}

int GuiTypeface::load_memory([]const unsigned char bytes_s, int flags) {
    unsigned long size = (unsigned long)bytes_s.len;
    unsigned char bytes_d_[1];
    const &unsigned char bytes = &bytes_d_[0];
    if (bytes_s.len > 0UL) { unsafe { bytes = (const &unsigned char)bytes_s.ptr; } }
    return self.load_memory_n_(bytes, size, flags);
}

int GuiTypeface::load_file_index(const &char path, int index) {
    __tt_blank(self);
    ?&File fq = File::open(path, "rb");
    match (fq) {
        case null: return 0;
        case some(f): {
            long long n = f.size();
            if (n < 12LL || n > 0x7FFFFFFFLL) { f.close(); return 0; }
            unsigned char* buf;
            unsafe { buf = (unsigned char*)alloc_raw_((unsigned long)n); }
            int ok;
            unsafe { ok = read_exact(f, ((unsigned char*)buf)[0..(unsigned long)n]); }
            f.close();
            if (!ok) { unsafe { dealloc(buf); } return 0; }
            int r;
            unsafe { r = self.load_memory_index_n_((const unsigned char*)buf, (unsigned long)n, index, 0); }
            if (!r) { unsafe { dealloc(buf); } return 0; }
            self.owned = 1;    // the buffer is ours now
            return 1;
        }
    }
    return 0;
}

int GuiTypeface::load_file(const &char path) {
    return self.load_file_index(path, 0);
}

void GuiTypeface::cache_clear() {
    unsigned long i = 0UL;
    while (i < self.cache.len) {
        &GuiGlyphBitmap b = self.cache.at(i);
        unsafe { if (!b.coverage.is_null()) { dealloc(b.coverage); } }
        b.coverage = null;
        i = i + 1UL;
    }
    self.cache.clear();
    self.cacheIndex.clear();
}

void GuiTypeface::free() { self.drop(); }

void GuiTypeface::drop() {
    self.cache_clear();
    self.cache.free();
    self.cacheIndex.free();
    self.gposPairs.free();
    self.family.free();
    unsafe {
        if (self.owned && !self.data.is_null()) { dealloc((?&heap unsigned char)self.data); }
    }
    self.data = null;
    self.size = 0UL;
    self.owned = 0;
    self.cmapSub = 0U;
    self.numGlyphs = 0;
}

// ── cmap ──────────────────────────────────────────────────────────────────

int GuiTypeface::glyph_index(unsigned int cp) const {
    if (self.cmapSub == 0U) { return 0; }
    unsigned long t = (unsigned long)self.cmapSub;
    int g = 0;
    if (self.cmapFormat == 4) {
        if (cp > 0xFFFFU) { return 0; }
        unsigned int segX2 = __tt_u16(self, t + 6UL);
        unsigned int segs = segX2 / 2U;
        unsigned long ends = t + 14UL;
        unsigned long starts = ends + (unsigned long)segX2 + 2UL;
        unsigned long deltas = starts + (unsigned long)segX2;
        unsigned long ranges = deltas + (unsigned long)segX2;
        // binary search for the first segment whose end >= cp
        unsigned int lo = 0U;
        unsigned int hi = segs;
        while (lo < hi) {
            unsigned int mid = (lo + hi) / 2U;
            if (__tt_u16(self, ends + (unsigned long)mid * 2UL) < cp) { lo = mid + 1U; } else { hi = mid; }
        }
        if (lo >= segs) { return 0; }
        unsigned int start = __tt_u16(self, starts + (unsigned long)lo * 2UL);
        if (cp < start) { return 0; }
        unsigned int delta = __tt_u16(self, deltas + (unsigned long)lo * 2UL);
        unsigned int roff = __tt_u16(self, ranges + (unsigned long)lo * 2UL);
        if (roff == 0U) { return (int)((cp + delta) & 0xFFFFU); }
        unsigned long ga = ranges + (unsigned long)lo * 2UL + (unsigned long)roff + (unsigned long)(cp - start) * 2UL;
        unsigned int gv = __tt_u16(self, ga);
        if (gv == 0U) { return 0; }
        return (int)((gv + delta) & 0xFFFFU);
    }
    if (self.cmapFormat == 12) {
        unsigned int n = __tt_u32(self, t + 12UL);
        unsigned int lo = 0U;
        unsigned int hi = n;
        while (lo < hi) {
            unsigned int mid = (lo + hi) / 2U;
            unsigned long gr = t + 16UL + (unsigned long)mid * 12UL;
            unsigned int s = __tt_u32(self, gr);
            unsigned int e = __tt_u32(self, gr + 4UL);
            if (cp < s) { hi = mid; }
            else if (cp > e) { lo = mid + 1U; }
            else { return (int)(__tt_u32(self, gr + 8UL) + (cp - s)); }
        }
        return 0;
    }
    if (self.cmapFormat == 6) {
        unsigned int first = __tt_u16(self, t + 6UL);
        unsigned int count = __tt_u16(self, t + 8UL);
        if (cp < first || cp >= first + count) { return 0; }
        return (int)__tt_u16(self, t + 10UL + (unsigned long)(cp - first) * 2UL);
    }
    if (self.cmapFormat == 0) {
        if (cp > 255U) { return 0; }
        return (int)__tt_u8(self, t + 6UL + (unsigned long)cp);
    }
    return g;
}

int GuiTypeface::has_glyph(unsigned int cp) const {
    return self.glyph_index(cp) != 0 ? 1 : 0;
}

// ── metrics ───────────────────────────────────────────────────────────────

static float __tt_scale(const &GuiTypeface f, float size) {
    return size / (float)f.unitsPerEm;
}

float GuiTypeface::advance(int glyph, float size) const {
    if (glyph < 0 || self.tHmtx == 0U) { return 0.0f; }
    int i = glyph < self.numHMetrics ? glyph : self.numHMetrics - 1;
    unsigned int adv = __tt_u16(self, (unsigned long)self.tHmtx + (unsigned long)i * 4UL);
    return (float)adv * __tt_scale(self, size);
}

static int __tt_coverage(const &GuiTypeface f, unsigned long cov, int glyph) {
    unsigned int fmt = __tt_u16(f, cov);
    unsigned int n = __tt_u16(f, cov + 2UL);
    unsigned int g = (unsigned int)glyph;
    if (fmt == 1U) {
        unsigned int lo = 0U; unsigned int hi = n;
        while (lo < hi) {
            unsigned int mid = (lo + hi) / 2U;
            unsigned int v = __tt_u16(f, cov + 4UL + (unsigned long)mid * 2UL);
            if (v < g) { lo = mid + 1U; } else if (v > g) { hi = mid; } else { return (int)mid; }
        }
        return -1;
    }
    if (fmt == 2U) {
        unsigned int lo = 0U; unsigned int hi = n;
        while (lo < hi) {
            unsigned int mid = (lo + hi) / 2U;
            unsigned long r = cov + 4UL + (unsigned long)mid * 6UL;
            unsigned int s = __tt_u16(f, r);
            unsigned int e = __tt_u16(f, r + 2UL);
            if (g < s) { hi = mid; } else if (g > e) { lo = mid + 1U; }
            else { return (int)(__tt_u16(f, r + 4UL) + (g - s)); }
        }
        return -1;
    }
    return -1;
}

static int __tt_classdef(const &GuiTypeface f, unsigned long cd, int glyph) {
    unsigned int fmt = __tt_u16(f, cd);
    unsigned int g = (unsigned int)glyph;
    if (fmt == 1U) {
        unsigned int start = __tt_u16(f, cd + 2UL);
        unsigned int n = __tt_u16(f, cd + 4UL);
        if (g < start || g >= start + n) { return 0; }
        return (int)__tt_u16(f, cd + 6UL + (unsigned long)(g - start) * 2UL);
    }
    if (fmt == 2U) {
        unsigned int n = __tt_u16(f, cd + 2UL);
        unsigned int lo = 0U; unsigned int hi = n;
        while (lo < hi) {
            unsigned int mid = (lo + hi) / 2U;
            unsigned long r = cd + 4UL + (unsigned long)mid * 6UL;
            unsigned int s = __tt_u16(f, r);
            unsigned int e = __tt_u16(f, r + 2UL);
            if (g < s) { hi = mid; } else if (g > e) { lo = mid + 1U; } else { return (int)__tt_u16(f, r + 4UL); }
        }
    }
    return 0;
}

static unsigned int __tt_popcount(unsigned int v) {
    unsigned int n = 0U;
    while (v != 0U) { n = n + (v & 1U); v = v >> 1; }
    return n;
}

// XAdvance of a value record (0 if the format has none).
static int __tt_xadvance(const &GuiTypeface f, unsigned long rec, unsigned int vf) {
    if ((vf & 4U) == 0U) { return 0; }
    return __tt_i16(f, rec + 2UL * (unsigned long)__tt_popcount(vf & 3U));
}

// 1 and *out = the first value record's xAdvance if a GPOS pair subtable
// covers (left, right).
static int __tt_gpos_pair(const &GuiTypeface f, unsigned long sub, int left, int right, &int out) {
    unsigned int fmt = __tt_u16(f, sub);
    int ci = __tt_coverage(f, sub + (unsigned long)__tt_u16(f, sub + 2UL), left);
    if (ci < 0) { return 0; }
    unsigned int vf1 = __tt_u16(f, sub + 4UL);
    unsigned int vf2 = __tt_u16(f, sub + 6UL);
    unsigned long s1 = 2UL * (unsigned long)__tt_popcount(vf1);
    unsigned long s2 = 2UL * (unsigned long)__tt_popcount(vf2);
    if (fmt == 1U) {
        unsigned int nsets = __tt_u16(f, sub + 8UL);
        if ((unsigned int)ci >= nsets) { return 0; }
        unsigned long set = sub + (unsigned long)__tt_u16(f, sub + 10UL + (unsigned long)ci * 2UL);
        unsigned int n = __tt_u16(f, set);
        unsigned long recSize = 2UL + s1 + s2;
        unsigned int lo = 0U; unsigned int hi = n;
        while (lo < hi) {
            unsigned int mid = (lo + hi) / 2U;
            unsigned long r = set + 2UL + (unsigned long)mid * recSize;
            unsigned int g2 = __tt_u16(f, r);
            if (g2 < (unsigned int)right) { lo = mid + 1U; }
            else if (g2 > (unsigned int)right) { hi = mid; }
            else { *out = __tt_xadvance(f, r + 2UL, vf1); return 1; }
        }
        return 0;
    }
    if (fmt == 2U) {
        unsigned long cd1 = sub + (unsigned long)__tt_u16(f, sub + 8UL);
        unsigned long cd2 = sub + (unsigned long)__tt_u16(f, sub + 10UL);
        unsigned int n1 = __tt_u16(f, sub + 12UL);
        unsigned int n2 = __tt_u16(f, sub + 14UL);
        int c1 = __tt_classdef(f, cd1, left);
        int c2 = __tt_classdef(f, cd2, right);
        if ((unsigned int)c1 >= n1 || (unsigned int)c2 >= n2) { return 0; }
        unsigned long r = sub + 16UL + ((unsigned long)c1 * (unsigned long)n2 + (unsigned long)c2) * (s1 + s2);
        *out = __tt_xadvance(f, r, vf1);
        return 1;
    }
    return 0;
}

float GuiTypeface::kerning(int left, int right, float size) const {
    if (left <= 0 || right <= 0) { return 0.0f; }
    if (self.kernSub != 0U && self.kernPairs > 0) {
        unsigned long pairs = (unsigned long)self.kernSub + 14UL;
        unsigned int key = ((unsigned int)left << 16) | (unsigned int)right;
        unsigned int lo = 0U; unsigned int hi = (unsigned int)self.kernPairs;
        while (lo < hi) {
            unsigned int mid = (lo + hi) / 2U;
            unsigned long r = pairs + (unsigned long)mid * 6UL;
            unsigned int k = __tt_u32(self, r);
            if (k < key) { lo = mid + 1U; } else if (k > key) { hi = mid; }
            else { return (float)__tt_i16(self, r + 4UL) * __tt_scale(self, size); }
        }
        return 0.0f;
    }
    unsigned long i = 0UL;
    while (i < self.gposPairs.len) {
        int v = 0;
        if (__tt_gpos_pair(self, (unsigned long)*self.gposPairs.at(i), left, right, &v)) {
            return (float)v * __tt_scale(self, size);
        }
        i = i + 1UL;
    }
    return 0.0f;
}

float GuiTypeface::ascent(float size) const { return (float)self.ascender * __tt_scale(self, size); }
float GuiTypeface::descent(float size) const { return -(float)self.descender * __tt_scale(self, size); }
float GuiTypeface::line_gap(float size) const {
    return (float)(self.lineGap > 0 ? self.lineGap : 0) * __tt_scale(self, size);
}
float GuiTypeface::line_height(float size) const {
    return self.ascent(size) + self.descent(size) + self.line_gap(size);
}

// ── outlines ──────────────────────────────────────────────────────────────

static int __tt_glyph_range(const &GuiTypeface f, int glyph, &unsigned long start, &unsigned long len) {
    if (!f.hasOutlines || glyph < 0 || glyph >= f.numGlyphs) { return 0; }
    unsigned long loca = (unsigned long)f.tLoca;
    unsigned long a = 0UL;
    unsigned long b = 0UL;
    if (f.indexToLocFormat == 0) {
        a = (unsigned long)__tt_u16(f, loca + (unsigned long)glyph * 2UL) * 2UL;
        b = (unsigned long)__tt_u16(f, loca + (unsigned long)(glyph + 1) * 2UL) * 2UL;
    } else {
        a = (unsigned long)__tt_u32(f, loca + (unsigned long)glyph * 4UL);
        b = (unsigned long)__tt_u32(f, loca + (unsigned long)(glyph + 1) * 4UL);
    }
    if (b <= a || b > (unsigned long)f.lenGlyf) { return 0; }
    *start = (unsigned long)f.tGlyf + a;
    *len = b - a;
    return 1;
}

int GuiTypeface::glyph_box(int glyph, &int x0, &int y0, &int x1, &int y1) const {
    unsigned long g = 0UL; unsigned long len = 0UL;
    if (!__tt_glyph_range(self, glyph, &g, &len) || len < 10UL) { return 0; }
    *x0 = __tt_i16(self, g + 2UL);
    *y0 = __tt_i16(self, g + 4UL);
    *x1 = __tt_i16(self, g + 6UL);
    *y1 = __tt_i16(self, g + 8UL);
    return 1;
}

static void __tt_emit(&GuiPath out, struct GuiMatrix m, int op, float x1, float y1, float x2, float y2) {
    float ax = m.apply_x(x1, y1); float ay = m.apply_y(x1, y1);
    if (op == 0) { out.move_to(ax, ay); return; }
    if (op == 1) { out.line_to(ax, ay); return; }
    float bx = m.apply_x(x2, y2); float by = m.apply_y(x2, y2);
    out.quad_to(ax, ay, bx, by);
}

// Appends glyph's contours with transform m (font units -> output).
static int __tt_outline(const &GuiTypeface f, int glyph, &GuiPath out, struct GuiMatrix m, int depth) {
    if (depth > 8) { return 0; }
    unsigned long g = 0UL; unsigned long len = 0UL;
    if (!__tt_glyph_range(f, glyph, &g, &len) || len < 10UL) { return 0; }
    int nc = __tt_i16(f, g);
    if (nc < 0) {
        // Composite glyph.
        unsigned long p = g + 10UL;
        int any = 0;
        unsigned int flags = 0x20U;
        int guard = 0;
        while ((flags & 0x20U) != 0U && guard < 64) {
            flags = __tt_u16(f, p);
            int sub = (int)__tt_u16(f, p + 2UL);
            p = p + 4UL;
            float dx = 0.0f; float dy = 0.0f;
            if ((flags & 1U) != 0U) {          // ARG_1_AND_2_ARE_WORDS
                dx = (float)__tt_i16(f, p); dy = (float)__tt_i16(f, p + 2UL);
                p = p + 4UL;
            } else {
                unsigned int a = __tt_u8(f, p);
                unsigned int b = __tt_u8(f, p + 1UL);
                dx = (float)(a >= 128U ? (int)a - 256 : (int)a);
                dy = (float)(b >= 128U ? (int)b - 256 : (int)b);
                p = p + 2UL;
            }
            if ((flags & 2U) == 0U) { dx = 0.0f; dy = 0.0f; }   // point matching: not supported
            float sa = 1.0f; float sb = 0.0f; float sc = 0.0f; float sd = 1.0f;
            if ((flags & 8U) != 0U) {           // WE_HAVE_A_SCALE
                sa = (float)__tt_i16(f, p) / 16384.0f; sd = sa;
                p = p + 2UL;
            } else if ((flags & 0x40U) != 0U) { // X_AND_Y_SCALE
                sa = (float)__tt_i16(f, p) / 16384.0f;
                sd = (float)__tt_i16(f, p + 2UL) / 16384.0f;
                p = p + 4UL;
            } else if ((flags & 0x80U) != 0U) { // TWO_BY_TWO
                sa = (float)__tt_i16(f, p) / 16384.0f;
                sb = (float)__tt_i16(f, p + 2UL) / 16384.0f;
                sc = (float)__tt_i16(f, p + 4UL) / 16384.0f;
                sd = (float)__tt_i16(f, p + 6UL) / 16384.0f;
                p = p + 8UL;
            }
            struct GuiMatrix cm = GuiMatrix::new(sa, sb, sc, sd, dx, dy);
            if (__tt_outline(f, sub, out, m.mul(cm), depth + 1)) { any = 1; }
            guard = guard + 1;
        }
        return any;
    }
    if (nc == 0) { return 0; }
    unsigned long endPts = g + 10UL;
    int npts = (int)__tt_u16(f, endPts + (unsigned long)(nc - 1) * 2UL) + 1;
    if (npts <= 0 || npts > 65535) { return 0; }
    unsigned long ins = endPts + (unsigned long)nc * 2UL;
    unsigned long p = ins + 2UL + (unsigned long)__tt_u16(f, ins);
    unsigned char* flg;
    int* xs;
    int* ys;
    unsafe {
        flg = (unsigned char*)alloc_raw_((unsigned long)npts);
        xs = (int*)alloc_raw_((unsigned long)npts * 4UL);
        ys = (int*)alloc_raw_((unsigned long)npts * 4UL);
    }
    unsafe {
        int i = 0;
        while (i < npts) {
            unsigned int fl = __tt_u8(f, p);
            p = p + 1UL;
            flg[i] = (unsigned char)fl;
            i = i + 1;
            if ((fl & 8U) != 0U) {
                unsigned int rep = __tt_u8(f, p);
                p = p + 1UL;
                while (rep > 0U && i < npts) { flg[i] = (unsigned char)fl; i = i + 1; rep = rep - 1U; }
            }
        }
        int v = 0;
        i = 0;
        while (i < npts) {
            unsigned int fl = (unsigned int)flg[i];
            if ((fl & 2U) != 0U) {
                int d = (int)__tt_u8(f, p); p = p + 1UL;
                v = v + ((fl & 16U) != 0U ? d : -d);
            } else if ((fl & 16U) == 0U) {
                v = v + __tt_i16(f, p); p = p + 2UL;
            }
            xs[i] = v;
            i = i + 1;
        }
        v = 0;
        i = 0;
        while (i < npts) {
            unsigned int fl = (unsigned int)flg[i];
            if ((fl & 4U) != 0U) {
                int d = (int)__tt_u8(f, p); p = p + 1UL;
                v = v + ((fl & 32U) != 0U ? d : -d);
            } else if ((fl & 32U) == 0U) {
                v = v + __tt_i16(f, p); p = p + 2UL;
            }
            ys[i] = v;
            i = i + 1;
        }
        // Contours: quadratic B-splines; two off-curve points in a row
        // imply an on-curve point midway.
        int start = 0;
        int c = 0;
        while (c < nc) {
            int end = (int)__tt_u16(f, endPts + (unsigned long)c * 2UL);
            c = c + 1;
            if (end < start || end >= npts) { break; }
            int n = end - start + 1;
            if (n < 2) { start = end + 1; continue; }
            // A starting on-curve point (or the midpoint of the first two).
            int first = -1;
            int k = 0;
            while (k < n) { if ((flg[start + k] & 1U) != 0U) { first = k; break; } k = k + 1; }
            float sx; float sy;
            if (first >= 0) {
                sx = (float)xs[start + first]; sy = (float)ys[start + first];
            } else {
                sx = ((float)xs[start] + (float)xs[start + 1]) * 0.5f;
                sy = ((float)ys[start] + (float)ys[start + 1]) * 0.5f;
                first = 0;
            }
            __tt_emit(out, m, 0, sx, sy, 0.0f, 0.0f);
            int haveOff = 0;
            float ox = 0.0f; float oy = 0.0f;
            k = 1;
            while (k <= n) {
                int idx = start + (first + k) % n;
                float px = (float)xs[idx];
                float py = (float)ys[idx];
                int on = (flg[idx] & 1U) != 0U ? 1 : 0;
                if (on) {
                    if (haveOff) { __tt_emit(out, m, 2, ox, oy, px, py); haveOff = 0; }
                    else { __tt_emit(out, m, 1, px, py, 0.0f, 0.0f); }
                } else {
                    if (haveOff) {
                        float mx = (ox + px) * 0.5f;
                        float my = (oy + py) * 0.5f;
                        __tt_emit(out, m, 2, ox, oy, mx, my);
                    }
                    ox = px; oy = py;
                    haveOff = 1;
                }
                k = k + 1;
            }
            if (haveOff) { __tt_emit(out, m, 2, ox, oy, sx, sy); }
            out.close();
            start = end + 1;
        }
        dealloc(flg);
        dealloc(xs);
        dealloc(ys);
    }
    return 1;
}

int GuiTypeface::glyph_path(int glyph, &GuiPath out, float size, float x, float y) const {
    float s = __tt_scale(self, size);
    // font units (y up) -> pixels (y down) at the pen
    struct GuiMatrix m = GuiMatrix::new(s, 0.0f, 0.0f, -s, x, y);
    return __tt_outline(self, glyph, out, m, 0);
}

// ── measuring ─────────────────────────────────────────────────────────────

float GuiTypeface::measure_n_n_(const &char text, unsigned long len, float size) const {
    &GuiTypeface fb = GuiTypeface::fallback();
    float w = 0.0f;
    int prev = 0;
    unsigned long i = 0UL;
    int self_is_fb;
    unsafe { self_is_fb = ((const unsigned char*)self == (const unsigned char*)fb) ? 1 : 0; }
    while (i < len) {
        unsigned int cp = gui_utf8_next_n_(text, len, &i);
        if (cp == 0U) { break; }
        int g = self.glyph_index(cp);
        if (g == 0 && !self_is_fb) {
            int g2 = fb.glyph_index(cp);
            if (g2 != 0) { w = w + fb.advance(g2, size); prev = 0; continue; }
        }
        if (prev != 0) { w = w + self.kerning(prev, g, size); }
        w = w + self.advance(g, size);
        prev = g;
    }
    return w;
}

float GuiTypeface::measure_n([]const char text_s, float size) const {
    unsigned long len = (unsigned long)text_s.len;
    char text_d_[1];
    const &char text = &text_d_[0];
    if (text_s.len > 0UL) { unsafe { text = (const &char)text_s.ptr; } }
    return self.measure_n_n_(text, len, size);
}

float GuiTypeface::measure(const &char text, float size) const {
    unsigned long n = 0UL;
    unsafe { while (text[n] != '\0') { n = n + 1UL; } }
    return self.measure_n_n_(text, n, size);
}

// ── glyph cache ───────────────────────────────────────────────────────────

static unsigned int __tt_hash(int glyph, int sizeKey, int sub) {
    unsigned int h = (unsigned int)glyph * 2654435761U;
    h = h ^ ((unsigned int)sizeKey * 40503U);
    h = h ^ ((unsigned int)sub * 0x9E3779B9U);
    return h ^ (h >> 15);
}

?&GuiGlyphBitmap GuiTypeface::cache_find(int glyph, int sizeKey, int subpixel) const {
    unsigned long cap = self.cacheIndex.len;
    if (cap == 0UL) { return null; }
    unsigned long mask = cap - 1UL;
    unsigned long h = (unsigned long)__tt_hash(glyph, sizeKey, subpixel) & mask;
    unsigned long probes = 0UL;
    while (probes < cap) {
        int idx = *self.cacheIndex.at(h);
        if (idx < 0) { return null; }
        &GuiGlyphBitmap b = self.cache.at((unsigned long)idx);
        if (b.glyph == glyph && b.sizeKey == sizeKey && b.subpixel == subpixel) { return b; }
        h = (h + 1UL) & mask;
        probes = probes + 1UL;
    }
    return null;
}

static void __tt_cache_reindex(&GuiTypeface f, unsigned long cap) {
    f.cacheIndex.clear();
    unsigned long i = 0UL;
    while (i < cap) { f.cacheIndex.push(-1); i = i + 1UL; }
    unsigned long mask = cap - 1UL;
    unsigned long k = 0UL;
    while (k < f.cache.len) {
        &GuiGlyphBitmap b = f.cache.at(k);
        unsigned long h = (unsigned long)__tt_hash(b.glyph, b.sizeKey, b.subpixel) & mask;
        while (*f.cacheIndex.at(h) >= 0) { h = (h + 1UL) & mask; }
        f.cacheIndex.set(h, (int)k);
        k = k + 1UL;
    }
}

&GuiGlyphBitmap GuiTypeface::cache_insert(struct GuiGlyphBitmap b) {
    // Bounded: a full cache starts over (glyphs are cheap to re-render).
    if (self.cache.len >= 4096UL) { self.cache_clear(); }
    self.cache.push(b);
    unsigned long cap = self.cacheIndex.len;
    if (cap < 64UL || self.cache.len * 2UL > cap) {
        unsigned long ncap = cap < 64UL ? 64UL : cap * 2UL;
        while (ncap < self.cache.len * 2UL) { ncap = ncap * 2UL; }
        __tt_cache_reindex(self, ncap);
    } else {
        unsigned long mask = cap - 1UL;
        unsigned long h = (unsigned long)__tt_hash(b.glyph, b.sizeKey, b.subpixel) & mask;
        while (*self.cacheIndex.at(h) >= 0) { h = (h + 1UL) & mask; }
        self.cacheIndex.set(h, (int)(self.cache.len - 1UL));
    }
    return self.cache.at(self.cache.len - 1UL);
}

// ── system fonts ──────────────────────────────────────────────────────────

struct GuiSystemFace {
    struct String key;
    ?&GuiTypeface face;
};

static struct Vec<struct GuiSystemFace> gGuiSystemFaces;
static int gGuiSystemFacesInit = 0;

static int __tt_try_load(&GuiTypeface f, const &char dir, const &char name) {
    struct String path = String::from(dir);
    path.push("/");
    path.push(name);
    int ok;
    unsafe { ok = f.load_file((const char*)path.data); }
    path.free();
    return ok;
}

extern char* getenv(const char* name);

static ?&GuiTypeface __tt_system_lookup(const &char family, int bold) {
    if (!gGuiSystemFacesInit) { gGuiSystemFaces = Vec::new(); gGuiSystemFacesInit = 1; }
    struct String key = String::from(family);
    key.push(bold ? "#b" : "#r");
    unsigned long i = 0UL;
    while (i < gGuiSystemFaces.len) {
        &GuiSystemFace e = gGuiSystemFaces.at(i);
        if (e.key.eq(&key)) { key.free(); return e.face; }
        i = i + 1UL;
    }
    // Candidate file names for the family.
    struct Vec<struct String> names = Vec::new();
    struct String fam = String::from(family);
    struct String lower = fam.to_lower();
    int isGeneric = 0;
    if (lower.eq_cstr("sans") || lower.eq_cstr("sans-serif") || lower.eq_cstr("ui")) {
        isGeneric = 1;
        if (bold) {
            names.push(String::from("DejaVuSans-Bold.ttf")); names.push(String::from("LiberationSans-Bold.ttf"));
            names.push(String::from("NotoSans-Bold.ttf")); names.push(String::from("FreeSansBold.ttf"));
            names.push(String::from("segoeuib.ttf")); names.push(String::from("arialbd.ttf"));
            names.push(String::from("Arial Bold.ttf"));
        } else {
            names.push(String::from("DejaVuSans.ttf")); names.push(String::from("LiberationSans-Regular.ttf"));
            names.push(String::from("NotoSans-Regular.ttf")); names.push(String::from("FreeSans.ttf"));
            names.push(String::from("segoeui.ttf")); names.push(String::from("arial.ttf"));
            names.push(String::from("Arial.ttf")); names.push(String::from("Geneva.ttf"));
        }
    } else if (lower.eq_cstr("serif")) {
        isGeneric = 1;
        names.push(String::from(bold ? "DejaVuSerif-Bold.ttf" : "DejaVuSerif.ttf"));
        names.push(String::from(bold ? "LiberationSerif-Bold.ttf" : "LiberationSerif-Regular.ttf"));
        names.push(String::from(bold ? "NotoSerif-Bold.ttf" : "NotoSerif-Regular.ttf"));
        names.push(String::from(bold ? "FreeSerifBold.ttf" : "FreeSerif.ttf"));
        names.push(String::from(bold ? "timesbd.ttf" : "times.ttf"));
        names.push(String::from(bold ? "Times New Roman Bold.ttf" : "Times New Roman.ttf"));
    } else if (lower.eq_cstr("mono") || lower.eq_cstr("monospace")) {
        isGeneric = 1;
        names.push(String::from(bold ? "DejaVuSansMono-Bold.ttf" : "DejaVuSansMono.ttf"));
        names.push(String::from(bold ? "LiberationMono-Bold.ttf" : "LiberationMono-Regular.ttf"));
        names.push(String::from(bold ? "NotoSansMono-Bold.ttf" : "NotoSansMono-Regular.ttf"));
        names.push(String::from(bold ? "FreeMonoBold.ttf" : "FreeMono.ttf"));
        names.push(String::from(bold ? "consolab.ttf" : "consola.ttf"));
        names.push(String::from(bold ? "courbd.ttf" : "cour.ttf"));
        names.push(String::from(bold ? "Courier New Bold.ttf" : "Courier New.ttf"));
    }
    // From the family name itself: "DejaVu Sans" -> DejaVuSans.ttf,
    // DejaVuSans-Regular.ttf, "DejaVu Sans.ttf", dejavusans.ttf ...
    struct String compact = String::new();
    unsafe {
        unsigned long k = 0UL;
        while (k < fam.len) { char ch = fam.data[k]; if (ch != ' ') { compact.push_char(ch); } k = k + 1UL; }
    }
    if (!isGeneric) {
        struct String a = compact.clone(); a.push(bold ? "-Bold.ttf" : ".ttf"); names.push(a);
        struct String b = compact.clone(); b.push(bold ? "Bold.ttf" : "-Regular.ttf"); names.push(b);
        struct String c = fam.clone(); c.push(bold ? " Bold.ttf" : ".ttf"); names.push(c);
        struct String d = compact.to_lower(); d.push(bold ? "bd.ttf" : ".ttf"); names.push(d);
        struct String e = compact.clone(); e.push(".ttc"); names.push(e);
        struct String g = fam.clone(); g.push(".ttc"); names.push(g);
    }
    // Directories to look in.
    struct Vec<struct String> dirs = Vec::new();
    unsafe {
        char* env = getenv("SAFEC_GUI_FONT_DIR");
        if (env != (char*)0) { dirs.push(String::from(env)); }
    }
    struct String firstWord = String::new();
    unsafe {
        unsigned long k = 0UL;
        while (k < lower.len && lower.data[k] != ' ') { firstWord.push_char(lower.data[k]); k = k + 1UL; }
    }
    const char* fixed[17];
    fixed[0] = "/usr/share/fonts/truetype/dejavu"; fixed[1] = "/usr/share/fonts/truetype/liberation";
    fixed[2] = "/usr/share/fonts/truetype/liberation2"; fixed[3] = "/usr/share/fonts/truetype/noto";
    fixed[4] = "/usr/share/fonts/truetype/freefont"; fixed[5] = "/usr/share/fonts/TTF";
    fixed[6] = "/usr/share/fonts/truetype"; fixed[7] = "/usr/share/fonts/dejavu";
    fixed[8] = "/usr/share/fonts/noto"; fixed[9] = "/usr/local/share/fonts";
    fixed[10] = "/usr/local/share/fonts/dejavu"; fixed[11] = "/System/Library/Fonts";
    fixed[12] = "/System/Library/Fonts/Supplemental"; fixed[13] = "/Library/Fonts";
    fixed[14] = "C:/Windows/Fonts"; fixed[15] = "/usr/share/fonts/opentype/noto";
    fixed[16] = "/usr/share/fonts/liberation";
    if (firstWord.len > 0UL) {
        struct String d1 = String::from("/usr/share/fonts/truetype/"); d1.push_str(&firstWord); dirs.push(d1);
        struct String d2 = String::from("/usr/share/fonts/"); d2.push_str(&firstWord); dirs.push(d2);
    }
    int fi = 0;
    while (fi < 17) { unsafe { dirs.push(String::from(fixed[fi])); } fi = fi + 1; }

    ?&GuiTypeface found = null;
    unsafe {
        &GuiTypeface face = (struct GuiTypeface*)alloc_zeroed_raw_(sizeof(struct GuiTypeface));
        int ok = 0;
        unsigned long ni = 0UL;
        while (ni < names.len && !ok) {
            unsigned long di = 0UL;
            while (di < dirs.len && !ok) {
                if (__tt_try_load(face, (const char*)dirs.at(di).data, (const char*)names.at(ni).data)) { ok = 1; }
                di = di + 1UL;
            }
            ni = ni + 1UL;
        }
        if (ok) { found = face; } else { face.free(); dealloc((?&heap GuiTypeface)face); }
    }
    names.free();   // and each name
    dirs.free();
    fam.free();
    lower.free();
    compact.free();
    firstWord.free();
    struct GuiSystemFace entry;
    entry.key = key;
    entry.face = found;
    gGuiSystemFaces.push(entry);
    return found;
}

?&GuiTypeface GuiTypeface::system(const &char family) {
    return __tt_system_lookup(family, 0);
}

?&GuiTypeface GuiTypeface::system_bold(const &char family) {
    return __tt_system_lookup(family, 1);
}

} // namespace std