fonts.rs

  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}