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 bounding_box = self.metric(font_id, |m| m.bounding_box);
136 self.scale_metric(bounding_box.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}