use crate::{
visualization::Image,
words::{Font, Word},
};
use image::Rgb;
use rand::seq::SliceRandom;
use rusttype::Scale;
use serde::{Deserialize, Serialize};
use serde_json::from_str;
use std::{collections::HashMap, error::Error, fs::read_to_string};
pub struct FontProvider<'font> {
fonts: HashMap<String, Font<'font>>,
}
impl<'font> Default for FontProvider<'font> {
fn default() -> Self {
Self::new()
}
}
impl<'font> FontProvider<'font> {
pub fn new() -> FontProvider<'font> {
FontProvider {
fonts: HashMap::new(),
}
}
pub fn from_file(filename: &str) -> FontProvider<'font> {
#[derive(Serialize, Deserialize)]
struct FontFile {
name: String,
file: String,
}
let mut fp = FontProvider::new();
let Ok(data) = read_to_string(filename) else {
panic!("Unable to read file '{}'", filename);
};
let Ok(fonts): Result<Vec<FontFile>, _> = from_str(&data) else {
panic!("Unable to parse file '{}'", filename);
};
for font in fonts {
let file = font.file.clone();
fp.register(&font.name, &file).unwrap();
}
fp
}
pub fn register(&mut self, name: &str, font_file: &str) -> Result<(), Box<dyn Error>> {
let font = Font::new(font_file.to_string())?;
self.fonts.insert(name.to_string(), font);
Ok(())
}
pub fn get_font(&self, name: &str) -> Result<&Font, String> {
match self.fonts.get(name) {
Some(font) => Ok(font),
None => Err(format!("Font not registered: {}", name)),
}
}
pub fn random_font_name(&self) -> String {
let keys: Vec<_> = self.fonts.keys().collect();
keys.choose(&mut rand::thread_rng()).unwrap().to_string()
}
pub fn debug_font(&self, font_name: &str, filename: &str) {
if let Some(font) = self.fonts.get(font_name) {
let word = Word::new(
font_name,
crate::geometry::Orientation::Horizontal,
Rgb([0, 0, 0]),
"",
64,
);
let bb = font.get_bounding_box(&word, Scale::uniform(64.));
let mut img = Image::new(bb.size().0 + 40, bb.size().1, Some(Rgb([255, 255, 255])));
img.draw_text(
(15, 0),
font_name,
font,
Scale::uniform(64.),
Rgb([0, 0, 0]),
&crate::geometry::Orientation::Horizontal,
);
img.save(filename).unwrap();
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn register_and_read() {
let mut fp = FontProvider::new();
fp.register("dejavu", "assets/fonts/DroidSans.ttf").unwrap();
let _ = fp.get_font("dejavu").unwrap();
}
#[test]
#[should_panic(expected = "Font file not found: unexisting.ttf")]
fn register_file_not_found() {
let mut fp = FontProvider::new();
fp.register("dejavu", "unexisting.ttf").unwrap();
}
#[test]
#[should_panic(expected = "Font not registered: dejavu")]
fn get_unexisting() {
let fp = FontProvider::new();
let _ = fp.get_font("dejavu").unwrap();
}
}