use serde::Deserialize;
use concinnity_core::components::Font;
pub(crate) fn compile_font_payload(args: &serde_json::Value) -> Result<Vec<u8>, String> {
let font: Font =
Deserialize::deserialize(args).map_err(|e| format!("Font: invalid args: {}", e))?;
let path = font.path.as_str();
let ttf_bytes: Vec<u8> = if path.is_empty() {
concinnity_core::build::font::BUILTIN_FONT_BYTES.to_vec()
} else {
std::fs::read(path).map_err(|e| format!("Font: could not read '{}': {}", path, e))?
};
let source = if path.is_empty() { "<built-in>" } else { path };
concinnity_core::build::font::compile(&ttf_bytes, font.size_px, source)
}
#[cfg(test)]
mod tests {
use super::*;
use concinnity_core::build::font::deserialise;
#[test]
fn builtin_font_compiles_from_an_empty_path() {
for args in [
serde_json::json!({ "size_px": 48 }),
serde_json::json!({ "path": "", "size_px": 48 }),
] {
let payload = compile_font_payload(&args).expect("compile bundled font at 48px");
let (w, h, _supersample, _size_px, rgba, metrics) = deserialise(&payload).unwrap();
assert!(w > 0 && h > 0, "atlas has non-zero dimensions");
assert!(!rgba.is_empty());
assert!(!metrics.is_empty());
}
}
#[test]
fn font_compiles_from_a_ttf_on_disk() {
let dir = tempfile::tempdir().expect("tempdir");
let path = dir.path().join("bundled.ttf");
std::fs::write(&path, concinnity_core::build::font::BUILTIN_FONT_BYTES).expect("write ttf");
let args = serde_json::json!({ "path": path.to_str().unwrap(), "size_px": 12 });
let payload = compile_font_payload(&args).expect("compile font read from disk");
let (w, h, _supersample, size_px, rgba, metrics) = deserialise(&payload).unwrap();
assert_eq!(size_px, 12);
assert_eq!(metrics.len(), 95);
assert_eq!(metrics[0].char_code, ' ' as u32);
assert_eq!(metrics[94].char_code, '~' as u32);
assert_eq!(rgba.len(), (w * h * 4) as usize);
let a = metrics
.iter()
.find(|m| m.char_code == 'A' as u32)
.expect("'A' is rasterised");
assert!(a.atlas_w > 0 && a.atlas_h > 0);
assert!(a.advance_px > 0.0);
}
#[test]
fn font_reports_a_missing_ttf() {
let args = serde_json::json!({ "path": "/no/such/font.ttf", "size_px": 12 });
let err = compile_font_payload(&args).unwrap_err();
assert!(err.contains("could not read"), "got: {err}");
assert!(err.contains("/no/such/font.ttf"), "got: {err}");
}
#[test]
fn font_rejects_a_file_that_is_not_a_ttf() {
let dir = tempfile::tempdir().expect("tempdir");
let path = dir.path().join("bogus.ttf");
std::fs::write(&path, b"not a font at all").expect("write junk");
let args = serde_json::json!({ "path": path.to_str().unwrap(), "size_px": 12 });
let err = compile_font_payload(&args).unwrap_err();
assert!(err.contains("failed to parse"), "got: {err}");
}
#[test]
fn font_rejects_args_that_are_not_a_font() {
let err = compile_font_payload(&serde_json::json!({ "size_px": "big" })).unwrap_err();
assert!(err.contains("Font: invalid args"), "got: {err}");
}
}