From 9b3bd7c0a6b2c916b31b74089dc1551f808f50c8 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 12 Nov 2021 12:00:17 +0800 Subject: [PATCH 1/6] Support custom glyph indices in GlyphKey --- src/darwin/mod.rs | 51 +++++++++++++++++++++------------- src/directwrite/mod.rs | 43 ++++++++++++++++++++--------- src/ft/mod.rs | 54 ++++++++++++++++++++++++++---------- src/lib.rs | 62 +++++++++++++++++++++++++++++++++++++----- 4 files changed, 156 insertions(+), 54 deletions(-) diff --git a/src/darwin/mod.rs b/src/darwin/mod.rs index c12426d..bfe515e 100644 --- a/src/darwin/mod.rs +++ b/src/darwin/mod.rs @@ -35,8 +35,8 @@ pub mod byte_order; use byte_order::kCGBitmapByteOrder32Host; use super::{ - BitmapBuffer, Error, FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, - Weight, + BitmapBuffer, Error, FontDesc, FontKey, GlyphId, GlyphKey, Metrics, RasterizedGlyph, Size, + Slant, Style, Weight, }; /// According to the documentation, the index of 0 must be a missing glyph character: @@ -89,11 +89,14 @@ impl Descriptor { // Investigate if we can actually use the .-prefixed // fallbacks somehow. if let Ok(apple_symbols) = new_from_name("Apple Symbols", size) { - fallbacks.push(Font { + let mut font = Font { cg_font: apple_symbols.copy_to_CGFont(), ct_font: apple_symbols, fallbacks: Vec::new(), - }) + placeholder_glyph_index: 0, + }; + font.placeholder_glyph_index = font.glyph_index(' '); + fallbacks.push(font); }; fallbacks @@ -101,7 +104,9 @@ impl Descriptor { Vec::new() }; - Font { ct_font, cg_font, fallbacks } + let mut font = Font { ct_font, cg_font, fallbacks, placeholder_glyph_index: 0 }; + font.placeholder_glyph_index = font.glyph_index(' '); + font } } @@ -153,13 +158,27 @@ impl crate::Rasterize for Rasterizer { // Find a font where the given character is present. let (font, glyph_index) = iter::once(font) .chain(font.fallbacks.iter()) - .find_map(|font| match font.glyph_index(glyph.character) { - MISSING_GLYPH_INDEX => None, - glyph_index => Some((font, glyph_index)), + .find_map(|font| { + if let Some(c) = glyph.id.as_char() { + match font.glyph_index(c) { + MISSING_GLYPH_INDEX => None, + glyph_index => Some((font, glyph_index)), + } + } else { + let index = glyph.id.value(); + if index == 0 { + match font.placeholder_glyph_index { + MISSING_GLYPH_INDEX => None, + glyph_index => Some((font, glyph_index)), + } + } else { + Some((font, index)) + } + } }) .unwrap_or((font, MISSING_GLYPH_INDEX)); - let glyph = font.get_glyph(glyph.character, glyph_index, self.use_thin_strokes); + let glyph = font.get_glyph(glyph.id, glyph_index, self.use_thin_strokes); if glyph_index == MISSING_GLYPH_INDEX { Err(Error::MissingGlyph(glyph)) @@ -315,6 +334,7 @@ pub struct Font { ct_font: CTFont, cg_font: CGFont, fallbacks: Vec, + placeholder_glyph_index: u32, } unsafe impl Send for Font {} @@ -375,7 +395,7 @@ impl Font { pub fn get_glyph( &self, - character: char, + id: GlyphId, glyph_index: u32, use_thin_strokes: bool, ) -> RasterizedGlyph { @@ -391,14 +411,7 @@ impl Font { let rasterized_height = (rasterized_descent + rasterized_ascent) as u32; if rasterized_width == 0 || rasterized_height == 0 { - return RasterizedGlyph { - character: ' ', - width: 0, - height: 0, - top: 0, - left: 0, - buffer: BitmapBuffer::Rgb(Vec::new()), - }; + return RasterizedGlyph::default(); } let mut cg_context = CGContext::create_bitmap_context( @@ -457,7 +470,7 @@ impl Font { }; RasterizedGlyph { - character, + id, left: rasterized_left, top: (bounds.size.height + bounds.origin.y).ceil() as i32, width: rasterized_width as i32, diff --git a/src/directwrite/mod.rs b/src/directwrite/mod.rs index a42261a..7827a55 100644 --- a/src/directwrite/mod.rs +++ b/src/directwrite/mod.rs @@ -15,8 +15,8 @@ use winapi::um::dwrite; use winapi::um::winnls::GetUserDefaultLocaleName; use super::{ - BitmapBuffer, Error, FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, - Weight, + BitmapBuffer, Error, FontDesc, FontKey, GlyphId, GlyphKey, Metrics, RasterizedGlyph, Size, + Slant, Style, Weight, }; /// DirectWrite uses 0 for missing glyph symbols. @@ -30,6 +30,7 @@ struct Font { weight: FontWeight, style: FontStyle, stretch: FontStretch, + placeholder_glyph_index: u16, } pub struct DirectWriteRasterizer { @@ -45,7 +46,7 @@ impl DirectWriteRasterizer { &self, face: &FontFace, size: Size, - character: char, + id: GlyphId, glyph_index: u16, ) -> Result { let em_size = em_size(size); @@ -85,7 +86,7 @@ impl DirectWriteRasterizer { ); Ok(RasterizedGlyph { - character, + id, width: (bounds.right - bounds.left) as i32, height: (bounds.bottom - bounds.top) as i32, top: -bounds.top, @@ -232,17 +233,28 @@ impl crate::Rasterize for DirectWriteRasterizer { let loaded_fallback_font; let mut font = loaded_font; - let mut glyph_index = self.get_glyph_index(&loaded_font.face, glyph.character); - if glyph_index == MISSING_GLYPH_INDEX { - if let Some(fallback_font) = self.get_fallback_font(&loaded_font, glyph.character) { - loaded_fallback_font = Font::from(fallback_font); - glyph_index = self.get_glyph_index(&loaded_fallback_font.face, glyph.character); - font = &loaded_fallback_font; + + let glyph_index = if let Some(character) = glyph.id.as_char() { + let mut glyph_index = self.get_glyph_index(&loaded_font.face, character); + if glyph_index == MISSING_GLYPH_INDEX { + if let Some(fallback_font) = self.get_fallback_font(&loaded_font, character) { + loaded_fallback_font = Font::from(fallback_font); + glyph_index = self.get_glyph_index(&loaded_fallback_font.face, character); + font = &loaded_fallback_font; + } } - } + glyph_index + } else { + let index = glyph.id.value(); + if index == 0 { + loaded_font.placeholder_glyph_index + } else { + index as u16 + } + }; let rasterized_glyph = - self.rasterize_glyph(&font.face, glyph.size, glyph.character, glyph_index)?; + self.rasterize_glyph(&font.face, glyph.size, glyph.id, glyph_index)?; if glyph_index == MISSING_GLYPH_INDEX { Err(Error::MissingGlyph(rasterized_glyph)) @@ -262,12 +274,17 @@ fn em_size(size: Size) -> f32 { impl From for Font { fn from(font: dwrote::Font) -> Font { + let face = font.create_font_face(); + let placeholder_glyph_index = + face.get_glyph_indices(&[' ' as u32]).first().copied().unwrap_or(MISSING_GLYPH_INDEX); + Font { - face: font.create_font_face(), + face, family_name: font.family_name(), weight: font.weight(), style: font.style(), stretch: font.stretch(), + placeholder_glyph_index, } } } diff --git a/src/ft/mod.rs b/src/ft/mod.rs index 95c0f8a..ed185b7 100644 --- a/src/ft/mod.rs +++ b/src/ft/mod.rs @@ -53,7 +53,7 @@ struct FallbackList { coverage: CharSet, } -struct FaceLoadingProperties { +pub struct FaceLoadingProperties { load_flags: LoadFlag, render_mode: freetype::RenderMode, lcd_filter: c_uint, @@ -64,6 +64,7 @@ struct FaceLoadingProperties { pixelsize_fixup_factor: Option, ft_face: Rc, rgba: Rgba, + placeholder_glyph_index: u32, } impl fmt::Debug for FaceLoadingProperties { @@ -86,7 +87,7 @@ impl fmt::Debug for FaceLoadingProperties { /// Rasterizes glyphs for a single font face. pub struct FreeTypeRasterizer { - loader: FreeTypeLoader, + pub loader: FreeTypeLoader, fallback_lists: HashMap, device_pixel_ratio: f32, @@ -297,11 +298,13 @@ impl FreeTypeRasterizer { } fn face_for_glyph(&mut self, glyph_key: GlyphKey) -> FontKey { - if let Some(face) = self.loader.faces.get(&glyph_key.font_key) { - let index = face.ft_face.get_char_index(glyph_key.character as usize); + if let Some(c) = glyph_key.id.as_char() { + if let Some(face) = self.loader.faces.get(&glyph_key.font_key) { + let index = face.ft_face.get_char_index(c as usize); - if index != 0 { - return glyph_key.font_key; + if index != 0 { + return glyph_key.font_key; + } } } @@ -309,10 +312,16 @@ impl FreeTypeRasterizer { } fn load_face_with_glyph(&mut self, glyph: GlyphKey) -> Result { + let c = if let Some(c) = glyph.id.as_char() { + c + } else { + return Ok(glyph.font_key); + }; + let fallback_list = self.fallback_lists.get(&glyph.font_key).unwrap(); // Check whether glyph is presented in any fallback font. - if !fallback_list.coverage.has_char(glyph.character) { + if !fallback_list.coverage.has_char(c) { return Ok(glyph.font_key); } @@ -321,7 +330,7 @@ impl FreeTypeRasterizer { let font_pattern = &fallback_font.pattern; match self.loader.faces.get(&font_key) { Some(face) => { - let index = face.ft_face.get_char_index(glyph.character as usize); + let index = face.ft_face.get_char_index(c as usize); // We found something in a current face, so let's use it. if index != 0 { @@ -329,8 +338,7 @@ impl FreeTypeRasterizer { } }, None => { - if !font_pattern.get_charset().map_or(false, |cs| cs.has_char(glyph.character)) - { + if !font_pattern.get_charset().map_or(false, |cs| cs.has_char(c)) { continue; } @@ -350,7 +358,18 @@ impl FreeTypeRasterizer { // Render a normal character if it's not a cursor. let font_key = self.face_for_glyph(glyph_key); let face = &self.loader.faces[&font_key]; - let index = face.ft_face.get_char_index(glyph_key.character as usize) as u32; + + let index = if let Some(c) = glyph_key.id.as_char() { + face.ft_face.get_char_index(c as usize) as u32 + } else { + let val = glyph_key.id.value(); + if val == 0 { + face.placeholder_glyph_index + } else { + val + } + }; + let pixelsize = face .non_scalable .unwrap_or_else(|| glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.); @@ -399,7 +418,7 @@ impl FreeTypeRasterizer { Self::normalize_buffer(&glyph.bitmap(), &face.rgba)?; let mut rasterized_glyph = RasterizedGlyph { - character: glyph_key.character, + id: glyph_key.id, top: glyph.bitmap_top(), left: glyph.bitmap_left(), width: pixel_width, @@ -612,10 +631,10 @@ impl From for Error { unsafe impl Send for FreeTypeRasterizer {} -struct FreeTypeLoader { +pub struct FreeTypeLoader { library: Library, - faces: HashMap, - ft_faces: HashMap>, + pub faces: HashMap, + pub ft_faces: HashMap>, } impl FreeTypeLoader { @@ -663,6 +682,10 @@ impl FreeTypeLoader { None => self.load_ft_face(ft_face_location)?, }; + // This will be different for each font so we can't use a constant but we don't want to + // look it up every time so we cache it on font load. + let placeholder_glyph_index = ft_face.get_char_index(' ' as usize); + let non_scalable = if pattern.scalable().next().unwrap_or(true) { None } else { @@ -696,6 +719,7 @@ impl FreeTypeLoader { pixelsize_fixup_factor, ft_face, rgba, + placeholder_glyph_index, }; debug!("Loaded Face {:?}", face); diff --git a/src/lib.rs b/src/lib.rs index c825af3..81aaef5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -96,9 +96,59 @@ impl FontKey { } } +/// An identifier of a glyph. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct GlyphId(u32); + +const C_BIT: u32 = 0b1000_0000_0000_0000_0000_0000_0000_0000; + +const C_MASK: u32 = !C_BIT; + +impl GlyphId { + /// Creates a `GlyphId` representing a unicode scalar value. + pub fn char(c: char) -> Self { + Self(c as u32 | C_BIT) + } + + /// Creates a `GlyphId` representing a placeholder value. + pub fn placeholder() -> Self { + Self(0) + } + + pub fn value(self) -> u32 { + self.0 + } + + pub fn as_char(self) -> Option { + let value = self.value(); + + if value & C_BIT == 0 { + None + } else { + // SAFETY: this is safe because we never construct a `GlyphId` with the C_BIT set + // with an invalid character. + unsafe { Some(char::from_u32_unchecked(value & C_MASK)) } + } + } +} + +impl fmt::Debug for GlyphId { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut f = f.debug_tuple("GlyphId"); + + if let Some(c) = self.as_char() { + f.field(&c); + } else { + f.field(&self.value()); + } + + f.finish() + } +} + #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub struct GlyphKey { - pub character: char, + pub id: GlyphId, pub font_key: FontKey, pub size: Size, } @@ -149,7 +199,7 @@ impl From for Size { #[derive(Clone)] pub struct RasterizedGlyph { - pub character: char, + pub id: GlyphId, pub width: i32, pub height: i32, pub top: i32, @@ -169,7 +219,7 @@ pub enum BitmapBuffer { impl Default for RasterizedGlyph { fn default() -> RasterizedGlyph { RasterizedGlyph { - character: ' ', + id: GlyphId::placeholder(), width: 0, height: 0, top: 0, @@ -182,7 +232,7 @@ impl Default for RasterizedGlyph { impl fmt::Debug for RasterizedGlyph { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("RasterizedGlyph") - .field("character", &self.character) + .field("id", &self.id) .field("width", &self.width) .field("height", &self.height) .field("top", &self.top) @@ -232,9 +282,7 @@ impl Display for Error { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { match self { Error::FontNotFound(font) => write!(f, "font {:?} not found", font), - Error::MissingGlyph(glyph) => { - write!(f, "glyph for character {:?} not found", glyph.character) - }, + Error::MissingGlyph(glyph) => write!(f, "glyph for {:?} not found", glyph.id), Error::UnknownFontKey => f.write_str("invalid font key"), Error::MetricsNotFound => f.write_str("metrics not found"), Error::PlatformError(err) => write!(f, "{}", err), From 15a11ea025338908480fc9f39a18e89fc2d3d78f Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Fri, 12 Nov 2021 12:13:47 +0800 Subject: [PATCH 2/6] Use TryFrom instead of associated function --- src/lib.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 81aaef5..27435f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -120,14 +120,17 @@ impl GlyphId { } pub fn as_char(self) -> Option { + use std::convert::TryFrom; let value = self.value(); if value & C_BIT == 0 { None } else { - // SAFETY: this is safe because we never construct a `GlyphId` with the C_BIT set - // with an invalid character. - unsafe { Some(char::from_u32_unchecked(value & C_MASK)) } + match char::try_from(value & C_MASK) { + Ok(c) => Some(c), + // we never construct a `GlyphId` with the C_BIT set with an invalid character. + Err(_) => unreachable!(), + } } } } From 16ecff452d37622a110b81243eda6f51407acb79 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Sun, 14 Nov 2021 11:37:20 +0800 Subject: [PATCH 3/6] Add `with_glyph_index` function --- src/lib.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 27435f2..e2ad246 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -110,6 +110,14 @@ impl GlyphId { Self(c as u32 | C_BIT) } + /// Creates a `GlyphId` representing a glyph index. + /// + /// The index must not have the most significant bit set. + pub fn with_glyph_index(n: u32) -> Self { + assert!(n < C_BIT); + Self(n) + } + /// Creates a `GlyphId` representing a placeholder value. pub fn placeholder() -> Self { Self(0) From 5249a56342300124f357528e3cd1d9f0316ea096 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 17 Nov 2021 22:06:24 +0800 Subject: [PATCH 4/6] Add location to FaceLoadingProperties --- src/ft/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ft/mod.rs b/src/ft/mod.rs index ed185b7..56d32be 100644 --- a/src/ft/mod.rs +++ b/src/ft/mod.rs @@ -63,6 +63,7 @@ pub struct FaceLoadingProperties { matrix: Option, pixelsize_fixup_factor: Option, ft_face: Rc, + pub location: FtFaceLocation, rgba: Rgba, placeholder_glyph_index: u32, } @@ -718,6 +719,7 @@ impl FreeTypeLoader { matrix, pixelsize_fixup_factor, ft_face, + location: ft_face_location, rgba, placeholder_glyph_index, }; From 5c18316b24a94a7b5caaaf26bad4a066c52d7730 Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Wed, 17 Nov 2021 22:43:02 +0800 Subject: [PATCH 5/6] Fix error --- src/ft/fc/pattern.rs | 2 +- src/ft/mod.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ft/fc/pattern.rs b/src/ft/fc/pattern.rs index 8a6f511..e25f4dd 100644 --- a/src/ft/fc/pattern.rs +++ b/src/ft/fc/pattern.rs @@ -345,7 +345,7 @@ macro_rules! string_accessor { #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub struct PatternHash(pub u32); -#[derive(Hash, Eq, PartialEq, Debug)] +#[derive(Clone, Hash, Eq, PartialEq, Debug)] pub struct FtFaceLocation { pub path: PathBuf, pub index: isize, diff --git a/src/ft/mod.rs b/src/ft/mod.rs index 56d32be..a65e0af 100644 --- a/src/ft/mod.rs +++ b/src/ft/mod.rs @@ -680,7 +680,7 @@ impl FreeTypeLoader { let ft_face = match self.ft_faces.get(&ft_face_location) { Some(ft_face) => Rc::clone(ft_face), - None => self.load_ft_face(ft_face_location)?, + None => self.load_ft_face(ft_face_location.clone())?, }; // This will be different for each font so we can't use a constant but we don't want to From dbf0fc720f75918e56c7d8b14fd42bf40b159ede Mon Sep 17 00:00:00 2001 From: Deadbeef Date: Thu, 18 Nov 2021 00:53:12 +0800 Subject: [PATCH 6/6] Add a cross platform way of getting path of a font --- src/darwin/mod.rs | 21 ++++++++++++++++++--- src/directwrite/mod.rs | 9 +++++++++ src/ft/mod.rs | 23 +++++++++++++---------- src/lib.rs | 6 ++++++ 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/src/darwin/mod.rs b/src/darwin/mod.rs index bfe515e..b738a2a 100644 --- a/src/darwin/mod.rs +++ b/src/darwin/mod.rs @@ -64,7 +64,7 @@ impl Descriptor { font_name: desc.font_name(), style_name: desc.style_name(), display_name: desc.display_name(), - font_path: desc.font_path().unwrap_or_else(PathBuf::new), + font_path: desc.font_path().unwrap_or_default(), ct_descriptor: desc, } } @@ -89,11 +89,13 @@ impl Descriptor { // Investigate if we can actually use the .-prefixed // fallbacks somehow. if let Ok(apple_symbols) = new_from_name("Apple Symbols", size) { + let path = apple_symbols.copy_descriptor().font_path().unwrap_or_default(); let mut font = Font { cg_font: apple_symbols.copy_to_CGFont(), ct_font: apple_symbols, fallbacks: Vec::new(), placeholder_glyph_index: 0, + path, }; font.placeholder_glyph_index = font.glyph_index(' '); fallbacks.push(font); @@ -104,7 +106,13 @@ impl Descriptor { Vec::new() }; - let mut font = Font { ct_font, cg_font, fallbacks, placeholder_glyph_index: 0 }; + let mut font = Font { + ct_font, + cg_font, + fallbacks, + placeholder_glyph_index: 0, + path: self.font_path.clone(), + }; font.placeholder_glyph_index = font.glyph_index(' '); font } @@ -190,6 +198,10 @@ impl crate::Rasterize for Rasterizer { fn update_dpr(&mut self, device_pixel_ratio: f32) { self.device_pixel_ratio = device_pixel_ratio; } + + fn font_path(&self, key: FontKey) -> Result<&std::path::Path, Error> { + self.fonts.get(&key).ok_or(Error::UnknownFontKey).map(|font| font.path.as_path()) + } } impl Rasterizer { @@ -335,6 +347,7 @@ pub struct Font { cg_font: CGFont, fallbacks: Vec, placeholder_glyph_index: u32, + path: PathBuf, } unsafe impl Send for Font {} @@ -511,6 +524,8 @@ impl Font { #[cfg(test)] mod tests { + use crate::GlyphId; + use super::BitmapBuffer; #[test] @@ -533,7 +548,7 @@ mod tests { // Get a glyph. for character in &['a', 'b', 'c', 'd'] { let glyph_index = font.glyph_index(*character); - let glyph = font.get_glyph(*character, glyph_index, false); + let glyph = font.get_glyph(GlyphId::char(*character), glyph_index, false); let buffer = match &glyph.buffer { BitmapBuffer::Rgb(buffer) | BitmapBuffer::Rgba(buffer) => buffer, diff --git a/src/directwrite/mod.rs b/src/directwrite/mod.rs index 7827a55..a3dd19c 100644 --- a/src/directwrite/mod.rs +++ b/src/directwrite/mod.rs @@ -4,6 +4,7 @@ use std::borrow::Cow; use std::collections::HashMap; use std::ffi::OsString; use std::os::windows::ffi::OsStringExt; +use std::path::{Path, PathBuf}; use dwrote::{ FontCollection, FontFace, FontFallback, FontStretch, FontStyle, FontWeight, GlyphOffset, @@ -31,6 +32,7 @@ struct Font { style: FontStyle, stretch: FontStretch, placeholder_glyph_index: u16, + path: PathBuf, } pub struct DirectWriteRasterizer { @@ -266,6 +268,10 @@ impl crate::Rasterize for DirectWriteRasterizer { fn update_dpr(&mut self, device_pixel_ratio: f32) { self.device_pixel_ratio = device_pixel_ratio; } + + fn font_path(&self, key: FontKey) -> Result<&Path, Error> { + Ok(&self.get_loaded_font(key)?.path) + } } fn em_size(size: Size) -> f32 { @@ -277,6 +283,8 @@ impl From for Font { let face = font.create_font_face(); let placeholder_glyph_index = face.get_glyph_indices(&[' ' as u32]).first().copied().unwrap_or(MISSING_GLYPH_INDEX); + let files = face.get_files(); + let path = files.first().unwrap().get_font_file_path().unwrap(); Font { face, @@ -285,6 +293,7 @@ impl From for Font { style: font.style(), stretch: font.stretch(), placeholder_glyph_index, + path, } } } diff --git a/src/ft/mod.rs b/src/ft/mod.rs index a65e0af..4b427c6 100644 --- a/src/ft/mod.rs +++ b/src/ft/mod.rs @@ -3,6 +3,7 @@ use std::cmp::{min, Ordering}; use std::collections::HashMap; use std::fmt::{self, Formatter}; +use std::path::PathBuf; use std::rc::Rc; use std::time::{Duration, Instant}; @@ -63,7 +64,6 @@ pub struct FaceLoadingProperties { matrix: Option, pixelsize_fixup_factor: Option, ft_face: Rc, - pub location: FtFaceLocation, rgba: Rgba, placeholder_glyph_index: u32, } @@ -88,7 +88,7 @@ impl fmt::Debug for FaceLoadingProperties { /// Rasterizes glyphs for a single font face. pub struct FreeTypeRasterizer { - pub loader: FreeTypeLoader, + loader: FreeTypeLoader, fallback_lists: HashMap, device_pixel_ratio: f32, @@ -118,7 +118,7 @@ impl Rasterize for FreeTypeRasterizer { } fn metrics(&self, key: FontKey, _size: Size) -> Result { - let face = &mut self.loader.faces.get(&key).ok_or(Error::UnknownFontKey)?; + let face = &self.loader.faces.get(&key).ok_or(Error::UnknownFontKey)?.0; let full = self.full_metrics(face)?; let ascent = (full.size_metrics.ascender / 64) as f32; @@ -181,6 +181,10 @@ impl Rasterize for FreeTypeRasterizer { fn update_dpr(&mut self, device_pixel_ratio: f32) { self.device_pixel_ratio = device_pixel_ratio; } + + fn font_path(&self, key: FontKey) -> Result<&std::path::Path, Error> { + self.loader.faces.get(&key).ok_or(Error::UnknownFontKey).map(|(_, path)| path.as_path()) + } } impl From for fc::Slant { @@ -301,7 +305,7 @@ impl FreeTypeRasterizer { fn face_for_glyph(&mut self, glyph_key: GlyphKey) -> FontKey { if let Some(c) = glyph_key.id.as_char() { if let Some(face) = self.loader.faces.get(&glyph_key.font_key) { - let index = face.ft_face.get_char_index(c as usize); + let index = face.0.ft_face.get_char_index(c as usize); if index != 0 { return glyph_key.font_key; @@ -331,7 +335,7 @@ impl FreeTypeRasterizer { let font_pattern = &fallback_font.pattern; match self.loader.faces.get(&font_key) { Some(face) => { - let index = face.ft_face.get_char_index(c as usize); + let index = face.0.ft_face.get_char_index(c as usize); // We found something in a current face, so let's use it. if index != 0 { @@ -358,7 +362,7 @@ impl FreeTypeRasterizer { fn get_rendered_glyph(&mut self, glyph_key: GlyphKey) -> Result { // Render a normal character if it's not a cursor. let font_key = self.face_for_glyph(glyph_key); - let face = &self.loader.faces[&font_key]; + let face = &self.loader.faces[&font_key].0; let index = if let Some(c) = glyph_key.id.as_char() { face.ft_face.get_char_index(c as usize) as u32 @@ -634,8 +638,8 @@ unsafe impl Send for FreeTypeRasterizer {} pub struct FreeTypeLoader { library: Library, - pub faces: HashMap, - pub ft_faces: HashMap>, + faces: HashMap, + ft_faces: HashMap>, } impl FreeTypeLoader { @@ -719,14 +723,13 @@ impl FreeTypeLoader { matrix, pixelsize_fixup_factor, ft_face, - location: ft_face_location, rgba, placeholder_glyph_index, }; debug!("Loaded Face {:?}", face); - self.faces.insert(font_key, face); + self.faces.insert(font_key, (face, ft_face_location.path)); Ok(Some(font_key)) } else { diff --git a/src/lib.rs b/src/lib.rs index e2ad246..72a850e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ use std::fmt::{self, Display, Formatter}; use std::ops::{Add, Mul}; +use std::path::Path; use std::sync::atomic::{AtomicUsize, Ordering}; // If target isn't macos or windows, reexport everything from ft. @@ -318,4 +319,9 @@ pub trait Rasterize { /// Update the Rasterizer's DPI factor. fn update_dpr(&mut self, device_pixel_ratio: f32); + + /// Get the path of a font by its key. + /// + /// This is useful when you want to load the font for another library. + fn font_path(&self, _: FontKey) -> Result<&Path, Error>; }