• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use std::borrow::{Borrow, Cow};
2 use std::cell::RefCell;
3 use std::collections::HashMap;
4 use std::sync::{Arc, RwLock};
5 
6 use lazy_static::lazy_static;
7 
8 use font_kit::{
9     canvas::{Canvas, Format, RasterizationOptions},
10     error::{FontLoadingError, GlyphLoadingError},
11     family_name::FamilyName,
12     font::Font,
13     handle::Handle,
14     hinting::HintingOptions,
15     properties::{Properties, Style, Weight},
16     source::SystemSource,
17 };
18 
19 use ttf_parser::{Face, GlyphId};
20 
21 use pathfinder_geometry::transform2d::Transform2F;
22 use pathfinder_geometry::vector::{Vector2F, Vector2I};
23 
24 use super::{FontData, FontFamily, FontStyle, LayoutBox};
25 
26 type FontResult<T> = Result<T, FontError>;
27 
28 #[derive(Debug, Clone)]
29 pub enum FontError {
30     LockError,
31     NoSuchFont(String, String),
32     FontLoadError(Arc<FontLoadingError>),
33     GlyphError(Arc<GlyphLoadingError>),
34 }
35 
36 impl std::fmt::Display for FontError {
fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>37     fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
38         match self {
39             FontError::LockError => write!(fmt, "Could not lock mutex"),
40             FontError::NoSuchFont(family, style) => {
41                 write!(fmt, "No such font: {} {}", family, style)
42             }
43             FontError::FontLoadError(e) => write!(fmt, "Font loading error {}", e),
44             FontError::GlyphError(e) => write!(fmt, "Glyph error {}", e),
45         }
46     }
47 }
48 
49 impl std::error::Error for FontError {}
50 
51 lazy_static! {
52     static ref DATA_CACHE: RwLock<HashMap<String, FontResult<Handle>>> =
53         RwLock::new(HashMap::new());
54 }
55 
56 thread_local! {
57     static FONT_SOURCE: SystemSource = SystemSource::new();
58     static FONT_OBJECT_CACHE: RefCell<HashMap<String, FontExt>> = RefCell::new(HashMap::new());
59 }
60 
61 const PLACEHOLDER_CHAR: char = '�';
62 
63 #[derive(Clone)]
64 struct FontExt {
65     inner: Font,
66     face: Option<Face<'static>>,
67 }
68 
69 impl Drop for FontExt {
drop(&mut self)70     fn drop(&mut self) {
71         // We should make sure the face object dead first
72         self.face.take();
73     }
74 }
75 
76 impl FontExt {
new(font: Font) -> Self77     fn new(font: Font) -> Self {
78         let handle = font.handle();
79         let (data, idx) = match handle.as_ref() {
80             Some(Handle::Memory { bytes, font_index }) => (&bytes[..], *font_index),
81             _ => unreachable!(),
82         };
83         let face = unsafe {
84             std::mem::transmute::<Option<_>, Option<Face<'static>>>(
85                 ttf_parser::Face::parse(data, idx).ok(),
86             )
87         };
88         Self { inner: font, face }
89     }
90 
query_kerning_table(&self, prev: u32, next: u32) -> f3291     fn query_kerning_table(&self, prev: u32, next: u32) -> f32 {
92         if let Some(face) = self.face.as_ref() {
93             if let Some(kern) = face.tables().kern {
94                 let kern = kern
95                     .subtables
96                     .into_iter()
97                     .filter(|st| st.horizontal && !st.variable)
98                     .filter_map(|st| st.glyphs_kerning(GlyphId(prev as u16), GlyphId(next as u16)))
99                     .next()
100                     .unwrap_or(0);
101                 return kern as f32;
102             }
103         }
104         0.0
105     }
106 }
107 
108 impl std::ops::Deref for FontExt {
109     type Target = Font;
deref(&self) -> &Font110     fn deref(&self) -> &Font {
111         &self.inner
112     }
113 }
114 
115 /// Lazily load font data. Font type doesn't own actual data, which
116 /// lives in the cache.
load_font_data(face: FontFamily, style: FontStyle) -> FontResult<FontExt>117 fn load_font_data(face: FontFamily, style: FontStyle) -> FontResult<FontExt> {
118     let key = match style {
119         FontStyle::Normal => Cow::Borrowed(face.as_str()),
120         _ => Cow::Owned(format!("{}, {}", face.as_str(), style.as_str())),
121     };
122 
123     // First, we try to find the font object for current thread
124     if let Some(font_object) = FONT_OBJECT_CACHE.with(|font_object_cache| {
125         font_object_cache
126             .borrow()
127             .get(Borrow::<str>::borrow(&key))
128             .cloned()
129     }) {
130         return Ok(font_object);
131     }
132 
133     // Then we need to check if the data cache contains the font data
134     let cache = DATA_CACHE.read().unwrap();
135     if let Some(data) = cache.get(Borrow::<str>::borrow(&key)) {
136         data.clone().map(|handle| {
137             handle
138                 .load()
139                 .map(FontExt::new)
140                 .map_err(|e| FontError::FontLoadError(Arc::new(e)))
141         })??;
142     }
143     drop(cache);
144 
145     // Otherwise we should load from system
146     let mut properties = Properties::new();
147     match style {
148         FontStyle::Normal => properties.style(Style::Normal),
149         FontStyle::Italic => properties.style(Style::Italic),
150         FontStyle::Oblique => properties.style(Style::Oblique),
151         FontStyle::Bold => properties.weight(Weight::BOLD),
152     };
153 
154     let family = match face {
155         FontFamily::Serif => FamilyName::Serif,
156         FontFamily::SansSerif => FamilyName::SansSerif,
157         FontFamily::Monospace => FamilyName::Monospace,
158         FontFamily::Name(name) => FamilyName::Title(name.to_owned()),
159     };
160 
161     let make_not_found_error =
162         || FontError::NoSuchFont(face.as_str().to_owned(), style.as_str().to_owned());
163 
164     if let Ok(handle) = FONT_SOURCE
165         .with(|source| source.select_best_match(&[family, FamilyName::SansSerif], &properties))
166     {
167         let font = handle
168             .load()
169             .map(FontExt::new)
170             .map_err(|e| FontError::FontLoadError(Arc::new(e)));
171         let (should_cache, data) = match font.as_ref().map(|f| f.handle()) {
172             Ok(None) => (false, Err(FontError::LockError)),
173             Ok(Some(handle)) => (true, Ok(handle)),
174             Err(e) => (true, Err(e.clone())),
175         };
176 
177         if should_cache {
178             DATA_CACHE
179                 .write()
180                 .map_err(|_| FontError::LockError)?
181                 .insert(key.clone().into_owned(), data);
182         }
183 
184         if let Ok(font) = font.as_ref() {
185             FONT_OBJECT_CACHE.with(|font_object_cache| {
186                 font_object_cache
187                     .borrow_mut()
188                     .insert(key.into_owned(), font.clone());
189             });
190         }
191 
192         return font;
193     }
194     Err(make_not_found_error())
195 }
196 
197 #[derive(Clone)]
198 pub struct FontDataInternal(FontExt);
199 
200 impl FontData for FontDataInternal {
201     type ErrorType = FontError;
202 
new(family: FontFamily, style: FontStyle) -> Result<Self, FontError>203     fn new(family: FontFamily, style: FontStyle) -> Result<Self, FontError> {
204         Ok(FontDataInternal(load_font_data(family, style)?))
205     }
206 
estimate_layout(&self, size: f64, text: &str) -> Result<LayoutBox, Self::ErrorType>207     fn estimate_layout(&self, size: f64, text: &str) -> Result<LayoutBox, Self::ErrorType> {
208         let font = &self.0;
209         let pixel_per_em = size / 1.24;
210         let metrics = font.metrics();
211 
212         let font = &self.0;
213 
214         let mut x_in_unit = 0f32;
215 
216         let mut prev = None;
217         let place_holder = font.glyph_for_char(PLACEHOLDER_CHAR);
218 
219         for c in text.chars() {
220             if let Some(glyph_id) = font.glyph_for_char(c).or(place_holder) {
221                 if let Ok(size) = font.advance(glyph_id) {
222                     x_in_unit += size.x();
223                 }
224                 if let Some(pc) = prev {
225                     x_in_unit += font.query_kerning_table(pc, glyph_id);
226                 }
227                 prev = Some(glyph_id);
228             }
229         }
230 
231         let x_pixels = x_in_unit * pixel_per_em as f32 / metrics.units_per_em as f32;
232 
233         Ok(((0, 0), (x_pixels as i32, pixel_per_em as i32)))
234     }
235 
draw<E, DrawFunc: FnMut(i32, i32, f32) -> Result<(), E>>( &self, (base_x, mut base_y): (i32, i32), size: f64, text: &str, mut draw: DrawFunc, ) -> Result<Result<(), E>, Self::ErrorType>236     fn draw<E, DrawFunc: FnMut(i32, i32, f32) -> Result<(), E>>(
237         &self,
238         (base_x, mut base_y): (i32, i32),
239         size: f64,
240         text: &str,
241         mut draw: DrawFunc,
242     ) -> Result<Result<(), E>, Self::ErrorType> {
243         let em = (size / 1.24) as f32;
244 
245         let mut x = base_x as f32;
246         let font = &self.0;
247         let metrics = font.metrics();
248 
249         let canvas_size = size as usize;
250 
251         base_y -= (0.24 * em) as i32;
252 
253         let mut prev = None;
254         let place_holder = font.glyph_for_char(PLACEHOLDER_CHAR);
255 
256         let mut result = Ok(());
257 
258         for c in text.chars() {
259             if let Some(glyph_id) = font.glyph_for_char(c).or(place_holder) {
260                 if let Some(pc) = prev {
261                     x += font.query_kerning_table(pc, glyph_id) * em / metrics.units_per_em as f32;
262                 }
263 
264                 let mut canvas = Canvas::new(Vector2I::splat(canvas_size as i32), Format::A8);
265 
266                 result = font
267                     .rasterize_glyph(
268                         &mut canvas,
269                         glyph_id,
270                         em,
271                         Transform2F::from_translation(Vector2F::new(0.0, em)),
272                         HintingOptions::None,
273                         RasterizationOptions::GrayscaleAa,
274                     )
275                     .map_err(|e| FontError::GlyphError(Arc::new(e)))
276                     .and(result);
277 
278                 let base_x = x as i32;
279 
280                 for dy in 0..canvas_size {
281                     for dx in 0..canvas_size {
282                         let alpha = canvas.pixels[dy * canvas_size + dx] as f32 / 255.0;
283                         if let Err(e) = draw(base_x + dx as i32, base_y + dy as i32, alpha) {
284                             return Ok(Err(e));
285                         }
286                     }
287                 }
288 
289                 x += font.advance(glyph_id).map(|size| size.x()).unwrap_or(0.0) * em
290                     / metrics.units_per_em as f32;
291 
292                 prev = Some(glyph_id);
293             }
294         }
295         result?;
296         Ok(Ok(()))
297     }
298 }
299 
300 #[cfg(test)]
301 mod test {
302 
303     use super::*;
304 
305     #[test]
test_font_cache() -> FontResult<()>306     fn test_font_cache() -> FontResult<()> {
307         // We cannot only check the size of font cache, because
308         // the test case may be run in parallel. Thus the font cache
309         // may contains other fonts.
310         let _a = load_font_data(FontFamily::Serif, FontStyle::Normal)?;
311         assert!(DATA_CACHE.read().unwrap().contains_key("serif"));
312 
313         let _b = load_font_data(FontFamily::Serif, FontStyle::Normal)?;
314         assert!(DATA_CACHE.read().unwrap().contains_key("serif"));
315 
316         // TODO: Check they are the same
317 
318         Ok(())
319     }
320 }
321