rocketsplash-rt 0.2.2

Runtime library for loading and rendering Rocketsplash assets (.rst, .rsf)
Documentation
// <FILE>crates/rocketsplash-rt/src/font/fnc_load_font.rs</FILE>
// <DESC>Font deserialization and validation</DESC>
// <VERS>VERSION: 1.1.0</VERS>
// <WCTX>Public release refactor audit</WCTX>
// <CLOG>Extract glyph conversion helper to keep fnc_ within size limit</CLOG>

use std::collections::BTreeMap;

use log::debug;
use rocketsplash_formats::{
    AllocationTracker, FontAtlas, GlyphData, GlyphVariants, FONT_ATLAS_MIN_SUPPORTED,
    FONT_ATLAS_VERSION, MAX_GLYPHS,
};

use super::fnc_convert_glyph::convert_glyph;
use crate::font::{Font, RuntimeGlyph, RuntimeGlyphVariants};
use crate::Error;

pub fn load_font_from_bytes(bytes: &[u8]) -> Result<Font, Error> {
    let atlas: FontAtlas = rmp_serde::from_slice(bytes).map_err(|err| Error::InvalidFormat {
        message: err.to_string(),
    })?;
    if atlas.version < FONT_ATLAS_MIN_SUPPORTED || atlas.version > FONT_ATLAS_VERSION {
        return Err(Error::UnsupportedVersion {
            format: "font atlas".to_string(),
            found: atlas.version,
            min_supported: FONT_ATLAS_MIN_SUPPORTED,
            current: FONT_ATLAS_VERSION,
        });
    }
    if atlas.glyphs.len() > MAX_GLYPHS {
        return Err(Error::InvalidFormat {
            message: format!(
                "Font atlas has {} glyphs, exceeds {}",
                atlas.glyphs.len(),
                MAX_GLYPHS
            ),
        });
    }
    if atlas.line_height == 0 {
        return Err(Error::InvalidFormat {
            message: "Font line height must be non-zero".to_string(),
        });
    }

    let mut tracker = AllocationTracker::new();
    let mut glyphs = BTreeMap::new();
    for (ch, variants) in atlas.glyphs {
        variants
            .validate()
            .map_err(|message| Error::InvalidFormat { message })?;
        let runtime_variants = convert_variants(&mut tracker, variants)?;
        glyphs.insert(ch, runtime_variants);
    }

    debug!("Loaded font atlas with {} glyphs", glyphs.len());

    Ok(Font {
        meta: atlas.meta,
        line_height: atlas.line_height,
        mode: atlas.mode,
        available_styles: atlas.available_styles,
        glyphs,
    })
}

fn convert_variants(
    tracker: &mut AllocationTracker,
    variants: GlyphVariants,
) -> Result<RuntimeGlyphVariants, Error> {
    Ok(RuntimeGlyphVariants {
        base: convert_glyph(tracker, variants.base)?,
        bold: convert_optional(tracker, variants.bold)?,
        italic: convert_optional(tracker, variants.italic)?,
        bold_italic: convert_optional(tracker, variants.bold_italic)?,
        reverse: convert_optional(tracker, variants.reverse)?,
    })
}

fn convert_optional(
    tracker: &mut AllocationTracker,
    glyph: Option<GlyphData>,
) -> Result<Option<RuntimeGlyph>, Error> {
    glyph.map(|value| convert_glyph(tracker, value)).transpose()
}

// <FILE>crates/rocketsplash-rt/src/font/fnc_load_font.rs</FILE>
// <VERS>END OF VERSION: 1.1.0</VERS>