sugarloaf 0.4.2

Sugarloaf is Rio rendering engine, designed to be multiplatform. It is based on WebGPU, Rust library for Desktops and WebAssembly for Web (JavaScript). This project is created and maintained for Rio terminal purposes but feel free to use it.
//! Windows per-codepoint font discovery via font-kit walk.
//!
//! On Linux we get this via fontconfig's `FcFontSort` + `FcCharSet`
//! (a single C call returns sorted candidates). On Windows there's no
//! equivalent in our dep tree — `IDWriteFontFallback::MapCharacters`
//! would be the moral twin but lives in the `windows` / `dwrote` crates,
//! neither of which Debian ships, so we'd violate the workspace's
//! Debian-only constraint.
//!
//! The fallback approach here: ask font-kit for every installed font,
//! then mmap each and probe its CMAP for the codepoint via ttf-parser.
//! Slower per cold lookup than DirectWrite (we touch every font file
//! once until we hit a match), but cached at the font-cache layer so
//! the cost amortizes to zero after the first hit per codepoint per
//! session. doesn't ship Windows discovery yet either, so this
//! still puts us ahead.
//!
//! Future replacement path: when either `windows` or `dwrote` lands in
//! Debian, swap the body of `discover_fallback` for a single
//! `IDWriteFontFallback::MapCharacters` call and delete the walk.

use std::path::PathBuf;
use std::sync::OnceLock;

use font_kit::handle::Handle;
use font_kit::source::SystemSource;
use memmap2::Mmap;

/// Cached list of every installed font path + collection index.
/// Built once on first miss; subsequent misses iterate in-memory only.
/// `Vec<(PathBuf, u32)>` rather than `Vec<Handle>` so we don't hold
/// onto font-kit's Memory variant (would defeat the lazy mmap).
static SYSTEM_FONTS: OnceLock<Vec<(PathBuf, u32)>> = OnceLock::new();

fn system_font_index() -> &'static [(PathBuf, u32)] {
    SYSTEM_FONTS
        .get_or_init(|| {
            let source = SystemSource::new();
            source
                .all_fonts()
                .unwrap_or_default()
                .into_iter()
                .filter_map(|h| match h {
                    Handle::Path { path, font_index } => Some((path, font_index)),
                    Handle::Memory { .. } => None,
                })
                .collect()
        })
        .as_slice()
}

/// Find a font file installed on the system that contains `ch`.
///
/// The signature mirrors `font/linux.rs::discover_fallback` so the
/// cross-platform call site in `FontLibrary::resolve_font_for_char`
/// stays platform-uniform. Style hints are accepted but ignored on
/// this path — picking the right weight/italic among multiple matches
/// would require reading each candidate's `OS/2` table, and the
/// payoff is small for fallback glyphs (CJK, emoji, symbols are
/// usually single-weight families anyway).
pub fn discover_fallback(
    _primary_family: &str,
    ch: char,
    _want_mono: bool,
    _want_bold: bool,
    _want_italic: bool,
) -> Option<(PathBuf, u32)> {
    for (path, face_index) in system_font_index() {
        if face_contains_char(path, *face_index, ch) {
            return Some((path.clone(), *face_index));
        }
    }
    None
}

/// `true` if the font at `(path, face_index)` declares a glyph for
/// `ch`. mmap + parse + CMAP lookup; fast in practice (< 1 ms per
/// font) because ttf-parser only walks the table directory and the
/// CMAP, not the full font.
fn face_contains_char(path: &PathBuf, face_index: u32, ch: char) -> bool {
    let Ok(file) = std::fs::File::open(path) else {
        return false;
    };
    let Ok(mmap) = (unsafe { Mmap::map(&file) }) else {
        return false;
    };
    let Ok(face) = ttf_parser::Face::parse(&mmap, face_index) else {
        return false;
    };
    face.glyph_index(ch).is_some()
}