mirror of
https://github.com/wez/wezterm.git
synced 2024-12-25 14:22:37 +03:00
separate out the fontconfig/freetype code
This commit is contained in:
parent
b35fc8a3b1
commit
999396bc45
211
src/font/fontconfigandfreetype.rs
Normal file
211
src/font/fontconfigandfreetype.rs
Normal file
@ -0,0 +1,211 @@
|
||||
//! Systems that use fontconfig and freetype
|
||||
|
||||
pub use self::fcwrap::Pattern as FontPattern;
|
||||
use config::{Config, TextStyle};
|
||||
use failure::{self, Error};
|
||||
use font::{FallbackIdx, Font, FontMetrics, FontSystem, GlyphInfo, NamedFont, shape_with_harfbuzz};
|
||||
use font::{fcwrap, ftwrap};
|
||||
use harfbuzz;
|
||||
use std::cell::RefCell;
|
||||
use std::slice;
|
||||
|
||||
|
||||
pub type FontSystemImpl = FontConfigAndFreeType;
|
||||
|
||||
pub struct FontConfigAndFreeType {}
|
||||
|
||||
impl FontConfigAndFreeType {
|
||||
pub fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
impl FontSystem for FontConfigAndFreeType {
|
||||
fn load_font(&self, config: &Config, style: &TextStyle) -> Result<Box<NamedFont>, Error> {
|
||||
let mut pattern = FontPattern::parse(&style.fontconfig_pattern)?;
|
||||
pattern.add_double("size", config.font_size)?;
|
||||
pattern.add_double("dpi", config.dpi)?;
|
||||
|
||||
Ok(Box::new(NamedFontImpl::new(pattern)?))
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds a loaded font alternative
|
||||
struct FontImpl {
|
||||
face: RefCell<ftwrap::Face>,
|
||||
font: RefCell<harfbuzz::Font>,
|
||||
/// nominal monospace cell height
|
||||
cell_height: f64,
|
||||
/// nominal monospace cell width
|
||||
cell_width: f64,
|
||||
}
|
||||
|
||||
|
||||
impl Font for FontImpl {
|
||||
fn harfbuzz_shape(
|
||||
&self,
|
||||
buf: &mut harfbuzz::Buffer,
|
||||
features: Option<&[harfbuzz::hb_feature_t]>,
|
||||
) {
|
||||
self.font.borrow_mut().shape(buf, features)
|
||||
}
|
||||
fn has_color(&self) -> bool {
|
||||
let face = self.face.borrow();
|
||||
unsafe { ((*face.face).face_flags & ftwrap::FT_FACE_FLAG_COLOR as i64) != 0 }
|
||||
}
|
||||
|
||||
fn metrics(&self) -> FontMetrics {
|
||||
let face = self.face.borrow();
|
||||
FontMetrics {
|
||||
cell_height: self.cell_height,
|
||||
cell_width: self.cell_width,
|
||||
descender: unsafe { (*face.face).descender },
|
||||
}
|
||||
}
|
||||
|
||||
fn load_glyph(&self, glyph_pos: u32) -> Result<ftwrap::FT_GlyphSlotRec_, Error> {
|
||||
let render_mode = //ftwrap::FT_Render_Mode::FT_RENDER_MODE_NORMAL;
|
||||
// ftwrap::FT_Render_Mode::FT_RENDER_MODE_LCD;
|
||||
ftwrap::FT_Render_Mode::FT_RENDER_MODE_LIGHT;
|
||||
|
||||
// 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;
|
||||
|
||||
self.font.borrow_mut().set_load_flags(load_flags);
|
||||
// This clone is conceptually unsafe, but ok in practice as we are
|
||||
// single threaded and don't load any other glyphs in the body of
|
||||
// this load_glyph() function.
|
||||
self.face
|
||||
.borrow_mut()
|
||||
.load_and_render_glyph(glyph_pos, load_flags, render_mode)
|
||||
.map(|g| g.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds "the" font selected by the user. In actuality, it
|
||||
/// holds the set of fallback fonts that match their criteria
|
||||
pub struct NamedFontImpl {
|
||||
lib: ftwrap::Library,
|
||||
pattern: fcwrap::Pattern,
|
||||
font_list: fcwrap::FontSet,
|
||||
fonts: Vec<FontImpl>,
|
||||
}
|
||||
|
||||
impl Drop for NamedFontImpl {
|
||||
fn drop(&mut self) {
|
||||
// Ensure that we drop the fonts before we drop the
|
||||
// library, otherwise we will end up faulting
|
||||
self.fonts.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl NamedFont for NamedFontImpl {
|
||||
fn get_fallback(&mut self, idx: FallbackIdx) -> Result<&Font, Error> {
|
||||
Ok(self.get_font(idx)?)
|
||||
}
|
||||
fn shape(&mut self, s: &str) -> Result<Vec<GlyphInfo>, Error> {
|
||||
shape_with_harfbuzz(self, 0, s)
|
||||
}
|
||||
}
|
||||
|
||||
impl NamedFontImpl {
|
||||
/// Construct a new Font from the user supplied pattern
|
||||
pub fn new(mut pattern: FontPattern) -> Result<Self, Error> {
|
||||
let mut lib = ftwrap::Library::new()?;
|
||||
lib.set_lcd_filter(
|
||||
ftwrap::FT_LcdFilter::FT_LCD_FILTER_DEFAULT,
|
||||
)?;
|
||||
|
||||
// Enable some filtering options and pull in the standard
|
||||
// fallback font selection from the user configuration
|
||||
pattern.monospace()?;
|
||||
pattern.config_substitute(fcwrap::MatchKind::Pattern)?;
|
||||
pattern.default_substitute();
|
||||
|
||||
// and obtain the selection with the best preference
|
||||
// at index 0.
|
||||
let font_list = pattern.sort(true)?;
|
||||
|
||||
Ok(Self {
|
||||
lib,
|
||||
font_list,
|
||||
pattern,
|
||||
fonts: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn load_next_fallback(&mut self) -> Result<(), Error> {
|
||||
let idx = self.fonts.len();
|
||||
let pat = self.font_list.iter().nth(idx).ok_or(failure::err_msg(
|
||||
"no more fallbacks",
|
||||
))?;
|
||||
let pat = self.pattern.render_prepare(&pat)?;
|
||||
let file = pat.get_file()?;
|
||||
|
||||
debug!("load_next_fallback: file={}", file);
|
||||
debug!("{}", pat.format("%{=unparse}")?);
|
||||
|
||||
let size = pat.get_double("size")?;
|
||||
let dpi = pat.get_double("dpi")? as u32;
|
||||
debug!("set_char_size {} dpi={}", size, dpi);
|
||||
// Scaling before truncating to integer minimizes the chances of hitting
|
||||
// the fallback code for set_pixel_sizes below.
|
||||
let size = (size * 64.0) as i64;
|
||||
|
||||
let mut face = self.lib.new_face(file, 0)?;
|
||||
|
||||
match face.set_char_size(size, size, dpi, dpi) {
|
||||
Err(err) => {
|
||||
let sizes = unsafe {
|
||||
let rec = &(*face.face);
|
||||
slice::from_raw_parts(rec.available_sizes, rec.num_fixed_sizes as usize)
|
||||
};
|
||||
if sizes.len() == 0 {
|
||||
return Err(err);
|
||||
} else {
|
||||
// Find the best matching size.
|
||||
// We just take the biggest.
|
||||
let mut size = 0i16;
|
||||
for info in sizes.iter() {
|
||||
size = size.max(info.height);
|
||||
}
|
||||
face.set_pixel_sizes(size as u32, size as u32)?;
|
||||
debug!("fall back to set_pixel_sizes {}", size);
|
||||
}
|
||||
}
|
||||
Ok(_) => {}
|
||||
}
|
||||
let font = harfbuzz::Font::new(face.face);
|
||||
|
||||
// Compute metrics for the nominal monospace cell
|
||||
let (cell_width, cell_height) = face.cell_metrics();
|
||||
debug!("metrics: width={} height={}", cell_width, cell_height);
|
||||
|
||||
self.fonts.push(FontImpl {
|
||||
face: RefCell::new(face),
|
||||
font: RefCell::new(font),
|
||||
cell_height,
|
||||
cell_width,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_font(&mut self, idx: usize) -> Result<&mut FontImpl, Error> {
|
||||
if idx >= self.fonts.len() {
|
||||
self.load_next_fallback()?;
|
||||
ensure!(
|
||||
idx < self.fonts.len(),
|
||||
"should not ask for a font later than the next prepared font"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(&mut self.fonts[idx])
|
||||
}
|
||||
}
|
206
src/font/mod.rs
206
src/font/mod.rs
@ -1,39 +1,27 @@
|
||||
use failure::{self, Error};
|
||||
use failure::Error;
|
||||
use harfbuzz;
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::slice;
|
||||
|
||||
pub mod fontconfigandfreetype;
|
||||
use self::fontconfigandfreetype::FontSystemImpl;
|
||||
|
||||
pub mod ftwrap;
|
||||
pub mod fcwrap;
|
||||
pub mod system;
|
||||
use self::system::{FontSystem, NamedFont};
|
||||
pub use self::system::*;
|
||||
|
||||
pub use self::fcwrap::Pattern as FontPattern;
|
||||
|
||||
pub use self::system::GlyphInfo;
|
||||
use super::config::{Config, TextStyle};
|
||||
use term::CellAttributes;
|
||||
|
||||
struct FontConfigAndFreeType {}
|
||||
|
||||
impl system::FontSystem for FontConfigAndFreeType {
|
||||
fn load_font(&self, config: &Config, style: &TextStyle) -> Result<Box<NamedFont>, Error> {
|
||||
let mut pattern = FontPattern::parse(&style.fontconfig_pattern)?;
|
||||
pattern.add_double("size", config.font_size)?;
|
||||
pattern.add_double("dpi", config.dpi)?;
|
||||
|
||||
Ok(Box::new(Font::new(pattern)?))
|
||||
}
|
||||
}
|
||||
|
||||
/// Matches and loads fonts for a given input style
|
||||
pub struct FontConfiguration {
|
||||
config: Config,
|
||||
fonts: RefCell<HashMap<TextStyle, Rc<RefCell<Box<NamedFont>>>>>,
|
||||
system: FontConfigAndFreeType,
|
||||
system: FontSystemImpl,
|
||||
}
|
||||
|
||||
impl FontConfiguration {
|
||||
@ -42,7 +30,7 @@ impl FontConfiguration {
|
||||
Self {
|
||||
config,
|
||||
fonts: RefCell::new(HashMap::new()),
|
||||
system: FontConfigAndFreeType {},
|
||||
system: FontSystemImpl::new(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -102,186 +90,6 @@ impl FontConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds a loaded font alternative
|
||||
struct FontInfo {
|
||||
face: RefCell<ftwrap::Face>,
|
||||
font: RefCell<harfbuzz::Font>,
|
||||
/// nominal monospace cell height
|
||||
cell_height: f64,
|
||||
/// nominal monospace cell width
|
||||
cell_width: f64,
|
||||
}
|
||||
|
||||
|
||||
impl system::Font for FontInfo {
|
||||
fn harfbuzz_shape(
|
||||
&self,
|
||||
buf: &mut harfbuzz::Buffer,
|
||||
features: Option<&[harfbuzz::hb_feature_t]>,
|
||||
) {
|
||||
self.font.borrow_mut().shape(buf, features)
|
||||
}
|
||||
fn has_color(&self) -> bool {
|
||||
let face = self.face.borrow();
|
||||
unsafe { ((*face.face).face_flags & ftwrap::FT_FACE_FLAG_COLOR as i64) != 0 }
|
||||
}
|
||||
|
||||
fn metrics(&self) -> system::FontMetrics {
|
||||
let face = self.face.borrow();
|
||||
system::FontMetrics {
|
||||
cell_height: self.cell_height,
|
||||
cell_width: self.cell_width,
|
||||
descender: unsafe { (*face.face).descender },
|
||||
}
|
||||
}
|
||||
|
||||
fn load_glyph(&self, glyph_pos: u32) -> Result<ftwrap::FT_GlyphSlotRec_, Error> {
|
||||
let render_mode = //ftwrap::FT_Render_Mode::FT_RENDER_MODE_NORMAL;
|
||||
// ftwrap::FT_Render_Mode::FT_RENDER_MODE_LCD;
|
||||
ftwrap::FT_Render_Mode::FT_RENDER_MODE_LIGHT;
|
||||
|
||||
// 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;
|
||||
|
||||
self.font.borrow_mut().set_load_flags(load_flags);
|
||||
// This clone is conceptually unsafe, but ok in practice as we are
|
||||
// single threaded and don't load any other glyphs in the body of
|
||||
// this load_glyph() function.
|
||||
self.face
|
||||
.borrow_mut()
|
||||
.load_and_render_glyph(glyph_pos, load_flags, render_mode)
|
||||
.map(|g| g.clone())
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds "the" font selected by the user. In actuality, it
|
||||
/// holds the set of fallback fonts that match their criteria
|
||||
pub struct Font {
|
||||
lib: ftwrap::Library,
|
||||
pattern: fcwrap::Pattern,
|
||||
font_list: fcwrap::FontSet,
|
||||
fonts: Vec<FontInfo>,
|
||||
}
|
||||
|
||||
impl Drop for Font {
|
||||
fn drop(&mut self) {
|
||||
// Ensure that we drop the fonts before we drop the
|
||||
// library, otherwise we will end up faulting
|
||||
self.fonts.clear();
|
||||
}
|
||||
}
|
||||
|
||||
impl NamedFont for Font {
|
||||
fn get_fallback(&mut self, idx: system::FallbackIdx) -> Result<&system::Font, Error> {
|
||||
Ok(self.get_font(idx)?)
|
||||
}
|
||||
fn shape(&mut self, s: &str) -> Result<Vec<GlyphInfo>, Error> {
|
||||
shape_with_harfbuzz(self, 0, s)
|
||||
}
|
||||
}
|
||||
|
||||
impl Font {
|
||||
/// Construct a new Font from the user supplied pattern
|
||||
pub fn new(mut pattern: FontPattern) -> Result<Font, Error> {
|
||||
let mut lib = ftwrap::Library::new()?;
|
||||
lib.set_lcd_filter(
|
||||
ftwrap::FT_LcdFilter::FT_LCD_FILTER_DEFAULT,
|
||||
)?;
|
||||
|
||||
// Enable some filtering options and pull in the standard
|
||||
// fallback font selection from the user configuration
|
||||
pattern.monospace()?;
|
||||
pattern.config_substitute(fcwrap::MatchKind::Pattern)?;
|
||||
pattern.default_substitute();
|
||||
|
||||
// and obtain the selection with the best preference
|
||||
// at index 0.
|
||||
let font_list = pattern.sort(true)?;
|
||||
|
||||
Ok(Font {
|
||||
lib,
|
||||
font_list,
|
||||
pattern,
|
||||
fonts: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn load_next_fallback(&mut self) -> Result<(), Error> {
|
||||
let idx = self.fonts.len();
|
||||
let pat = self.font_list.iter().nth(idx).ok_or(failure::err_msg(
|
||||
"no more fallbacks",
|
||||
))?;
|
||||
let pat = self.pattern.render_prepare(&pat)?;
|
||||
let file = pat.get_file()?;
|
||||
|
||||
debug!("load_next_fallback: file={}", file);
|
||||
debug!("{}", pat.format("%{=unparse}")?);
|
||||
|
||||
let size = pat.get_double("size")?;
|
||||
let dpi = pat.get_double("dpi")? as u32;
|
||||
debug!("set_char_size {} dpi={}", size, dpi);
|
||||
// Scaling before truncating to integer minimizes the chances of hitting
|
||||
// the fallback code for set_pixel_sizes below.
|
||||
let size = (size * 64.0) as i64;
|
||||
|
||||
let mut face = self.lib.new_face(file, 0)?;
|
||||
|
||||
match face.set_char_size(size, size, dpi, dpi) {
|
||||
Err(err) => {
|
||||
let sizes = unsafe {
|
||||
let rec = &(*face.face);
|
||||
slice::from_raw_parts(rec.available_sizes, rec.num_fixed_sizes as usize)
|
||||
};
|
||||
if sizes.len() == 0 {
|
||||
return Err(err);
|
||||
} else {
|
||||
// Find the best matching size.
|
||||
// We just take the biggest.
|
||||
let mut size = 0i16;
|
||||
for info in sizes.iter() {
|
||||
size = size.max(info.height);
|
||||
}
|
||||
face.set_pixel_sizes(size as u32, size as u32)?;
|
||||
debug!("fall back to set_pixel_sizes {}", size);
|
||||
}
|
||||
}
|
||||
Ok(_) => {}
|
||||
}
|
||||
let font = harfbuzz::Font::new(face.face);
|
||||
|
||||
// Compute metrics for the nominal monospace cell
|
||||
let (cell_width, cell_height) = face.cell_metrics();
|
||||
debug!("metrics: width={} height={}", cell_width, cell_height);
|
||||
|
||||
self.fonts.push(FontInfo {
|
||||
face: RefCell::new(face),
|
||||
font: RefCell::new(font),
|
||||
cell_height,
|
||||
cell_width,
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn get_font(&mut self, idx: usize) -> Result<&mut FontInfo, Error> {
|
||||
if idx >= self.fonts.len() {
|
||||
self.load_next_fallback()?;
|
||||
ensure!(
|
||||
idx < self.fonts.len(),
|
||||
"should not ask for a font later than the next prepared font"
|
||||
);
|
||||
}
|
||||
|
||||
Ok(&mut self.fonts[idx])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn shape_with_harfbuzz(
|
||||
font: &mut NamedFont,
|
||||
font_idx: system::FallbackIdx,
|
||||
|
Loading…
Reference in New Issue
Block a user