| 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 { |
| 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: &String, style: &String) => { |
| 41 | write!(fmt, "No such font: {} {}" , family, style) |
| 42 | } |
| 43 | FontError::FontLoadError(e: &Arc) => write!(fmt, "Font loading error {}" , e), |
| 44 | FontError::GlyphError(e: &Arc) => 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 { |
| 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 { |
| 77 | 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 | |
| 91 | 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; |
| 110 | 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. |
| 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 | |
| 203 | fn new(family: FontFamily, style: FontStyle) -> Result<Self, FontError> { |
| 204 | Ok(FontDataInternal(load_font_data(family, style)?)) |
| 205 | } |
| 206 | |
| 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 | |
| 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 ] |
| 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 | |