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}