fonts.rs

  1use crate::{
  2    geometry::vector::{vec2f, Vector2F},
  3    platform,
  4};
  5use anyhow::{anyhow, Result};
  6use font_kit::metrics::Metrics;
  7pub use font_kit::properties::{Properties, Weight};
  8use parking_lot::{RwLock, RwLockUpgradableReadGuard};
  9use std::{collections::HashMap, sync::Arc};
 10
 11pub type GlyphId = u32;
 12
 13#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
 14pub struct FamilyId(usize);
 15
 16#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
 17pub struct FontId(pub usize);
 18
 19pub struct FontCache(RwLock<FontCacheState>);
 20
 21pub struct FontCacheState {
 22    fonts: Arc<dyn platform::FontSystem>,
 23    families: Vec<Family>,
 24    font_selections: HashMap<FamilyId, HashMap<Properties, FontId>>,
 25    metrics: HashMap<FontId, Metrics>,
 26}
 27
 28unsafe impl Send for FontCache {}
 29
 30struct Family {
 31    name: String,
 32    font_ids: Vec<FontId>,
 33}
 34
 35impl FontCache {
 36    pub fn new(fonts: Arc<dyn platform::FontSystem>) -> Self {
 37        Self(RwLock::new(FontCacheState {
 38            fonts,
 39            families: Vec::new(),
 40            font_selections: HashMap::new(),
 41            metrics: HashMap::new(),
 42        }))
 43    }
 44
 45    pub fn load_family(&self, names: &[&str]) -> Result<FamilyId> {
 46        for name in names {
 47            let state = self.0.upgradable_read();
 48
 49            if let Some(ix) = state.families.iter().position(|f| f.name == *name) {
 50                return Ok(FamilyId(ix));
 51            }
 52
 53            let mut state = RwLockUpgradableReadGuard::upgrade(state);
 54
 55            if let Ok(font_ids) = state.fonts.load_family(name) {
 56                if font_ids.is_empty() {
 57                    continue;
 58                }
 59
 60                let family_id = FamilyId(state.families.len());
 61                for font_id in &font_ids {
 62                    if state.fonts.glyph_for_char(*font_id, 'm').is_none() {
 63                        return Err(anyhow!("font must contain a glyph for the 'm' character"));
 64                    }
 65                }
 66
 67                state.families.push(Family {
 68                    name: String::from(*name),
 69                    font_ids,
 70                });
 71                return Ok(family_id);
 72            }
 73        }
 74
 75        Err(anyhow!(
 76            "could not find a non-empty font family matching one of the given names"
 77        ))
 78    }
 79
 80    pub fn default_font(&self, family_id: FamilyId) -> FontId {
 81        self.select_font(family_id, &Properties::default()).unwrap()
 82    }
 83
 84    pub fn select_font(&self, family_id: FamilyId, properties: &Properties) -> Result<FontId> {
 85        let inner = self.0.upgradable_read();
 86        if let Some(font_id) = inner
 87            .font_selections
 88            .get(&family_id)
 89            .and_then(|f| f.get(properties))
 90        {
 91            Ok(*font_id)
 92        } else {
 93            let mut inner = RwLockUpgradableReadGuard::upgrade(inner);
 94            let family = &inner.families[family_id.0];
 95            let font_id = inner
 96                .fonts
 97                .select_font(&family.font_ids, properties)
 98                .unwrap_or(family.font_ids[0]);
 99
100            inner
101                .font_selections
102                .entry(family_id)
103                .or_default()
104                .insert(properties.clone(), font_id);
105            Ok(font_id)
106        }
107    }
108
109    pub fn metric<F, T>(&self, font_id: FontId, f: F) -> T
110    where
111        F: FnOnce(&Metrics) -> T,
112        T: 'static,
113    {
114        let state = self.0.upgradable_read();
115        if let Some(metrics) = state.metrics.get(&font_id) {
116            f(metrics)
117        } else {
118            let metrics = state.fonts.font_metrics(font_id);
119            let metric = f(&metrics);
120            let mut state = RwLockUpgradableReadGuard::upgrade(state);
121            state.metrics.insert(font_id, metrics);
122            metric
123        }
124    }
125
126    pub fn bounding_box(&self, font_id: FontId, font_size: f32) -> Vector2F {
127        let bounding_box = self.metric(font_id, |m| m.bounding_box);
128        let width = self.scale_metric(bounding_box.width(), font_id, font_size);
129        let height = self.scale_metric(bounding_box.height(), font_id, font_size);
130        vec2f(width, height)
131    }
132
133    pub fn em_width(&self, font_id: FontId, font_size: f32) -> f32 {
134        let state = self.0.read();
135        let glyph_id = state.fonts.glyph_for_char(font_id, 'm').unwrap();
136        let bounds = state.fonts.typographic_bounds(font_id, glyph_id).unwrap();
137        self.scale_metric(bounds.width(), font_id, font_size)
138    }
139
140    pub fn line_height(&self, font_id: FontId, font_size: f32) -> f32 {
141        let bounding_box = self.metric(font_id, |m| m.bounding_box);
142        self.scale_metric(bounding_box.height(), font_id, font_size)
143    }
144
145    pub fn cap_height(&self, font_id: FontId, font_size: f32) -> f32 {
146        self.scale_metric(self.metric(font_id, |m| m.cap_height), font_id, font_size)
147    }
148
149    pub fn ascent(&self, font_id: FontId, font_size: f32) -> f32 {
150        self.scale_metric(self.metric(font_id, |m| m.ascent), font_id, font_size)
151    }
152
153    pub fn descent(&self, font_id: FontId, font_size: f32) -> f32 {
154        self.scale_metric(self.metric(font_id, |m| m.descent), font_id, font_size)
155    }
156
157    pub fn scale_metric(&self, metric: f32, font_id: FontId, font_size: f32) -> f32 {
158        metric * font_size / self.metric(font_id, |m| m.units_per_em as f32)
159    }
160}