font_cache.rs

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