use crate::sugarloaf::primitives::is_private_user_area;
use crate::SpanStyle;
use rustc_hash::FxHashMap;
use swash::Attributes;
use unicode_width::UnicodeWidthChar;
#[derive(Debug, Clone, Copy)]
pub struct AdvanceInfo {
pub advance_units: f32,
pub units_per_em: u16,
}
impl AdvanceInfo {
#[inline]
pub fn scaled(&self, font_size: f32) -> f32 {
if self.units_per_em > 0 {
self.advance_units * font_size / self.units_per_em as f32
} else {
0.0
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct ResolvedGlyph {
pub font_id: usize,
pub width: f32,
pub is_pua: bool,
pub advance: Option<AdvanceInfo>,
}
pub(crate) struct FontCache {
cache: FxHashMap<(char, Attributes), ResolvedGlyph>,
}
impl FontCache {
pub fn new() -> Self {
Self {
cache: FxHashMap::default(),
}
}
#[inline]
pub fn get(&self, key: &(char, Attributes)) -> Option<&ResolvedGlyph> {
self.cache.get(key)
}
#[inline]
pub fn insert(&mut self, key: (char, Attributes), value: ResolvedGlyph) {
self.cache.insert(key, value);
}
pub fn clear(&mut self) {
self.cache.clear();
}
#[inline]
pub(crate) fn set_advance(&mut self, key: (char, Attributes), advance: AdvanceInfo) {
if let Some(entry) = self.cache.get_mut(&key) {
entry.advance = Some(advance);
}
}
}
pub(crate) fn resolve_with(
cache: &mut FontCache,
font_lib: &crate::font::FontLibrary,
ch: char,
attrs: Attributes,
) -> ResolvedGlyph {
if let Some(cached) = cache.get(&(ch, attrs)) {
return *cached;
}
let style = SpanStyle {
font_attrs: attrs,
..Default::default()
};
let mut width = ch.width().unwrap_or(1) as f32;
let (font_id, is_emoji) = font_lib.resolve_font_for_char(ch, &style, None);
if is_emoji {
width = 2.0;
}
let resolved = ResolvedGlyph {
font_id,
width,
is_pua: is_private_user_area(&ch),
advance: None,
};
cache.insert((ch, attrs), resolved);
resolved
}
#[cfg(not(target_os = "macos"))]
pub(crate) fn compute_advance(
font_ctx: &crate::font::FontLibraryData,
font_id: usize,
ch: char,
) -> Option<AdvanceInfo> {
let (data, offset, _key) = font_ctx.get_data(&font_id)?;
let font_ref = swash::FontRef::from_index(&data, offset as usize)?;
let glyph_id = font_ref.charmap().map(ch as u32);
let metrics = font_ref.glyph_metrics(&[]);
Some(AdvanceInfo {
advance_units: metrics.advance_width(glyph_id),
units_per_em: font_ref.metrics(&[]).units_per_em,
})
}
#[cfg(target_os = "macos")]
pub(crate) fn compute_advance(
font_ctx: &crate::font::FontLibraryData,
font_id: usize,
ch: char,
) -> Option<AdvanceInfo> {
let font = font_ctx.try_get(&font_id)?;
let handle = if let Some(path) = font.path() {
crate::font::macos::FontHandle::from_path(path)
} else if let Some(bytes) = font.data() {
crate::font::macos::FontHandle::from_bytes(bytes.as_ref())
} else {
None
}?;
let (advance_units, units_per_em) =
crate::font::macos::advance_units_for_char(&handle, ch)?;
Some(AdvanceInfo {
advance_units,
units_per_em,
})
}