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 { |
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 { |
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 { |
78 | 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>>>(ttf_parser::Face::parse(data, idx).ok()) |
86 | }; |
87 | Self { inner: font, face } |
88 | } |
89 | |
90 | fn query_kerning_table(&self, prev: u32, next: u32) -> f32 { |
91 | if let Some(face) = self.face.as_ref() { |
92 | if let Some(kern) = face.tables().kern { |
93 | let kern = kern |
94 | .subtables |
95 | .into_iter() |
96 | .filter(|st| st.horizontal && !st.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 | } |
103 | 0.0 |
104 | } |
105 | } |
106 | |
107 | impl std::ops::Deref for FontExt { |
108 | type Target = Font; |
109 | fn deref(&self) -> &Font { |
110 | &self.inner |
111 | } |
112 | } |
113 | |
114 | /// Lazily load font data. Font type doesn't own actual data, which |
115 | /// lives in the cache. |
116 | fn load_font_data(face: FontFamily, style: FontStyle) -> FontResult<FontExt> { |
117 | let key = match style { |
118 | FontStyle::Normal => Cow::Borrowed(face.as_str()), |
119 | _ => Cow::Owned(format!("{}, {}" , face.as_str(), style.as_str())), |
120 | }; |
121 | |
122 | // First, we try to find the font object for current thread |
123 | if let Some(font_object) = FONT_OBJECT_CACHE.with(|font_object_cache| { |
124 | font_object_cache |
125 | .borrow() |
126 | .get(Borrow::<str>::borrow(&key)) |
127 | .map(Clone::clone) |
128 | }) { |
129 | return Ok(font_object); |
130 | } |
131 | |
132 | // Then we need to check if the data cache contains the font data |
133 | let cache = DATA_CACHE.read().unwrap(); |
134 | if let Some(data) = cache.get(Borrow::<str>::borrow(&key)) { |
135 | return data.clone().map(|handle| { |
136 | handle |
137 | .load() |
138 | .map(FontExt::new) |
139 | .map_err(|e| FontError::FontLoadError(Arc::new(e))) |
140 | })?; |
141 | } |
142 | drop(cache); |
143 | |
144 | // Otherwise we should load from system |
145 | let mut properties = Properties::new(); |
146 | match style { |
147 | FontStyle::Normal => properties.style(Style::Normal), |
148 | FontStyle::Italic => properties.style(Style::Italic), |
149 | FontStyle::Oblique => properties.style(Style::Oblique), |
150 | FontStyle::Bold => properties.weight(Weight::BOLD), |
151 | }; |
152 | |
153 | let family = match face { |
154 | FontFamily::Serif => FamilyName::Serif, |
155 | FontFamily::SansSerif => FamilyName::SansSerif, |
156 | FontFamily::Monospace => FamilyName::Monospace, |
157 | FontFamily::Name(name) => FamilyName::Title(name.to_owned()), |
158 | }; |
159 | |
160 | let make_not_found_error = |
161 | || FontError::NoSuchFont(face.as_str().to_owned(), style.as_str().to_owned()); |
162 | |
163 | if let Ok(handle) = FONT_SOURCE |
164 | .with(|source| source.select_best_match(&[family, FamilyName::SansSerif], &properties)) |
165 | { |
166 | let font = handle |
167 | .load() |
168 | .map(FontExt::new) |
169 | .map_err(|e| FontError::FontLoadError(Arc::new(e))); |
170 | let (should_cache, data) = match font.as_ref().map(|f| f.handle()) { |
171 | Ok(None) => (false, Err(FontError::LockError)), |
172 | Ok(Some(handle)) => (true, Ok(handle)), |
173 | Err(e) => (true, Err(e.clone())), |
174 | }; |
175 | |
176 | if should_cache { |
177 | DATA_CACHE |
178 | .write() |
179 | .map_err(|_| FontError::LockError)? |
180 | .insert(key.clone().into_owned(), data); |
181 | } |
182 | |
183 | if let Ok(font) = font.as_ref() { |
184 | FONT_OBJECT_CACHE.with(|font_object_cache| { |
185 | font_object_cache |
186 | .borrow_mut() |
187 | .insert(key.into_owned(), font.clone()); |
188 | }); |
189 | } |
190 | |
191 | return font; |
192 | } |
193 | Err(make_not_found_error()) |
194 | } |
195 | |
196 | #[derive(Clone)] |
197 | pub struct FontDataInternal(FontExt); |
198 | |
199 | impl FontData for FontDataInternal { |
200 | type ErrorType = FontError; |
201 | |
202 | fn new(family: FontFamily, style: FontStyle) -> Result<Self, FontError> { |
203 | Ok(FontDataInternal(load_font_data(family, style)?)) |
204 | } |
205 | |
206 | fn estimate_layout(&self, size: f64, text: &str) -> Result<LayoutBox, Self::ErrorType> { |
207 | let font = &self.0; |
208 | let pixel_per_em = size / 1.24; |
209 | let metrics = font.metrics(); |
210 | |
211 | let font = &self.0; |
212 | |
213 | let mut x_in_unit = 0f32; |
214 | |
215 | let mut prev = None; |
216 | let place_holder = font.glyph_for_char(PLACEHOLDER_CHAR); |
217 | |
218 | for c in text.chars() { |
219 | if let Some(glyph_id) = font.glyph_for_char(c).or(place_holder) { |
220 | if let Ok(size) = font.advance(glyph_id) { |
221 | x_in_unit += size.x(); |
222 | } |
223 | if let Some(pc) = prev { |
224 | x_in_unit += font.query_kerning_table(pc, glyph_id); |
225 | } |
226 | prev = Some(glyph_id); |
227 | } |
228 | } |
229 | |
230 | let x_pixels = x_in_unit * pixel_per_em as f32 / metrics.units_per_em as f32; |
231 | |
232 | Ok(((0, 0), (x_pixels as i32, pixel_per_em as i32))) |
233 | } |
234 | |
235 | fn draw<E, DrawFunc: FnMut(i32, i32, f32) -> Result<(), E>>( |
236 | &self, |
237 | (base_x, mut base_y): (i32, i32), |
238 | size: f64, |
239 | text: &str, |
240 | mut draw: DrawFunc, |
241 | ) -> Result<Result<(), E>, Self::ErrorType> { |
242 | let em = (size / 1.24) as f32; |
243 | |
244 | let mut x = base_x as f32; |
245 | let font = &self.0; |
246 | let metrics = font.metrics(); |
247 | |
248 | let canvas_size = size as usize; |
249 | |
250 | base_y -= (0.24 * em) as i32; |
251 | |
252 | let mut prev = None; |
253 | let place_holder = font.glyph_for_char(PLACEHOLDER_CHAR); |
254 | |
255 | let mut result = Ok(()); |
256 | |
257 | for c in text.chars() { |
258 | if let Some(glyph_id) = font.glyph_for_char(c).or(place_holder) { |
259 | if let Some(pc) = prev { |
260 | x += font.query_kerning_table(pc, glyph_id) * em / metrics.units_per_em as f32; |
261 | } |
262 | |
263 | let mut canvas = Canvas::new(Vector2I::splat(canvas_size as i32), Format::A8); |
264 | |
265 | result = font |
266 | .rasterize_glyph( |
267 | &mut canvas, |
268 | glyph_id, |
269 | em as f32, |
270 | Transform2F::from_translation(Vector2F::new(0.0, em as f32)), |
271 | HintingOptions::None, |
272 | RasterizationOptions::GrayscaleAa, |
273 | ) |
274 | .map_err(|e| FontError::GlyphError(Arc::new(e))) |
275 | .and(result); |
276 | |
277 | let base_x = x as i32; |
278 | |
279 | for dy in 0..canvas_size { |
280 | for dx in 0..canvas_size { |
281 | let alpha = canvas.pixels[dy * canvas_size + dx] as f32 / 255.0; |
282 | if let Err(e) = draw(base_x + dx as i32, base_y + dy as i32, alpha) { |
283 | return Ok(Err(e)); |
284 | } |
285 | } |
286 | } |
287 | |
288 | x += font.advance(glyph_id).map(|size| size.x()).unwrap_or(0.0) * em |
289 | / metrics.units_per_em as f32; |
290 | |
291 | prev = Some(glyph_id); |
292 | } |
293 | } |
294 | result?; |
295 | Ok(Ok(())) |
296 | } |
297 | } |
298 | |
299 | #[cfg (test)] |
300 | mod test { |
301 | |
302 | use super::*; |
303 | |
304 | #[test] |
305 | fn test_font_cache() -> FontResult<()> { |
306 | // We cannot only check the size of font cache, because |
307 | // the test case may be run in parallel. Thus the font cache |
308 | // may contains other fonts. |
309 | let _a = load_font_data(FontFamily::Serif, FontStyle::Normal)?; |
310 | assert!(DATA_CACHE.read().unwrap().contains_key("serif" )); |
311 | |
312 | let _b = load_font_data(FontFamily::Serif, FontStyle::Normal)?; |
313 | assert!(DATA_CACHE.read().unwrap().contains_key("serif" )); |
314 | |
315 | // TODO: Check they are the same |
316 | |
317 | return Ok(()); |
318 | } |
319 | } |
320 | |