2019-12-10 00:56:34 +03:00
|
|
|
use failure::{format_err, Error, Fallible};
|
2018-02-23 08:07:17 +03:00
|
|
|
mod hbwrap;
|
2018-02-21 01:32:28 +03:00
|
|
|
|
2018-02-07 20:23:24 +03:00
|
|
|
use std::cell::RefCell;
|
|
|
|
use std::collections::HashMap;
|
|
|
|
use std::rc::Rc;
|
2018-02-21 07:20:37 +03:00
|
|
|
|
2019-12-10 00:56:34 +03:00
|
|
|
pub mod ftwrap;
|
2019-12-10 07:06:12 +03:00
|
|
|
pub mod locator;
|
2019-12-10 05:08:02 +03:00
|
|
|
pub mod parser;
|
2019-12-09 10:27:10 +03:00
|
|
|
pub mod rasterizer;
|
2019-12-09 11:06:47 +03:00
|
|
|
pub mod shaper;
|
2019-12-10 20:01:55 +03:00
|
|
|
pub mod units;
|
2019-12-09 09:48:47 +03:00
|
|
|
|
2019-12-10 07:26:58 +03:00
|
|
|
#[cfg(all(unix, not(target_os = "macos")))]
|
2018-02-21 07:55:03 +03:00
|
|
|
pub mod fcwrap;
|
2018-01-17 10:32:23 +03:00
|
|
|
|
2019-12-15 02:36:50 +03:00
|
|
|
use crate::font::locator::{FontDataHandle, FontLocator, FontLocatorSelection};
|
2019-12-10 00:56:34 +03:00
|
|
|
pub use crate::font::rasterizer::RasterizedGlyph;
|
2019-12-09 23:46:58 +03:00
|
|
|
use crate::font::rasterizer::{FontRasterizer, FontRasterizerSelection};
|
2019-12-10 00:56:34 +03:00
|
|
|
pub use crate::font::shaper::{FallbackIdx, FontMetrics, GlyphInfo};
|
2019-12-09 23:46:58 +03:00
|
|
|
use crate::font::shaper::{FontShaper, FontShaperSelection};
|
2019-12-09 23:12:54 +03:00
|
|
|
|
2019-11-24 20:19:14 +03:00
|
|
|
use super::config::{configuration, ConfigHandle, TextStyle};
|
2018-02-07 20:23:24 +03:00
|
|
|
use term::CellAttributes;
|
|
|
|
|
2019-12-09 23:12:54 +03:00
|
|
|
pub struct LoadedFont {
|
2019-12-15 02:36:50 +03:00
|
|
|
rasterizers: Vec<RefCell<Option<Box<dyn FontRasterizer>>>>,
|
|
|
|
handles: Vec<FontDataHandle>,
|
2019-12-09 23:46:58 +03:00
|
|
|
shaper: Box<dyn FontShaper>,
|
2019-12-10 00:17:40 +03:00
|
|
|
metrics: FontMetrics,
|
|
|
|
font_size: f64,
|
|
|
|
dpi: u32,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl LoadedFont {
|
|
|
|
pub fn metrics(&self) -> FontMetrics {
|
|
|
|
self.metrics
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn shape(&self, text: &str) -> Fallible<Vec<GlyphInfo>> {
|
|
|
|
self.shaper.shape(text, self.font_size, self.dpi)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn rasterize_glyph(
|
|
|
|
&self,
|
|
|
|
glyph_pos: u32,
|
|
|
|
fallback: FallbackIdx,
|
|
|
|
) -> Fallible<RasterizedGlyph> {
|
2019-12-15 02:36:50 +03:00
|
|
|
let cell = self
|
2019-12-10 00:17:40 +03:00
|
|
|
.rasterizers
|
|
|
|
.get(fallback)
|
|
|
|
.ok_or_else(|| format_err!("no such fallback index: {}", fallback))?;
|
2019-12-15 02:36:50 +03:00
|
|
|
let mut opt_raster = cell.borrow_mut();
|
|
|
|
if opt_raster.is_none() {
|
|
|
|
let raster =
|
|
|
|
FontRasterizerSelection::get_default().new_rasterizer(&self.handles[fallback])?;
|
|
|
|
opt_raster.replace(raster);
|
|
|
|
}
|
|
|
|
|
|
|
|
opt_raster
|
|
|
|
.as_ref()
|
|
|
|
.unwrap()
|
|
|
|
.rasterize_glyph(glyph_pos, self.font_size, self.dpi)
|
2019-12-10 00:17:40 +03:00
|
|
|
}
|
2019-12-09 23:12:54 +03:00
|
|
|
}
|
|
|
|
|
2018-02-07 20:23:24 +03:00
|
|
|
/// Matches and loads fonts for a given input style
|
|
|
|
pub struct FontConfiguration {
|
2019-12-10 00:24:09 +03:00
|
|
|
fonts: RefCell<HashMap<TextStyle, Rc<LoadedFont>>>,
|
2019-02-23 00:34:48 +03:00
|
|
|
metrics: RefCell<Option<FontMetrics>>,
|
2019-02-23 01:52:35 +03:00
|
|
|
dpi_scale: RefCell<f64>,
|
|
|
|
font_scale: RefCell<f64>,
|
2019-11-24 21:01:23 +03:00
|
|
|
config_generation: RefCell<usize>,
|
2019-12-10 07:06:12 +03:00
|
|
|
locator: Box<dyn FontLocator>,
|
2019-02-19 09:02:31 +03:00
|
|
|
}
|
|
|
|
|
2018-02-07 20:23:24 +03:00
|
|
|
impl FontConfiguration {
|
|
|
|
/// Create a new empty configuration
|
2019-12-10 00:35:38 +03:00
|
|
|
pub fn new() -> Self {
|
2019-12-10 07:06:12 +03:00
|
|
|
let locator = FontLocatorSelection::get_default().new_locator();
|
2018-02-07 20:23:24 +03:00
|
|
|
Self {
|
|
|
|
fonts: RefCell::new(HashMap::new()),
|
2019-12-10 07:06:12 +03:00
|
|
|
locator,
|
2019-02-23 00:34:48 +03:00
|
|
|
metrics: RefCell::new(None),
|
2019-02-23 01:52:35 +03:00
|
|
|
font_scale: RefCell::new(1.0),
|
|
|
|
dpi_scale: RefCell::new(1.0),
|
2019-11-24 21:01:23 +03:00
|
|
|
config_generation: RefCell::new(configuration().generation()),
|
2018-02-07 20:23:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-12-10 00:24:09 +03:00
|
|
|
/// Given a text style, load (with caching) the font that best
|
|
|
|
/// matches according to the fontconfig pattern.
|
2019-12-09 23:46:58 +03:00
|
|
|
pub fn resolve_font(&self, style: &TextStyle) -> Fallible<Rc<LoadedFont>> {
|
2019-12-10 00:24:09 +03:00
|
|
|
let mut fonts = self.fonts.borrow_mut();
|
|
|
|
|
|
|
|
let config = configuration();
|
|
|
|
let current_generation = config.generation();
|
|
|
|
if current_generation != *self.config_generation.borrow() {
|
|
|
|
// Config was reloaded, invalidate our caches
|
|
|
|
fonts.clear();
|
|
|
|
self.metrics.borrow_mut().take();
|
|
|
|
*self.config_generation.borrow_mut() = current_generation;
|
|
|
|
}
|
|
|
|
|
2019-12-09 23:46:58 +03:00
|
|
|
if let Some(entry) = fonts.get(style) {
|
|
|
|
return Ok(Rc::clone(entry));
|
|
|
|
}
|
|
|
|
|
|
|
|
let attributes = style.font_with_fallback();
|
2019-12-10 08:55:36 +03:00
|
|
|
let mut handles = parser::ParsedFont::load_fonts(&config, &attributes)?;
|
|
|
|
handles.append(&mut self.locator.load_fonts(&attributes)?);
|
2019-12-09 23:46:58 +03:00
|
|
|
let mut rasterizers = vec![];
|
2019-12-15 02:36:50 +03:00
|
|
|
for _ in &handles {
|
|
|
|
rasterizers.push(RefCell::new(None));
|
2019-12-09 23:46:58 +03:00
|
|
|
}
|
|
|
|
let shaper = FontShaperSelection::get_default().new_shaper(&handles)?;
|
|
|
|
|
2019-12-10 00:17:40 +03:00
|
|
|
let config = configuration();
|
|
|
|
let font_size = config.font_size * *self.font_scale.borrow();
|
2019-12-10 09:15:58 +03:00
|
|
|
let dpi = *self.dpi_scale.borrow() as u32 * config.dpi as u32;
|
2019-12-10 00:17:40 +03:00
|
|
|
let metrics = shaper.metrics(font_size, dpi)?;
|
|
|
|
|
2019-12-09 23:46:58 +03:00
|
|
|
let loaded = Rc::new(LoadedFont {
|
|
|
|
rasterizers,
|
2019-12-15 02:36:50 +03:00
|
|
|
handles,
|
2019-12-09 23:46:58 +03:00
|
|
|
shaper,
|
2019-12-10 00:17:40 +03:00
|
|
|
metrics,
|
|
|
|
font_size,
|
|
|
|
dpi,
|
2019-12-09 23:46:58 +03:00
|
|
|
});
|
|
|
|
|
|
|
|
fonts.insert(style.clone(), Rc::clone(&loaded));
|
|
|
|
|
|
|
|
Ok(loaded)
|
|
|
|
}
|
|
|
|
|
2019-02-23 01:52:35 +03:00
|
|
|
pub fn change_scaling(&self, font_scale: f64, dpi_scale: f64) {
|
|
|
|
*self.dpi_scale.borrow_mut() = dpi_scale;
|
|
|
|
*self.font_scale.borrow_mut() = font_scale;
|
|
|
|
self.fonts.borrow_mut().clear();
|
|
|
|
self.metrics.borrow_mut().take();
|
|
|
|
}
|
|
|
|
|
2018-02-07 20:23:24 +03:00
|
|
|
/// Returns the baseline font specified in the configuration
|
2019-12-10 00:24:09 +03:00
|
|
|
pub fn default_font(&self) -> Fallible<Rc<LoadedFont>> {
|
|
|
|
self.resolve_font(&configuration().font)
|
2018-02-07 20:23:24 +03:00
|
|
|
}
|
|
|
|
|
2019-02-23 01:52:35 +03:00
|
|
|
pub fn get_font_scale(&self) -> f64 {
|
|
|
|
*self.font_scale.borrow()
|
|
|
|
}
|
|
|
|
|
2019-02-23 00:34:48 +03:00
|
|
|
pub fn default_font_metrics(&self) -> Result<FontMetrics, Error> {
|
|
|
|
{
|
|
|
|
let metrics = self.metrics.borrow();
|
|
|
|
if let Some(metrics) = metrics.as_ref() {
|
|
|
|
return Ok(*metrics);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let font = self.default_font()?;
|
2019-12-10 00:24:09 +03:00
|
|
|
let metrics = font.metrics();
|
2019-02-23 00:34:48 +03:00
|
|
|
|
2019-02-24 21:33:21 +03:00
|
|
|
*self.metrics.borrow_mut() = Some(metrics);
|
2019-02-23 00:34:48 +03:00
|
|
|
|
|
|
|
Ok(metrics)
|
|
|
|
}
|
|
|
|
|
2018-02-07 20:23:24 +03:00
|
|
|
/// Apply the defined font_rules from the user configuration to
|
|
|
|
/// produce the text style that best matches the supplied input
|
|
|
|
/// cell attributes.
|
2019-11-24 19:55:55 +03:00
|
|
|
pub fn match_style<'a>(
|
|
|
|
&self,
|
2019-11-24 20:19:14 +03:00
|
|
|
config: &'a ConfigHandle,
|
2019-11-24 19:55:55 +03:00
|
|
|
attrs: &CellAttributes,
|
|
|
|
) -> &'a TextStyle {
|
2018-02-07 20:23:24 +03:00
|
|
|
// a little macro to avoid boilerplate for matching the rules.
|
|
|
|
// If the rule doesn't specify a value for an attribute then
|
|
|
|
// it will implicitly match. If it specifies an attribute
|
|
|
|
// then it has to have the same value as that in the input attrs.
|
|
|
|
macro_rules! attr_match {
|
|
|
|
($ident:ident, $rule:expr) => {
|
|
|
|
if let Some($ident) = $rule.$ident {
|
|
|
|
if $ident != attrs.$ident() {
|
|
|
|
// Does not match
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// matches so far...
|
2018-04-11 23:15:12 +03:00
|
|
|
};
|
2018-02-07 20:23:24 +03:00
|
|
|
};
|
|
|
|
|
2019-11-24 19:55:55 +03:00
|
|
|
for rule in &config.font_rules {
|
2018-02-07 20:23:24 +03:00
|
|
|
attr_match!(intensity, &rule);
|
|
|
|
attr_match!(underline, &rule);
|
|
|
|
attr_match!(italic, &rule);
|
|
|
|
attr_match!(blink, &rule);
|
|
|
|
attr_match!(reverse, &rule);
|
|
|
|
attr_match!(strikethrough, &rule);
|
|
|
|
attr_match!(invisible, &rule);
|
|
|
|
|
|
|
|
// If we get here, then none of the rules didn't match,
|
|
|
|
// so we therefore assume that it did match overall.
|
|
|
|
return &rule.font;
|
|
|
|
}
|
2019-11-24 19:55:55 +03:00
|
|
|
&config.font
|
2018-02-07 20:23:24 +03:00
|
|
|
}
|
|
|
|
}
|