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