1use crate::geometry::vector::{vec2f, Vector2F};
2use anyhow::{anyhow, Result};
3use parking_lot::{RwLock, RwLockUpgradableReadGuard};
4
5pub use font_kit::properties::{Properties, Weight};
6use font_kit::{
7 font::Font, loaders::core_text::NativeFont, metrics::Metrics, source::SystemSource,
8};
9use ordered_float::OrderedFloat;
10use std::{collections::HashMap, sync::Arc};
11
12pub type GlyphId = u32;
13
14#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
15pub struct FamilyId(usize);
16
17#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
18pub struct FontId(usize);
19
20pub struct FontCache(RwLock<FontCacheState>);
21
22pub struct FontCacheState {
23 source: SystemSource,
24 families: Vec<Family>,
25 fonts: Vec<Arc<Font>>,
26 font_names: Vec<Arc<String>>,
27 font_selections: HashMap<FamilyId, HashMap<Properties, FontId>>,
28 metrics: HashMap<FontId, Metrics>,
29 native_fonts: HashMap<(FontId, OrderedFloat<f32>), NativeFont>,
30 fonts_by_name: HashMap<Arc<String>, FontId>,
31 emoji_font_id: Option<FontId>,
32}
33
34unsafe impl Send for FontCache {}
35
36struct Family {
37 name: String,
38 font_ids: Vec<FontId>,
39}
40
41impl FontCache {
42 pub fn new() -> Self {
43 Self(RwLock::new(FontCacheState {
44 source: SystemSource::new(),
45 families: Vec::new(),
46 fonts: Vec::new(),
47 font_names: Vec::new(),
48 font_selections: HashMap::new(),
49 metrics: HashMap::new(),
50 native_fonts: HashMap::new(),
51 fonts_by_name: HashMap::new(),
52 emoji_font_id: None,
53 }))
54 }
55
56 pub fn load_family(&self, names: &[&str]) -> Result<FamilyId> {
57 for name in names {
58 let state = self.0.upgradable_read();
59
60 if let Some(ix) = state.families.iter().position(|f| f.name == *name) {
61 return Ok(FamilyId(ix));
62 }
63
64 let mut state = RwLockUpgradableReadGuard::upgrade(state);
65
66 if let Ok(handle) = state.source.select_family_by_name(name) {
67 if handle.is_empty() {
68 continue;
69 }
70
71 let family_id = FamilyId(state.families.len());
72 let mut font_ids = Vec::new();
73 for font in handle.fonts() {
74 let font = font.load()?;
75 if font.glyph_for_char('m').is_none() {
76 return Err(anyhow!("font must contain a glyph for the 'm' character"));
77 }
78 font_ids.push(push_font(&mut state, font));
79 }
80
81 state.families.push(Family {
82 name: String::from(*name),
83 font_ids,
84 });
85 return Ok(family_id);
86 }
87 }
88
89 Err(anyhow!(
90 "could not find a non-empty font family matching one of the given names"
91 ))
92 }
93
94 pub fn default_font(&self, family_id: FamilyId) -> FontId {
95 self.select_font(family_id, &Properties::default()).unwrap()
96 }
97
98 pub fn select_font(&self, family_id: FamilyId, properties: &Properties) -> Result<FontId> {
99 let inner = self.0.upgradable_read();
100 if let Some(font_id) = inner
101 .font_selections
102 .get(&family_id)
103 .and_then(|f| f.get(properties))
104 {
105 Ok(*font_id)
106 } else {
107 let mut inner = RwLockUpgradableReadGuard::upgrade(inner);
108 let family = &inner.families[family_id.0];
109 let candidates = family
110 .font_ids
111 .iter()
112 .map(|font_id| inner.fonts[font_id.0].properties())
113 .collect::<Vec<_>>();
114 let idx = font_kit::matching::find_best_match(&candidates, properties)?;
115 let font_id = family.font_ids[idx];
116
117 inner
118 .font_selections
119 .entry(family_id)
120 .or_default()
121 .insert(properties.clone(), font_id);
122 Ok(font_id)
123 }
124 }
125
126 pub fn font(&self, font_id: FontId) -> Arc<Font> {
127 self.0.read().fonts[font_id.0].clone()
128 }
129
130 pub fn font_name(&self, font_id: FontId) -> Arc<String> {
131 self.0.read().font_names[font_id.0].clone()
132 }
133
134 pub fn metric<F, T>(&self, font_id: FontId, f: F) -> T
135 where
136 F: FnOnce(&Metrics) -> T,
137 T: 'static,
138 {
139 let state = self.0.upgradable_read();
140 if let Some(metrics) = state.metrics.get(&font_id) {
141 f(metrics)
142 } else {
143 let metrics = state.fonts[font_id.0].metrics();
144 let metric = f(&metrics);
145 let mut state = RwLockUpgradableReadGuard::upgrade(state);
146 state.metrics.insert(font_id, metrics);
147 metric
148 }
149 }
150
151 pub fn is_emoji(&self, font_id: FontId) -> bool {
152 self.0
153 .read()
154 .emoji_font_id
155 .map_or(false, |emoji_font_id| emoji_font_id == font_id)
156 }
157
158 pub fn bounding_box(&self, font_id: FontId, font_size: f32) -> Vector2F {
159 let bounding_box = self.metric(font_id, |m| m.bounding_box);
160 let width = self.scale_metric(bounding_box.width(), font_id, font_size);
161 let height = self.scale_metric(bounding_box.height(), font_id, font_size);
162 vec2f(width, height)
163 }
164
165 pub fn line_height(&self, font_id: FontId, font_size: f32) -> f32 {
166 let bounding_box = self.metric(font_id, |m| m.bounding_box);
167 self.scale_metric(bounding_box.height(), font_id, font_size)
168 }
169
170 pub fn cap_height(&self, font_id: FontId, font_size: f32) -> f32 {
171 self.scale_metric(self.metric(font_id, |m| m.cap_height), font_id, font_size)
172 }
173
174 pub fn ascent(&self, font_id: FontId, font_size: f32) -> f32 {
175 self.scale_metric(self.metric(font_id, |m| m.ascent), font_id, font_size)
176 }
177
178 pub fn descent(&self, font_id: FontId, font_size: f32) -> f32 {
179 self.scale_metric(self.metric(font_id, |m| m.descent), font_id, font_size)
180 }
181
182 // pub fn render_emoji(&self, glyph_id: GlyphId, font_size: f32) -> Result<Pattern> {
183 // let key = (glyph_id, OrderedFloat(font_size));
184
185 // {
186 // if let Some(image) = self.0.read().emoji_images.get(&key) {
187 // return Ok(image.clone());
188 // }
189 // }
190
191 // let font_id = self.emoji_font_id()?;
192 // let bounding_box = self.bounding_box(font_id, font_size);
193 // let width = (4.0 * bounding_box.x()) as usize;
194 // let height = (4.0 * bounding_box.y()) as usize;
195 // let mut ctx = CGContext::create_bitmap_context(
196 // None,
197 // width,
198 // height,
199 // 8,
200 // width * 4,
201 // &CGColorSpace::create_device_rgb(),
202 // kCGImageAlphaPremultipliedLast | kCGBitmapByteOrderDefault,
203 // );
204 // ctx.scale(4.0, 4.0);
205
206 // let native_font = self.native_font(font_id, font_size);
207 // let glyph = glyph_id.0 as CGGlyph;
208 // let glyph_bounds = native_font.get_bounding_rects_for_glyphs(Default::default(), &[glyph]);
209 // let position = CGPoint::new(glyph_bounds.origin.x, -glyph_bounds.origin.y);
210
211 // native_font.draw_glyphs(&[glyph], &[position], ctx.clone());
212
213 // ctx.flush();
214
215 // let image = Pattern::from_image(Image::new(
216 // vec2i(ctx.width() as i32, ctx.height() as i32),
217 // Arc::new(u8_slice_to_color_slice(&ctx.data()).into()),
218 // ));
219 // self.0.write().emoji_images.insert(key, image.clone());
220
221 // Ok(image)
222 // }
223
224 fn emoji_font_id(&self) -> Result<FontId> {
225 let state = self.0.upgradable_read();
226
227 if let Some(font_id) = state.emoji_font_id {
228 Ok(font_id)
229 } else {
230 let handle = state.source.select_family_by_name("Apple Color Emoji")?;
231 let font = handle
232 .fonts()
233 .first()
234 .ok_or(anyhow!("no fonts in Apple Color Emoji font family"))?
235 .load()?;
236 let mut state = RwLockUpgradableReadGuard::upgrade(state);
237 let font_id = push_font(&mut state, font);
238 state.emoji_font_id = Some(font_id);
239 Ok(font_id)
240 }
241 }
242
243 pub fn scale_metric(&self, metric: f32, font_id: FontId, font_size: f32) -> f32 {
244 metric * font_size / self.metric(font_id, |m| m.units_per_em as f32)
245 }
246
247 pub fn native_font(&self, font_id: FontId, size: f32) -> NativeFont {
248 let native_key = (font_id, OrderedFloat(size));
249
250 let state = self.0.upgradable_read();
251 if let Some(native_font) = state.native_fonts.get(&native_key).cloned() {
252 native_font
253 } else {
254 let native_font = state.fonts[font_id.0]
255 .native_font()
256 .clone_with_font_size(size as f64);
257 RwLockUpgradableReadGuard::upgrade(state)
258 .native_fonts
259 .insert(native_key, native_font.clone());
260 native_font
261 }
262 }
263
264 pub fn font_id_for_native_font(&self, native_font: NativeFont) -> FontId {
265 let postscript_name = native_font.postscript_name();
266 let state = self.0.upgradable_read();
267 if let Some(font_id) = state.fonts_by_name.get(&postscript_name) {
268 *font_id
269 } else {
270 push_font(&mut RwLockUpgradableReadGuard::upgrade(state), unsafe {
271 Font::from_native_font(native_font.clone())
272 })
273 }
274 }
275}
276
277fn push_font(state: &mut FontCacheState, font: Font) -> FontId {
278 let font_id = FontId(state.fonts.len());
279 let name = Arc::new(font.postscript_name().unwrap());
280 if *name == "AppleColorEmoji" {
281 state.emoji_font_id = Some(font_id);
282 }
283 state.fonts.push(Arc::new(font));
284 state.font_names.push(name.clone());
285 state.fonts_by_name.insert(name, font_id);
286 font_id
287}