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 family_name(&self, family_id: FamilyId) -> Result<String> {
40 self.0
41 .read()
42 .families
43 .get(family_id.0)
44 .ok_or_else(|| anyhow!("invalid family id"))
45 .map(|family| family.name.clone())
46 }
47
48 pub fn load_family(&self, names: &[&str]) -> Result<FamilyId> {
49 for name in names {
50 let state = self.0.upgradable_read();
51
52 if let Some(ix) = state.families.iter().position(|f| f.name == *name) {
53 return Ok(FamilyId(ix));
54 }
55
56 let mut state = RwLockUpgradableReadGuard::upgrade(state);
57
58 if let Ok(font_ids) = state.fonts.load_family(name) {
59 if font_ids.is_empty() {
60 continue;
61 }
62
63 let family_id = FamilyId(state.families.len());
64 for font_id in &font_ids {
65 if state.fonts.glyph_for_char(*font_id, 'm').is_none() {
66 return Err(anyhow!("font must contain a glyph for the 'm' character"));
67 }
68 }
69
70 state.families.push(Family {
71 name: String::from(*name),
72 font_ids,
73 });
74 return Ok(family_id);
75 }
76 }
77
78 Err(anyhow!(
79 "could not find a non-empty font family matching one of the given names"
80 ))
81 }
82
83 pub fn default_font(&self, family_id: FamilyId) -> FontId {
84 self.select_font(family_id, &Properties::default()).unwrap()
85 }
86
87 pub fn select_font(&self, family_id: FamilyId, properties: &Properties) -> Result<FontId> {
88 let inner = self.0.upgradable_read();
89 if let Some(font_id) = inner
90 .font_selections
91 .get(&family_id)
92 .and_then(|f| f.get(properties))
93 {
94 Ok(*font_id)
95 } else {
96 let mut inner = RwLockUpgradableReadGuard::upgrade(inner);
97 let family = &inner.families[family_id.0];
98 let font_id = inner
99 .fonts
100 .select_font(&family.font_ids, properties)
101 .unwrap_or(family.font_ids[0]);
102
103 inner
104 .font_selections
105 .entry(family_id)
106 .or_default()
107 .insert(properties.clone(), font_id);
108 Ok(font_id)
109 }
110 }
111
112 pub fn metric<F, T>(&self, font_id: FontId, f: F) -> T
113 where
114 F: FnOnce(&Metrics) -> T,
115 T: 'static,
116 {
117 let state = self.0.upgradable_read();
118 if let Some(metrics) = state.metrics.get(&font_id) {
119 f(metrics)
120 } else {
121 let metrics = state.fonts.font_metrics(font_id);
122 let metric = f(&metrics);
123 let mut state = RwLockUpgradableReadGuard::upgrade(state);
124 state.metrics.insert(font_id, metrics);
125 metric
126 }
127 }
128
129 pub fn bounding_box(&self, font_id: FontId, font_size: f32) -> Vector2F {
130 let bounding_box = self.metric(font_id, |m| m.bounding_box);
131 let width = self.scale_metric(bounding_box.width(), font_id, font_size);
132 let height = self.scale_metric(bounding_box.height(), font_id, font_size);
133 vec2f(width, height)
134 }
135
136 pub fn em_width(&self, font_id: FontId, font_size: f32) -> f32 {
137 let state = self.0.read();
138 let glyph_id = state.fonts.glyph_for_char(font_id, 'm').unwrap();
139 let bounds = state.fonts.typographic_bounds(font_id, glyph_id).unwrap();
140 self.scale_metric(bounds.width(), font_id, font_size)
141 }
142
143 pub fn line_height(&self, font_id: FontId, font_size: f32) -> f32 {
144 let height = self.metric(font_id, |m| m.bounding_box.height());
145 self.scale_metric(height, font_id, font_size)
146 }
147
148 pub fn cap_height(&self, font_id: FontId, font_size: f32) -> f32 {
149 self.scale_metric(self.metric(font_id, |m| m.cap_height), font_id, font_size)
150 }
151
152 pub fn ascent(&self, font_id: FontId, font_size: f32) -> f32 {
153 self.scale_metric(self.metric(font_id, |m| m.ascent), font_id, font_size)
154 }
155
156 pub fn descent(&self, font_id: FontId, font_size: f32) -> f32 {
157 self.scale_metric(self.metric(font_id, |m| -m.descent), font_id, font_size)
158 }
159
160 pub fn scale_metric(&self, metric: f32, font_id: FontId, font_size: f32) -> f32 {
161 metric * font_size / self.metric(font_id, |m| m.units_per_em as f32)
162 }
163}
164
165#[cfg(test)]
166mod tests {
167 use super::*;
168 use crate::{
169 fonts::{Style, Weight},
170 platform::{test, Platform as _},
171 };
172
173 #[test]
174 fn test_select_font() {
175 let platform = test::platform();
176 let fonts = FontCache::new(platform.fonts());
177 let arial = fonts.load_family(&["Arial"]).unwrap();
178 let arial_regular = fonts.select_font(arial, &Properties::new()).unwrap();
179 let arial_italic = fonts
180 .select_font(arial, &Properties::new().style(Style::Italic))
181 .unwrap();
182 let arial_bold = fonts
183 .select_font(arial, &Properties::new().weight(Weight::BOLD))
184 .unwrap();
185 assert_ne!(arial_regular, arial_italic);
186 assert_ne!(arial_regular, arial_bold);
187 assert_ne!(arial_italic, arial_bold);
188 }
189}