diff --git a/src/font/ftfont.rs b/src/font/ftfont.rs index bf0753d59..7fdc65ff4 100644 --- a/src/font/ftfont.rs +++ b/src/font/ftfont.rs @@ -78,13 +78,7 @@ impl Font for FreeTypeFontImpl { // when changing the load flags, we also need // to change them for harfbuzz otherwise it won't // hint correctly - let load_flags = (ftwrap::FT_LOAD_COLOR) as i32 | - // enable FT_LOAD_TARGET bits. There are no flags defined - // for these in the bindings so we do some bit magic for - // ourselves. This is how the FT_LOAD_TARGET_() macro - // assembles these bits. - (render_mode as i32) << 16; - + let load_flags = ftwrap::compute_load_flags_for_mode(render_mode); self.font.borrow_mut().set_load_flags(load_flags); // This clone is conceptually unsafe, but ok in practice as we are diff --git a/src/font/ftwrap.rs b/src/font/ftwrap.rs index 798c9e863..7ccef7564 100644 --- a/src/font/ftwrap.rs +++ b/src/font/ftwrap.rs @@ -1,5 +1,6 @@ //! Higher level freetype bindings +use crate::font::loader::FontDataHandle; use failure::{bail, format_err, Error, Fallible, ResultExt}; pub use freetype::*; use std::ffi::CString; @@ -19,6 +20,15 @@ fn ft_result(err: FT_Error, t: T) -> Result { } } +pub fn compute_load_flags_for_mode(render_mode: FT_Render_Mode) -> i32 { + FT_LOAD_COLOR as i32 | + // enable FT_LOAD_TARGET bits. There are no flags defined + // for these in the bindings so we do some bit magic for + // ourselves. This is how the FT_LOAD_TARGET_() macro + // assembles these bits. + (render_mode as i32) << 16 +} + pub struct Face { pub face: FT_Face, _bytes: Vec, @@ -172,6 +182,15 @@ impl Library { Ok(Library { lib }) } + pub fn face_from_locator(&self, handle: &FontDataHandle) -> Fallible { + match handle { + FontDataHandle::OnDisk { path, index } => { + self.new_face(path.to_str().unwrap(), *index as _) + } + FontDataHandle::Memory { data, index } => self.new_face_from_slice(&data, *index as _), + } + } + #[cfg(any(target_os = "macos", windows))] pub fn load_font_kit_handle(&self, handle: &font_kit::handle::Handle) -> Result { use font_kit::handle::Handle; diff --git a/src/font/loader/mod.rs b/src/font/loader/mod.rs index 1c1fe9c66..d4961be14 100644 --- a/src/font/loader/mod.rs +++ b/src/font/loader/mod.rs @@ -1,6 +1,8 @@ use crate::config::FontAttributes; -use failure::Fallible; +use failure::{format_err, Error, Fallible}; +use serde_derive::*; use std::path::PathBuf; +use std::sync::Mutex; #[cfg(all(unix, any(feature = "fontconfig", not(target_os = "macos"))))] pub mod font_config; @@ -25,3 +27,79 @@ pub trait FontLocator { /// FontDataHandle's that correspond to it fn load_fonts(&self, fonts_selection: &[FontAttributes]) -> Fallible>; } + +#[derive(Debug, Deserialize, Clone, Copy)] +pub enum FontLocatorSelection { + FontConfig, + FontLoader, + FontKit, +} + +lazy_static::lazy_static! { + static ref DEFAULT_LOCATOR: Mutex = Mutex::new(Default::default()); +} + +impl Default for FontLocatorSelection { + fn default() -> Self { + if cfg!(all(unix, not(target_os = "macos"))) { + FontLocatorSelection::FontConfig + } else { + FontLocatorSelection::FontLoader + } + } +} + +impl FontLocatorSelection { + pub fn set_default(self) { + let mut def = DEFAULT_LOCATOR.lock().unwrap(); + *def = self; + } + + pub fn get_default() -> Self { + let def = DEFAULT_LOCATOR.lock().unwrap(); + *def + } + + pub fn variants() -> Vec<&'static str> { + vec!["FontConfig", "FontLoader", "FontKit"] + } + + pub fn new_locator(self) -> Box { + match self { + Self::FontConfig => { + #[cfg(all(unix, any(feature = "fontconfig", not(target_os = "macos"))))] + return Box::new(font_config::FontConfigFontLocator {}); + #[cfg(not(all(unix, any(feature = "fontconfig", not(target_os = "macos")))))] + panic!("fontconfig not compiled in"); + } + Self::FontLoader => { + #[cfg(any(target_os = "macos", windows))] + return Box::new(font_loader::FontLoaderFontLocator {}); + #[cfg(not(any(target_os = "macos", windows)))] + panic!("fontloader not compiled in"); + } + Self::FontKit => { + #[cfg(any(target_os = "macos", windows))] + return Box::new(::font_kit::source::SystemSource::new()); + #[cfg(not(any(target_os = "macos", windows)))] + panic!("fontkit not compiled in"); + } + } + } +} + +impl std::str::FromStr for FontLocatorSelection { + type Err = Error; + fn from_str(s: &str) -> Result { + match s.to_lowercase().as_ref() { + "fontconfig" => Ok(Self::FontConfig), + "fontloader" => Ok(Self::FontLoader), + "fontkit" => Ok(Self::FontKit), + _ => Err(format_err!( + "{} is not a valid FontLocatorSelection variant, possible values are {:?}", + s, + Self::variants() + )), + } + } +} diff --git a/src/font/mod.rs b/src/font/mod.rs index 35507c8db..b377e83f5 100644 --- a/src/font/mod.rs +++ b/src/font/mod.rs @@ -34,19 +34,28 @@ pub mod fontloader_and_freetype; #[cfg(any(target_os = "macos", windows))] pub mod fontkit; +use crate::font::loader::{FontLocator, FontLocatorSelection}; + use super::config::{configuration, ConfigHandle, TextStyle}; use term::CellAttributes; +pub struct LoadedFont { + rasterizer: Box, + shaper: Box, +} + type FontPtr = Rc>>; /// Matches and loads fonts for a given input style pub struct FontConfiguration { fonts: RefCell>, + new_fonts: RefCell>>, system: Rc, metrics: RefCell>, dpi_scale: RefCell, font_scale: RefCell, config_generation: RefCell, + loader: Box, } #[derive(Debug, Deserialize, Clone, Copy)] @@ -111,6 +120,7 @@ impl FontSystemSelection { } } } + pub fn variants() -> Vec<&'static str> { vec![ "FontConfigAndFreeType", @@ -153,8 +163,11 @@ impl std::str::FromStr for FontSystemSelection { impl FontConfiguration { /// Create a new empty configuration pub fn new(system: FontSystemSelection) -> Self { + let loader = FontLocatorSelection::get_default().new_locator(); Self { fonts: RefCell::new(HashMap::new()), + loader, + new_fonts: RefCell::new(HashMap::new()), system: system.new_font_system(), metrics: RefCell::new(None), font_scale: RefCell::new(1.0), diff --git a/src/font/rasterizer/freetype.rs b/src/font/rasterizer/freetype.rs index 74d004ad7..97f6d6295 100644 --- a/src/font/rasterizer/freetype.rs +++ b/src/font/rasterizer/freetype.rs @@ -19,12 +19,7 @@ impl FontRasterizer for FreeTypeRasterizer { // ftwrap::FT_Render_Mode::FT_RENDER_MODE_LCD; ftwrap::FT_Render_Mode::FT_RENDER_MODE_LIGHT; - let load_flags = (ftwrap::FT_LOAD_COLOR) as i32 | - // enable FT_LOAD_TARGET bits. There are no flags defined - // for these in the bindings so we do some bit magic for - // ourselves. This is how the FT_LOAD_TARGET_() macro - // assembles these bits. - (render_mode as i32) << 16; + let load_flags = ftwrap::compute_load_flags_for_mode(render_mode); let mut face = self.face.borrow_mut(); let descender = unsafe { (*(*face.face).size).metrics.descender as f64 / 64.0 }; @@ -268,9 +263,7 @@ impl FreeTypeRasterizer { } } - pub fn with_face_size_and_dpi(mut face: ftwrap::Face, size: f64, dpi: u32) -> Fallible { - face.set_font_size(size, dpi)?; - + pub fn with_face(face: ftwrap::Face) -> Fallible { let has_color = unsafe { (((*face.face).face_flags as u32) & (ftwrap::FT_FACE_FLAG_COLOR as u32)) != 0 }; diff --git a/src/font/shaper/harfbuzz.rs b/src/font/shaper/harfbuzz.rs index 86a738010..2df8350ee 100644 --- a/src/font/shaper/harfbuzz.rs +++ b/src/font/shaper/harfbuzz.rs @@ -1,5 +1,6 @@ use crate::font::ftwrap; use crate::font::hbwrap as harfbuzz; +use crate::font::loader::FontDataHandle; use crate::font::shaper::FontShaper; use crate::font::system::GlyphInfo; use failure::{bail, Fallible}; @@ -13,9 +14,24 @@ struct FontPair { pub struct HarfbuzzShaper { fonts: Vec>, + lib: ftwrap::Library, } impl HarfbuzzShaper { + pub fn new(handles: &[FontDataHandle]) -> Fallible { + let lib = ftwrap::Library::new()?; + let mut fonts = vec![]; + for handle in handles { + let face = lib.face_from_locator(handle)?; + let mut font = harfbuzz::Font::new(face.face); + let render_mode = ftwrap::FT_Render_Mode::FT_RENDER_MODE_LIGHT; + let load_flags = ftwrap::compute_load_flags_for_mode(render_mode); + font.set_load_flags(load_flags); + fonts.push(RefCell::new(FontPair { face, font })); + } + Ok(Self { fonts, lib }) + } + fn do_shape( &self, font_idx: crate::font::system::FallbackIdx,