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()
}