diff options
author | Martin Algesten <martin@algesten.se> | 2017-05-31 12:53:12 +0200 |
---|---|---|
committer | Joe Wilm <jwilm@users.noreply.github.com> | 2017-06-06 09:47:32 -0700 |
commit | 40de8402a709d87421a5d287087a87b7b18a9d02 (patch) | |
tree | 98e264daf78ca21a722c5e3ea56b118b5a3cf45b | |
parent | 1e064fb3e772ad6249d132c7d63a17d233029621 (diff) | |
download | alacritty-40de8402a709d87421a5d287087a87b7b18a9d02.tar.gz alacritty-40de8402a709d87421a5d287087a87b7b18a9d02.zip |
macOS use system font fallback
-rw-r--r-- | font/src/darwin/mod.rs | 157 |
1 files changed, 133 insertions, 24 deletions
diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index 84fc4ff9..940a7239 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -23,14 +23,13 @@ use ::{Slant, Weight, Style}; use core_foundation::base::TCFType; use core_foundation::string::{CFString, CFStringRef}; -use core_foundation::array::CFIndex; -use core_foundation_sys::string::UniChar; +use core_foundation::array::{CFIndex, CFArray}; use core_graphics::base::kCGImageAlphaPremultipliedFirst; use core_graphics::color_space::CGColorSpace; use core_graphics::context::{CGContext}; use core_graphics::font::{CGFont, CGGlyph}; use core_graphics::geometry::{CGPoint, CGRect, CGSize}; -use core_text::font::{CTFont, new_from_descriptor as ct_new_from_descriptor}; +use core_text::font::{CTFont, new_from_descriptor as ct_new_from_descriptor, cascade_list_for_languages as ct_cascade_list_for_languages}; use core_text::font_collection::create_for_family; use core_text::font_collection::get_family_names as ct_get_family_names; use core_text::font_descriptor::kCTFontDefaultOrientation; @@ -65,6 +64,19 @@ pub struct Descriptor { ct_descriptor: CTFontDescriptor } +impl Descriptor { + fn new(desc:CTFontDescriptor) -> Descriptor { + Descriptor { + family_name: desc.family_name(), + font_name: desc.font_name(), + style_name: desc.style_name(), + display_name: desc.display_name(), + font_path: desc.font_path().unwrap_or_else(||{"".to_owned()}), + ct_descriptor: desc, + } + } +} + /// Rasterizer, the main type exported by this package /// /// Given a fontdesc, can rasterize fonts. @@ -142,7 +154,22 @@ impl ::Rasterize for Rasterizer { .get(&(desc.to_owned(), size)) .map(|k| Ok(*k)) .unwrap_or_else(|| { - let font = self.get_font(desc, size)?; + let mut font = self.get_font(desc, size)?; + + // TODO, we can't use apple's proposed + // .Apple Symbol Fallback (filtered out below), + // but not having these makes us not able to render + // many chars. We add the symbols back in. + // Investigate if we can actually use the .-prefixed + // fallbacks somehow. + { + let symbols = { + let d = FontDesc::new("Apple Symbols".to_owned(), desc.style.clone()); + self.get_font(&d, size)? + }; + font.fallbacks.push(symbols); + } + let key = FontKey::next(); self.fonts.insert(key, font); @@ -154,12 +181,25 @@ impl ::Rasterize for Rasterizer { /// Get rasterized glyph for given glyph key fn get_glyph(&mut self, glyph: &GlyphKey) -> Result<RasterizedGlyph, Error> { - let scaled_size = self.device_pixel_ratio * glyph.size.as_f32_pts(); - self.fonts + // get loaded font + let font = self.fonts .get(&glyph.font_key) - .ok_or(Error::FontNotLoaded)? - .get_glyph(glyph.c, scaled_size as _, self.use_thin_strokes) + .ok_or(Error::FontNotLoaded)?; + + // first try the font itself as a direct hit + self.maybe_get_glyph(glyph, font) + .unwrap_or_else(|| { + // then try fallbacks + for fallback in &font.fallbacks { + if let Some(result) = self.maybe_get_glyph(glyph, &fallback) { + // found a fallback + return result; + } + } + // no fallback, give up. + Err(Error::MissingGlyph(glyph.c)) + }) } } @@ -175,7 +215,7 @@ impl Rasterizer { if descriptor.style_name == style { // Found the font we want let scaled_size = size.as_f32_pts() as f64 * self.device_pixel_ratio as f64; - let font = descriptor.to_font(scaled_size); + let font = descriptor.to_font(scaled_size, true); return Ok(font); } } @@ -202,7 +242,7 @@ impl Rasterizer { let descriptors = descriptors_for_family(&desc.name[..]); for descriptor in descriptors { - let font = descriptor.to_font(scaled_size); + let font = descriptor.to_font(scaled_size, true); if font.is_bold() == bold && font.is_italic() == italic { // Found the font we want return Ok(font); @@ -220,6 +260,22 @@ impl Rasterizer { }, } } + + // Helper to try and get a glyph for a given font. Used for font fallback. + fn maybe_get_glyph( + &self, + glyph: &GlyphKey, + font: &Font, + ) -> Option<Result<RasterizedGlyph, Error>> { + let scaled_size = self.device_pixel_ratio * glyph.size.as_f32_pts(); + font.get_glyph(glyph.c, scaled_size as _, self.use_thin_strokes) + .map(|r| Some(Ok(r))) + .unwrap_or_else(|e| match e { + Error::MissingGlyph(_) => None, + _ => Some(Err(e)), + }) + } + } /// Specifies the intended rendering orientation of the font for obtaining glyph metrics @@ -241,6 +297,7 @@ impl Default for FontOrientation { pub struct Font { ct_font: CTFont, cg_font: CGFont, + fallbacks: Vec<Font>, } unsafe impl Send for Font {} @@ -259,6 +316,36 @@ pub fn get_family_names() -> Vec<String> { owned_names } + +/// Return fallback descriptors for font/language list +fn cascade_list_for_languages( + ct_font: &CTFont, + languages: &Vec<String> +) -> Vec<Descriptor> { + + // convert language type &Vec<String> -> CFArray + let langarr:CFArray = { + let tmp:Vec<CFString> = languages.iter() + .map(|language| CFString::new(&language)) + .collect(); + CFArray::from_CFTypes(&tmp) + }; + + // CFArray of CTFontDescriptorRef (again) + let list = ct_cascade_list_for_languages(ct_font, &langarr); + + // convert CFArray to Vec<Descriptor> + list.into_iter() + .map(|fontdesc| { + let desc: CTFontDescriptor = unsafe { + TCFType::wrap_under_get_rule(fontdesc as CTFontDescriptorRef) + }; + Descriptor::new(desc) + }) + .collect() +} + + /// Get descriptors for family name pub fn descriptors_for_family(family: &str) -> Vec<Descriptor> { let mut out = Vec::new(); @@ -274,14 +361,7 @@ pub fn descriptors_for_family(family: &str) -> Vec<Descriptor> { let desc: CTFontDescriptor = unsafe { TCFType::wrap_under_get_rule(descriptor as CTFontDescriptorRef) }; - out.push(Descriptor { - family_name: desc.family_name(), - font_name: desc.font_name(), - style_name: desc.style_name(), - display_name: desc.display_name(), - font_path: desc.font_path().unwrap_or_else(||{"".to_owned()}), - ct_descriptor: desc, - }); + out.push(Descriptor::new(desc)); } out @@ -289,12 +369,31 @@ pub fn descriptors_for_family(family: &str) -> Vec<Descriptor> { impl Descriptor { /// Create a Font from this descriptor - pub fn to_font(&self, size: f64) -> Font { + pub fn to_font(&self, size: f64, load_fallbacks:bool) -> Font { let ct_font = ct_new_from_descriptor(&self.ct_descriptor, size); let cg_font = ct_font.copy_to_CGFont(); + + let fallbacks = if load_fallbacks { + // TODO fixme, hardcoded en for english + cascade_list_for_languages(&ct_font, &vec!["en".to_owned()]) + .into_iter() + // the system lists contains (at least) two strange fonts: + // .Apple Symbol Fallback + // .Noto Sans Universal + // both have a .-prefix (to indicate they are internal?) + // neither work very well. the latter even breaks things because + // it defines code points with just [?] glyphs. + .filter(|desc| desc.font_path != "") + .map(|desc| desc.to_font(size, false)) + .collect() + } else { + vec![] + }; + Font { ct_font: ct_font, cg_font: cg_font, + fallbacks: fallbacks, } } } @@ -438,13 +537,23 @@ impl Font { } fn glyph_index(&self, character: char) -> Option<u32> { - let chars = [character as UniChar]; - let mut glyphs = [0 as CGGlyph]; + // encode this char as utf-16 + let mut buf = [0; 2]; + let encoded:&[u16] = character.encode_utf16(&mut buf); + // and use the utf-16 buffer to get the index + self.glyph_index_utf16(encoded) + } + fn glyph_index_utf16(&self, encoded: &[u16]) -> Option<u32> { + + // output buffer for the glyph. for non-BMP glyphs, like + // emojis, this will be filled with two chars the second + // always being a 0. + let mut glyphs:[CGGlyph; 2] = [0; 2]; let res = self.ct_font.get_glyphs_for_characters( - &chars[0], - &mut glyphs[0], - 1 as CFIndex + encoded.as_ptr(), + glyphs.as_mut_ptr(), + encoded.len() as CFIndex ); if res { |