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