pub trait TextMetrics {
fn width(&self, text: &str, font_size: f64) -> f64;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct BuiltinMetrics;
impl BuiltinMetrics {
pub const VERSION: &'static str = "builtin-latin-v1";
}
impl TextMetrics for BuiltinMetrics {
fn width(&self, text: &str, font_size: f64) -> f64 {
let units = text.chars().map(character_width).sum::<f64>();
units * font_size
}
}
const MONO_UP_TO: f64 = 11.5;
pub(crate) const MONO_ADVANCE: f64 = 0.6;
pub(crate) struct Face<'a, M> {
sans: &'a M,
mono: bool,
}
impl<'a, M: TextMetrics> Face<'a, M> {
pub(crate) const fn new(sans: &'a M, mono: bool) -> Self {
Self { sans, mono }
}
}
impl<M: TextMetrics> TextMetrics for Face<'_, M> {
fn width(&self, text: &str, font_size: f64) -> f64 {
if self.mono && font_size <= MONO_UP_TO {
#[allow(clippy::cast_precision_loss)]
let characters = text.chars().count() as f64;
characters * MONO_ADVANCE * font_size
} else {
self.sans.width(text, font_size)
}
}
}
fn character_width(character: char) -> f64 {
match character {
'i' | 'j' | 'l' | 'I' | '1' | '|' | '!' | '.' | ',' | ':' | ';' => 0.30,
'm' | 'w' | 'M' | 'W' | '@' | '%' => 0.90,
' ' => 0.32,
character if character.is_ascii_uppercase() => 0.67,
character if character.is_ascii_digit() => 0.56,
_ => 0.56,
}
}