mirror of
https://github.com/wez/wezterm.git
synced 2024-11-23 23:21:08 +03:00
refactor the font loading code
This is the first step towards making this code more system independent.
This commit is contained in:
parent
b66f93ad11
commit
dd4c9d6683
408
src/font/mod.rs
408
src/font/mod.rs
@ -5,20 +5,35 @@ use std::cell::RefCell;
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
use std::slice;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
pub mod ftwrap;
|
||||
pub mod fcwrap;
|
||||
pub mod system;
|
||||
use self::system::{FontSystem, NamedFont};
|
||||
|
||||
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<Font>>>>,
|
||||
fonts: RefCell<HashMap<TextStyle, Rc<RefCell<Box<NamedFont>>>>>,
|
||||
system: FontConfigAndFreeType,
|
||||
}
|
||||
|
||||
impl FontConfiguration {
|
||||
@ -27,35 +42,26 @@ impl FontConfiguration {
|
||||
Self {
|
||||
config,
|
||||
fonts: RefCell::new(HashMap::new()),
|
||||
system: FontConfigAndFreeType {},
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a text style, load (without caching) the font that
|
||||
/// best matches according to the fontconfig pattern.
|
||||
pub fn load_font(&self, style: &TextStyle) -> Result<Rc<RefCell<Font>>, Error> {
|
||||
let mut pattern = FontPattern::parse(&style.fontconfig_pattern)?;
|
||||
pattern.add_double("size", self.config.font_size)?;
|
||||
pattern.add_double("dpi", self.config.dpi)?;
|
||||
|
||||
Ok(Rc::new(RefCell::new(Font::new(pattern)?)))
|
||||
}
|
||||
|
||||
/// Given a text style, load (with caching) the font that best
|
||||
/// matches according to the fontconfig pattern.
|
||||
pub fn cached_font(&self, style: &TextStyle) -> Result<Rc<RefCell<Font>>, Error> {
|
||||
pub fn cached_font(&self, style: &TextStyle) -> Result<Rc<RefCell<Box<NamedFont>>>, Error> {
|
||||
let mut fonts = self.fonts.borrow_mut();
|
||||
|
||||
if let Some(entry) = fonts.get(style) {
|
||||
return Ok(Rc::clone(entry));
|
||||
}
|
||||
|
||||
let font = self.load_font(style)?;
|
||||
let font = Rc::new(RefCell::new(self.system.load_font(&self.config, style)?));
|
||||
fonts.insert(style.clone(), Rc::clone(&font));
|
||||
Ok(font)
|
||||
}
|
||||
|
||||
/// Returns the baseline font specified in the configuration
|
||||
pub fn default_font(&self) -> Result<Rc<RefCell<Font>>, Error> {
|
||||
pub fn default_font(&self) -> Result<Rc<RefCell<Box<NamedFont>>>, Error> {
|
||||
self.cached_font(&self.config.font)
|
||||
}
|
||||
|
||||
@ -96,63 +102,65 @@ impl FontConfiguration {
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds information about a shaped glyph
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GlyphInfo {
|
||||
/// We only retain text in debug mode for diagnostic purposes
|
||||
#[cfg(debug_assertions)]
|
||||
pub text: String,
|
||||
/// Offset within text
|
||||
pub cluster: u32,
|
||||
/// How many cells/columns this glyph occupies horizontally
|
||||
pub num_cells: u8,
|
||||
/// Which font alternative to use; index into Font.fonts
|
||||
pub font_idx: usize,
|
||||
/// Which freetype glyph to load
|
||||
pub glyph_pos: u32,
|
||||
/// How far to advance the render cursor after drawing this glyph
|
||||
pub x_advance: f64,
|
||||
/// How far to advance the render cursor after drawing this glyph
|
||||
pub y_advance: f64,
|
||||
/// Destination render offset
|
||||
pub x_offset: f64,
|
||||
/// Destination render offset
|
||||
pub y_offset: f64,
|
||||
}
|
||||
|
||||
impl GlyphInfo {
|
||||
pub fn new(
|
||||
text: &str,
|
||||
font_idx: usize,
|
||||
info: &harfbuzz::hb_glyph_info_t,
|
||||
pos: &harfbuzz::hb_glyph_position_t,
|
||||
) -> GlyphInfo {
|
||||
let num_cells = UnicodeWidthStr::width(text) as u8;
|
||||
GlyphInfo {
|
||||
#[cfg(debug_assertions)]
|
||||
text: text.into(),
|
||||
num_cells,
|
||||
font_idx,
|
||||
glyph_pos: info.codepoint,
|
||||
cluster: info.cluster,
|
||||
x_advance: pos.x_advance as f64 / 64.0,
|
||||
y_advance: pos.y_advance as f64 / 64.0,
|
||||
x_offset: pos.x_offset as f64 / 64.0,
|
||||
y_offset: pos.y_offset as f64 / 64.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds a loaded font alternative
|
||||
struct FontInfo {
|
||||
face: ftwrap::Face,
|
||||
font: harfbuzz::Font,
|
||||
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 {
|
||||
@ -170,6 +178,15 @@ impl Drop for Font {
|
||||
}
|
||||
}
|
||||
|
||||
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> {
|
||||
@ -244,8 +261,8 @@ impl Font {
|
||||
debug!("metrics: width={} height={}", cell_width, cell_height);
|
||||
|
||||
self.fonts.push(FontInfo {
|
||||
face,
|
||||
font,
|
||||
face: RefCell::new(face),
|
||||
font: RefCell::new(font),
|
||||
cell_height,
|
||||
cell_width,
|
||||
});
|
||||
@ -263,186 +280,131 @@ impl Font {
|
||||
|
||||
Ok(&mut self.fonts[idx])
|
||||
}
|
||||
}
|
||||
|
||||
pub fn has_color(&mut self, idx: usize) -> Result<bool, Error> {
|
||||
let font = self.get_font(idx)?;
|
||||
unsafe {
|
||||
Ok(
|
||||
((*font.face.face).face_flags & ftwrap::FT_FACE_FLAG_COLOR as i64) != 0,
|
||||
)
|
||||
}
|
||||
pub fn shape_with_harfbuzz(
|
||||
font: &mut NamedFont,
|
||||
font_idx: system::FallbackIdx,
|
||||
s: &str,
|
||||
) -> Result<Vec<GlyphInfo>, Error> {
|
||||
let features = vec![
|
||||
// kerning
|
||||
harfbuzz::feature_from_string("kern")?,
|
||||
// ligatures
|
||||
harfbuzz::feature_from_string("liga")?,
|
||||
// contextual ligatures
|
||||
harfbuzz::feature_from_string("clig")?,
|
||||
];
|
||||
|
||||
let mut buf = harfbuzz::Buffer::new()?;
|
||||
buf.set_script(harfbuzz::HB_SCRIPT_LATIN);
|
||||
buf.set_direction(harfbuzz::HB_DIRECTION_LTR);
|
||||
buf.set_language(harfbuzz::language_from_string("en")?);
|
||||
buf.add_str(s);
|
||||
|
||||
{
|
||||
let fallback = font.get_fallback(font_idx)?;
|
||||
fallback.harfbuzz_shape(&mut buf, Some(features.as_slice()));
|
||||
}
|
||||
|
||||
pub fn get_metrics(&mut self) -> Result<(f64, f64, i16), Error> {
|
||||
let font = self.get_font(0)?;
|
||||
Ok((font.cell_height, font.cell_width, unsafe {
|
||||
(*font.face.face).descender
|
||||
}))
|
||||
}
|
||||
let infos = buf.glyph_infos();
|
||||
let positions = buf.glyph_positions();
|
||||
|
||||
pub fn shape(&mut self, font_idx: usize, s: &str) -> Result<Vec<GlyphInfo>, Error> {
|
||||
/*
|
||||
debug!(
|
||||
"shape text for font_idx {} with len {} {}",
|
||||
font_idx,
|
||||
s.len(),
|
||||
s
|
||||
);
|
||||
*/
|
||||
let features = vec![
|
||||
// kerning
|
||||
harfbuzz::feature_from_string("kern")?,
|
||||
// ligatures
|
||||
harfbuzz::feature_from_string("liga")?,
|
||||
// contextual ligatures
|
||||
harfbuzz::feature_from_string("clig")?,
|
||||
];
|
||||
let mut cluster = Vec::new();
|
||||
|
||||
let mut buf = harfbuzz::Buffer::new()?;
|
||||
buf.set_script(harfbuzz::HB_SCRIPT_LATIN);
|
||||
buf.set_direction(harfbuzz::HB_DIRECTION_LTR);
|
||||
buf.set_language(harfbuzz::language_from_string("en")?);
|
||||
buf.add_str(s);
|
||||
let mut last_text_pos = None;
|
||||
let mut first_fallback_pos = None;
|
||||
|
||||
self.shape_with_font(font_idx, &mut buf, &features)?;
|
||||
let infos = buf.glyph_infos();
|
||||
let positions = buf.glyph_positions();
|
||||
|
||||
let mut cluster = Vec::new();
|
||||
|
||||
let mut last_text_pos = None;
|
||||
let mut first_fallback_pos = None;
|
||||
|
||||
// Compute the lengths of the text clusters.
|
||||
// Ligatures and combining characters mean
|
||||
// that a single glyph can take the place of
|
||||
// multiple characters. The 'cluster' member
|
||||
// of the glyph info is set to the position
|
||||
// in the input utf8 text, so we make a pass
|
||||
// over the set of clusters to look for differences
|
||||
// greater than 1 and backfill the length of
|
||||
// the corresponding text fragment. We need
|
||||
// the fragments to properly handle fallback,
|
||||
// and they're handy to have for debugging
|
||||
// purposes too.
|
||||
let mut sizes = Vec::with_capacity(s.len());
|
||||
for (i, info) in infos.iter().enumerate() {
|
||||
let pos = info.cluster as usize;
|
||||
let mut size = 1;
|
||||
if let Some(last_pos) = last_text_pos {
|
||||
let diff = pos - last_pos;
|
||||
if diff > 1 {
|
||||
sizes[i - 1] = diff;
|
||||
}
|
||||
} else if pos != 0 {
|
||||
size = pos;
|
||||
}
|
||||
last_text_pos = Some(pos);
|
||||
sizes.push(size);
|
||||
}
|
||||
// Compute the lengths of the text clusters.
|
||||
// Ligatures and combining characters mean
|
||||
// that a single glyph can take the place of
|
||||
// multiple characters. The 'cluster' member
|
||||
// of the glyph info is set to the position
|
||||
// in the input utf8 text, so we make a pass
|
||||
// over the set of clusters to look for differences
|
||||
// greater than 1 and backfill the length of
|
||||
// the corresponding text fragment. We need
|
||||
// the fragments to properly handle fallback,
|
||||
// and they're handy to have for debugging
|
||||
// purposes too.
|
||||
let mut sizes = Vec::with_capacity(s.len());
|
||||
for (i, info) in infos.iter().enumerate() {
|
||||
let pos = info.cluster as usize;
|
||||
let mut size = 1;
|
||||
if let Some(last_pos) = last_text_pos {
|
||||
let diff = s.len() - last_pos;
|
||||
let diff = pos - last_pos;
|
||||
if diff > 1 {
|
||||
let last = sizes.len() - 1;
|
||||
sizes[last] = diff;
|
||||
sizes[i - 1] = diff;
|
||||
}
|
||||
} else if pos != 0 {
|
||||
size = pos;
|
||||
}
|
||||
//debug!("sizes: {:?}", sizes);
|
||||
|
||||
// Now make a second pass to determine if we need
|
||||
// to perform fallback to a later font.
|
||||
// We can determine this by looking at the codepoint.
|
||||
for (i, info) in infos.iter().enumerate() {
|
||||
let pos = info.cluster as usize;
|
||||
if info.codepoint == 0 {
|
||||
if first_fallback_pos.is_none() {
|
||||
// Start of a run that needs fallback
|
||||
first_fallback_pos = Some(pos);
|
||||
}
|
||||
} else if let Some(start) = first_fallback_pos {
|
||||
// End of a fallback run
|
||||
//debug!("range: {:?}-{:?} needs fallback", start, pos);
|
||||
|
||||
let substr = &s[start..pos];
|
||||
let mut shape = self.shape(font_idx + 1, substr)?;
|
||||
|
||||
// Fixup the cluster member to match our current offset
|
||||
for info in shape.iter_mut() {
|
||||
info.cluster += start as u32;
|
||||
}
|
||||
cluster.append(&mut shape);
|
||||
|
||||
first_fallback_pos = None;
|
||||
}
|
||||
if info.codepoint != 0 {
|
||||
let text = &s[pos..pos + sizes[i]];
|
||||
//debug!("glyph from `{}`", text);
|
||||
cluster.push(GlyphInfo::new(text, font_idx, info, &positions[i]));
|
||||
}
|
||||
last_text_pos = Some(pos);
|
||||
sizes.push(size);
|
||||
}
|
||||
if let Some(last_pos) = last_text_pos {
|
||||
let diff = s.len() - last_pos;
|
||||
if diff > 1 {
|
||||
let last = sizes.len() - 1;
|
||||
sizes[last] = diff;
|
||||
}
|
||||
}
|
||||
//debug!("sizes: {:?}", sizes);
|
||||
|
||||
// Check to see if we started and didn't finish a
|
||||
// fallback run.
|
||||
if let Some(start) = first_fallback_pos {
|
||||
let substr = &s[start..];
|
||||
if false {
|
||||
debug!(
|
||||
"at end {:?}-{:?} needs fallback {}",
|
||||
start,
|
||||
s.len() - 1,
|
||||
substr,
|
||||
);
|
||||
// Now make a second pass to determine if we need
|
||||
// to perform fallback to a later font.
|
||||
// We can determine this by looking at the codepoint.
|
||||
for (i, info) in infos.iter().enumerate() {
|
||||
let pos = info.cluster as usize;
|
||||
if info.codepoint == 0 {
|
||||
if first_fallback_pos.is_none() {
|
||||
// Start of a run that needs fallback
|
||||
first_fallback_pos = Some(pos);
|
||||
}
|
||||
let mut shape = self.shape(font_idx + 1, substr)?;
|
||||
} else if let Some(start) = first_fallback_pos {
|
||||
// End of a fallback run
|
||||
//debug!("range: {:?}-{:?} needs fallback", start, pos);
|
||||
|
||||
let substr = &s[start..pos];
|
||||
let mut shape = shape_with_harfbuzz(font, font_idx + 1, substr)?;
|
||||
|
||||
// Fixup the cluster member to match our current offset
|
||||
for info in shape.iter_mut() {
|
||||
info.cluster += start as u32;
|
||||
}
|
||||
cluster.append(&mut shape);
|
||||
|
||||
first_fallback_pos = None;
|
||||
}
|
||||
if info.codepoint != 0 {
|
||||
let text = &s[pos..pos + sizes[i]];
|
||||
//debug!("glyph from `{}`", text);
|
||||
cluster.push(GlyphInfo::new(text, font_idx, info, &positions[i]));
|
||||
}
|
||||
|
||||
//debug!("shaped: {:#?}", cluster);
|
||||
|
||||
Ok(cluster)
|
||||
}
|
||||
|
||||
fn shape_with_font(
|
||||
&mut self,
|
||||
idx: usize,
|
||||
buf: &mut harfbuzz::Buffer,
|
||||
features: &Vec<harfbuzz::hb_feature_t>,
|
||||
) -> Result<(), Error> {
|
||||
let info = self.get_font(idx)?;
|
||||
info.font.shape(buf, Some(features.as_slice()));
|
||||
Ok(())
|
||||
// Check to see if we started and didn't finish a
|
||||
// fallback run.
|
||||
if let Some(start) = first_fallback_pos {
|
||||
let substr = &s[start..];
|
||||
if false {
|
||||
debug!(
|
||||
"at end {:?}-{:?} needs fallback {}",
|
||||
start,
|
||||
s.len() - 1,
|
||||
substr,
|
||||
);
|
||||
}
|
||||
let mut shape = shape_with_harfbuzz(font, font_idx + 1, substr)?;
|
||||
// Fixup the cluster member to match our current offset
|
||||
for info in shape.iter_mut() {
|
||||
info.cluster += start as u32;
|
||||
}
|
||||
cluster.append(&mut shape);
|
||||
}
|
||||
|
||||
pub fn load_glyph(
|
||||
&mut self,
|
||||
font_idx: usize,
|
||||
glyph_pos: u32,
|
||||
) -> Result<&ftwrap::FT_GlyphSlotRec_, Error> {
|
||||
let info = &mut self.fonts[font_idx];
|
||||
//debug!("shaped: {:#?}", cluster);
|
||||
|
||||
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;
|
||||
Ok(cluster)
|
||||
|
||||
// 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;
|
||||
|
||||
info.font.set_load_flags(load_flags);
|
||||
info.face.load_and_render_glyph(
|
||||
glyph_pos,
|
||||
load_flags,
|
||||
render_mode,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
113
src/font/system.rs
Normal file
113
src/font/system.rs
Normal file
@ -0,0 +1,113 @@
|
||||
//! Abstracts over the font selection system for the system
|
||||
|
||||
use super::super::config::{Config, TextStyle};
|
||||
use failure::Error;
|
||||
use harfbuzz;
|
||||
use unicode_width::UnicodeWidthStr;
|
||||
|
||||
/// Holds information about a shaped glyph
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct GlyphInfo {
|
||||
/// We only retain text in debug mode for diagnostic purposes
|
||||
#[cfg(debug_assertions)]
|
||||
pub text: String,
|
||||
/// Offset within text
|
||||
pub cluster: u32,
|
||||
/// How many cells/columns this glyph occupies horizontally
|
||||
pub num_cells: u8,
|
||||
/// Which font alternative to use; index into Font.fonts
|
||||
pub font_idx: usize,
|
||||
/// Which freetype glyph to load
|
||||
pub glyph_pos: u32,
|
||||
/// How far to advance the render cursor after drawing this glyph
|
||||
pub x_advance: f64,
|
||||
/// How far to advance the render cursor after drawing this glyph
|
||||
pub y_advance: f64,
|
||||
/// Destination render offset
|
||||
pub x_offset: f64,
|
||||
/// Destination render offset
|
||||
pub y_offset: f64,
|
||||
}
|
||||
|
||||
impl GlyphInfo {
|
||||
pub fn new(
|
||||
text: &str,
|
||||
font_idx: usize,
|
||||
info: &harfbuzz::hb_glyph_info_t,
|
||||
pos: &harfbuzz::hb_glyph_position_t,
|
||||
) -> GlyphInfo {
|
||||
let num_cells = UnicodeWidthStr::width(text) as u8;
|
||||
GlyphInfo {
|
||||
#[cfg(debug_assertions)]
|
||||
text: text.into(),
|
||||
num_cells,
|
||||
font_idx,
|
||||
glyph_pos: info.codepoint,
|
||||
cluster: info.cluster,
|
||||
x_advance: pos.x_advance as f64 / 64.0,
|
||||
y_advance: pos.y_advance as f64 / 64.0,
|
||||
x_offset: pos.x_offset as f64 / 64.0,
|
||||
y_offset: pos.y_offset as f64 / 64.0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Represents a numbered index in the fallback sequence for a NamedFont.
|
||||
/// 0 is the first, best match. If a glyph isn't present then we will
|
||||
/// want to search for a fallback in later indices.
|
||||
pub type FallbackIdx = usize;
|
||||
|
||||
/// Represents a named, user-selected font.
|
||||
/// This is really a set of fallback fonts indexed by FallbackIdx with
|
||||
/// zero as the best/most preferred font.
|
||||
pub trait NamedFont {
|
||||
/// Get a reference to a numbered fallback Font instance
|
||||
fn get_fallback(&mut self, idx: FallbackIdx) -> Result<&Font, Error>;
|
||||
|
||||
/// Shape text and return a vector of GlyphInfo
|
||||
fn shape(&mut self, text: &str) -> Result<Vec<GlyphInfo>, Error>;
|
||||
}
|
||||
|
||||
/// FontSystem is a handle to the system font selection system
|
||||
pub trait FontSystem {
|
||||
/// Given a text style, load (without caching) the font that
|
||||
/// best matches according to the fontconfig pattern.
|
||||
fn load_font(&self, config: &Config, style: &TextStyle) -> Result<Box<NamedFont>, Error>;
|
||||
}
|
||||
|
||||
/// Describes the key font metrics that we use in rendering
|
||||
pub struct FontMetrics {
|
||||
/// Width of a character cell in pixels
|
||||
pub cell_width: f64,
|
||||
/// Height of a character cell in pixels
|
||||
pub cell_height: f64,
|
||||
/// Added to the bottom y coord to find the baseline.
|
||||
/// descender is typically negative.
|
||||
pub descender: i16,
|
||||
}
|
||||
|
||||
use super::ftwrap;
|
||||
|
||||
/// Represents a concrete instance of a font.
|
||||
pub trait Font {
|
||||
/// Returns true if the font rasterizes with true color glyphs,
|
||||
/// or false if it produces gray scale glyphs that need to be
|
||||
/// colorized.
|
||||
fn has_color(&self) -> bool;
|
||||
|
||||
/// Returns the font metrics
|
||||
fn metrics(&self) -> FontMetrics;
|
||||
|
||||
/// FIXME: This is a temporary hack and will be replaced
|
||||
/// with a rasterize method.
|
||||
fn load_glyph(&self, glyph_pos: u32) -> Result<ftwrap::FT_GlyphSlotRec_, Error>;
|
||||
|
||||
/// Perform shaping on the supplied harfbuzz buffer.
|
||||
/// This is really just a proxy for calling the harfbuzz::Font::shape()
|
||||
/// method on the contained harfbuzz font instance.
|
||||
fn harfbuzz_shape(
|
||||
&self,
|
||||
buf: &mut harfbuzz::Buffer,
|
||||
features: Option<&[harfbuzz::hb_feature_t]>,
|
||||
);
|
||||
}
|
@ -92,12 +92,12 @@ fn run() -> Result<(), Error> {
|
||||
// we always load the cell_height for font 0,
|
||||
// regardless of which font we are shaping here,
|
||||
// so that we can scale glyphs appropriately
|
||||
let (cell_height, cell_width, _) = font.borrow_mut().get_metrics()?;
|
||||
let metrics = font.borrow_mut().get_fallback(0)?.metrics();
|
||||
|
||||
let initial_cols = 80u16;
|
||||
let initial_rows = 24u16;
|
||||
let initial_pixel_width = initial_cols * cell_width.ceil() as u16;
|
||||
let initial_pixel_height = initial_rows * cell_height.ceil() as u16;
|
||||
let initial_pixel_width = initial_cols * metrics.cell_width.ceil() as u16;
|
||||
let initial_pixel_height = initial_rows * metrics.cell_height.ceil() as u16;
|
||||
|
||||
let (master, slave) = pty::openpty(
|
||||
initial_rows,
|
||||
|
@ -273,8 +273,8 @@ impl Renderer {
|
||||
let (cell_height, cell_width, descender) = {
|
||||
// Urgh, this is a bit repeaty, but we need to satisfy the borrow checker
|
||||
let font = fonts.default_font()?;
|
||||
let tuple = font.borrow_mut().get_metrics()?;
|
||||
tuple
|
||||
let metrics = font.borrow_mut().get_fallback(0)?.metrics();
|
||||
(metrics.cell_height, metrics.cell_width, metrics.descender)
|
||||
};
|
||||
let descender = if descender.is_positive() {
|
||||
((descender as f64) / 64.0).ceil() as isize
|
||||
@ -456,13 +456,11 @@ impl Renderer {
|
||||
let (has_color, ft_glyph, cell_width, cell_height) = {
|
||||
let font = self.fonts.cached_font(style)?;
|
||||
let mut font = font.borrow_mut();
|
||||
let (height, width, _) = font.get_metrics()?;
|
||||
let has_color = font.has_color(info.font_idx)?;
|
||||
// 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.
|
||||
let ft_glyph = font.load_glyph(info.font_idx, info.glyph_pos)?.clone();
|
||||
(has_color, ft_glyph, width, height)
|
||||
let metrics = font.get_fallback(0)?.metrics();
|
||||
let active_font = font.get_fallback(info.font_idx)?;
|
||||
let has_color = active_font.has_color();
|
||||
let ft_glyph = active_font.load_glyph(info.glyph_pos)?;
|
||||
(has_color, ft_glyph, metrics.cell_width, metrics.cell_height)
|
||||
};
|
||||
|
||||
let scale = if (info.x_advance / info.num_cells as f64).floor() > cell_width {
|
||||
@ -729,7 +727,7 @@ impl Renderer {
|
||||
fn shape_text(&self, s: &str, style: &TextStyle) -> Result<Vec<GlyphInfo>, Error> {
|
||||
let font = self.fonts.cached_font(style)?;
|
||||
let mut font = font.borrow_mut();
|
||||
font.shape(0, s)
|
||||
font.shape(s)
|
||||
}
|
||||
|
||||
/// "Render" a line of the terminal screen into the vertex buffer.
|
||||
|
@ -152,11 +152,11 @@ impl<'a> TerminalWindow<'a> {
|
||||
fonts: FontConfiguration,
|
||||
palette: term::color::ColorPalette,
|
||||
) -> Result<TerminalWindow, Error> {
|
||||
let (cell_height, cell_width, _) = {
|
||||
let (cell_height, cell_width) = {
|
||||
// Urgh, this is a bit repeaty, but we need to satisfy the borrow checker
|
||||
let font = fonts.default_font()?;
|
||||
let tuple = font.borrow_mut().get_metrics()?;
|
||||
tuple
|
||||
let metrics = font.borrow_mut().get_fallback(0)?.metrics();
|
||||
(metrics.cell_height, metrics.cell_width)
|
||||
};
|
||||
|
||||
let window = Window::new(&conn, width, height)?;
|
||||
|
Loading…
Reference in New Issue
Block a user