use std::cell::RefCell;
use skia_safe::FontMgr;
use crate::render::dimension::Pt;
use crate::render::fonts;
use super::fragment::FontProps;
pub struct TextMeasurer {
font_mgr: FontMgr,
font_cache: RefCell<fonts::FontCache>,
}
impl TextMeasurer {
pub fn new(font_mgr: FontMgr) -> Self {
Self {
font_mgr,
font_cache: RefCell::new(fonts::FontCache::new()),
}
}
pub fn measure(
&self,
text: &str,
font_props: &FontProps,
) -> (Pt, super::fragment::TextMetrics) {
let mut cache = self.font_cache.borrow_mut();
let font = cache.get(
&self.font_mgr,
&font_props.family,
font_props.size,
font_props.bold,
font_props.italic,
);
let (width, _bounds) = font.measure_str(text, None);
let (_, metrics) = font.metrics();
let text_metrics = super::fragment::TextMetrics {
ascent: Pt::new(-metrics.ascent),
descent: Pt::new(metrics.descent),
};
let char_count = text.chars().count();
let spacing_extra = if char_count > 0 {
font_props.char_spacing * (char_count as f32)
} else {
Pt::ZERO
};
(Pt::new(width) + spacing_extra, text_metrics)
}
pub fn underline_metrics(&self, font_props: &FontProps) -> (Pt, Pt) {
let mut cache = self.font_cache.borrow_mut();
let font = cache.get(
&self.font_mgr,
&font_props.family,
font_props.size,
font_props.bold,
font_props.italic,
);
let (_, metrics) = font.metrics();
let raw_pos = metrics.underline_position();
let raw_thick = metrics.underline_thickness();
if raw_pos.is_none() || raw_thick.is_none() {
log::warn!(
"font '{}' ({:?}) missing underline metrics, using descent as fallback",
font_props.family,
font_props.size
);
}
let position = Pt::new(-raw_pos.unwrap_or(metrics.descent));
let thickness = Pt::new(raw_thick.unwrap_or(1.0));
(position, thickness)
}
pub fn default_line_height(&self, family: &str, size: Pt) -> Pt {
let mut cache = self.font_cache.borrow_mut();
let font = cache.get(&self.font_mgr, family, size, false, false);
let (_, metrics) = font.metrics();
Pt::new(-metrics.ascent + metrics.descent)
}
}