diff options
Diffstat (limited to 'font')
-rw-r--r-- | font/Cargo.toml | 6 | ||||
-rw-r--r-- | font/src/darwin/mod.rs | 2 | ||||
-rw-r--r-- | font/src/ft/mod.rs | 2 | ||||
-rw-r--r-- | font/src/lib.rs | 41 | ||||
-rw-r--r-- | font/src/rusttype/mod.rs | 151 |
5 files changed, 182 insertions, 20 deletions
diff --git a/font/Cargo.toml b/font/Cargo.toml index a0c524b8..307153f6 100644 --- a/font/Cargo.toml +++ b/font/Cargo.toml @@ -11,7 +11,7 @@ libc = "0.2" foreign-types = "0.3" log = "0.4" -[target.'cfg(not(target_os = "macos"))'.dependencies] +[target.'cfg(not(any(target_os = "macos", windows)))'.dependencies] servo-fontconfig = "0.4.0" freetype-rs = "0.19" @@ -20,3 +20,7 @@ core-foundation = "0.6" core-text = "13" core-graphics = "0.17" core-foundation-sys = "0.6" + +[target.'cfg(windows)'.dependencies] +font-loader = "0.6.0" +rusttype = "0.4.1" diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs index c688c53d..bdcbbdfa 100644 --- a/font/src/darwin/mod.rs +++ b/font/src/darwin/mod.rs @@ -139,7 +139,7 @@ impl ::Rasterize for Rasterizer { } /// Get metrics for font specified by FontKey - fn metrics(&self, key: FontKey) -> Result<Metrics, Error> { + fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> { let font = self.fonts .get(&key) .ok_or(Error::FontNotLoaded)?; diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs index 51c304f2..55409174 100644 --- a/font/src/ft/mod.rs +++ b/font/src/ft/mod.rs @@ -85,7 +85,7 @@ impl ::Rasterize for FreeTypeRasterizer { }) } - fn metrics(&self, key: FontKey) -> Result<Metrics, Error> { + fn metrics(&self, key: FontKey, _size: Size) -> Result<Metrics, Error> { let full = self.full_metrics(key)?; let height = (full.size_metrics.height / 64) as f64; diff --git a/font/src/lib.rs b/font/src/lib.rs index fe9e9e85..5fb50217 100644 --- a/font/src/lib.rs +++ b/font/src/lib.rs @@ -20,40 +20,45 @@ #![cfg_attr(feature = "cargo-clippy", deny(clippy, if_not_else, enum_glob_use, wrong_pub_self_convention))] -#[cfg(not(target_os = "macos"))] +#[cfg(not(any(target_os = "macos", windows)))] extern crate fontconfig; -#[cfg(not(target_os = "macos"))] +#[cfg(not(any(target_os = "macos", windows)))] extern crate freetype; #[cfg(target_os = "macos")] -extern crate core_text; -#[cfg(target_os = "macos")] extern crate core_foundation; #[cfg(target_os = "macos")] extern crate core_foundation_sys; #[cfg(target_os = "macos")] extern crate core_graphics; #[cfg(target_os = "macos")] +extern crate core_text; +#[cfg(target_os = "macos")] extern crate euclid; extern crate libc; -#[cfg(not(target_os = "macos"))] +#[cfg(not(any(target_os = "macos", windows)))] #[macro_use] extern crate foreign_types; -#[macro_use] +#[cfg_attr(not(windows), macro_use)] extern crate log; use std::hash::{Hash, Hasher}; use std::{fmt, cmp}; use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; -// If target isn't macos, reexport everything from ft -#[cfg(not(target_os = "macos"))] +// If target isn't macos or windows, reexport everything from ft +#[cfg(not(any(target_os = "macos", windows)))] pub mod ft; -#[cfg(not(target_os = "macos"))] -pub use ft::{FreeTypeRasterizer as Rasterizer, Error}; +#[cfg(not(any(target_os = "macos", windows)))] +pub use ft::{Error, FreeTypeRasterizer as Rasterizer}; + +#[cfg(windows)] +pub mod rusttype; +#[cfg(windows)] +pub use rusttype::{Error, RustTypeRasterizer as Rasterizer}; // If target is macos, reexport everything from darwin #[cfg(target_os = "macos")] @@ -92,14 +97,14 @@ pub enum Slant { #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum Weight { Normal, - Bold + Bold, } /// Style of font #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum Style { Specific(String), - Description { slant: Slant, weight: Weight } + Description { slant: Slant, weight: Weight }, } impl fmt::Display for Style { @@ -108,14 +113,15 @@ impl fmt::Display for Style { Style::Specific(ref s) => f.write_str(&s), Style::Description { slant, weight } => { write!(f, "slant={:?}, weight={:?}", slant, weight) - }, + } } } } impl FontDesc { pub fn new<S>(name: S, style: Style) -> FontDesc - where S: Into<String> + where + S: Into<String>, { FontDesc { name: name.into(), @@ -336,12 +342,13 @@ pub trait Rasterize { /// Errors occurring in Rasterize methods type Err: ::std::error::Error + Send + Sync + 'static; - /// Create a new Rasterize + /// Create a new Rasterizer fn new(device_pixel_ratio: f32, use_thin_strokes: bool) -> Result<Self, Self::Err> - where Self: Sized; + where + Self: Sized; /// Get `Metrics` for the given `FontKey` - fn metrics(&self, FontKey) -> Result<Metrics, Self::Err>; + fn metrics(&self, FontKey, Size) -> Result<Metrics, Self::Err>; /// Load the font described by `FontDesc` and `Size` fn load_font(&mut self, &FontDesc, Size) -> Result<FontKey, Self::Err>; diff --git a/font/src/rusttype/mod.rs b/font/src/rusttype/mod.rs new file mode 100644 index 00000000..dc64bc78 --- /dev/null +++ b/font/src/rusttype/mod.rs @@ -0,0 +1,151 @@ +extern crate font_loader; +use self::font_loader::system_fonts; + +extern crate rusttype; +use self::rusttype::{point, Codepoint, FontCollection, Scale}; + +use super::{FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight}; + +pub struct RustTypeRasterizer { + fonts: Vec<rusttype::Font<'static>>, + dpi_ratio: f32, +} + +impl ::Rasterize for RustTypeRasterizer { + type Err = Error; + + fn new(device_pixel_ratio: f32, _: bool) -> Result<RustTypeRasterizer, Error> { + Ok(RustTypeRasterizer { + fonts: Vec::new(), + dpi_ratio: device_pixel_ratio, + }) + } + + fn metrics(&self, key: FontKey, size: Size) -> Result<Metrics, Error> { + let scale = Scale::uniform(size.as_f32_pts() * self.dpi_ratio * 96. / 72.); + let vmetrics = self.fonts[key.token as usize].v_metrics(scale); + let hmetrics = self.fonts[key.token as usize] + .glyph( + // If the font is monospaced all glyphs *should* have the same width + // 33 '!' is the first displaying character + Codepoint(33), + ) + .ok_or(Error::MissingGlyph)? + .scaled(scale) + .h_metrics(); + Ok(Metrics { + descent: vmetrics.descent, + average_advance: f64::from(hmetrics.advance_width), + line_height: f64::from(vmetrics.ascent - vmetrics.descent + vmetrics.line_gap), + }) + } + + fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> { + let fp = system_fonts::FontPropertyBuilder::new() + .family(&desc.name) + .monospace(); + + let fp = match desc.style { + Style::Specific(_) => unimplemented!(""), + Style::Description { slant, weight } => { + let fp = match slant { + Slant::Normal => fp, + Slant::Italic => fp.italic(), + // This style is not supported by rust-font-loader + Slant::Oblique => return Err(Error::UnsupportedStyle), + }; + match weight { + Weight::Bold => fp.bold(), + Weight::Normal => fp, + } + } + }; + self.fonts.push(FontCollection::from_bytes( + system_fonts::get(&fp.build()) + .ok_or_else(|| Error::MissingFont(desc.clone()))? + .0, + ).into_font() + .ok_or(Error::UnsupportedFont)?); + Ok(FontKey { + token: (self.fonts.len() - 1) as u16, + }) + } + + fn get_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> { + let scaled_glyph = self.fonts[glyph_key.font_key.token as usize] + .glyph(glyph_key.c) + .ok_or(Error::MissingGlyph)? + .scaled(Scale::uniform( + glyph_key.size.as_f32_pts() * self.dpi_ratio * 96. / 72., + )); + + let glyph = scaled_glyph.positioned(point(0.0, 0.0)); + + // Pixel bounding box + let bb = match glyph.pixel_bounding_box() { + Some(bb) => bb, + // Bounding box calculation fails for spaces so we provide a placeholder bounding box + None => rusttype::Rect { + min: point(0, 0), + max: point(0, 0), + }, + }; + + let mut buf = Vec::with_capacity((bb.width() * bb.height()) as usize); + + glyph.draw(|_x, _y, v| { + buf.push((v * 255.0) as u8); + buf.push((v * 255.0) as u8); + buf.push((v * 255.0) as u8); + }); + Ok(RasterizedGlyph { + c: glyph_key.c, + width: bb.width(), + height: bb.height(), + top: -bb.min.y, + left: bb.min.x, + buf, + }) + } +} + +#[derive(Debug)] +pub enum Error { + MissingFont(FontDesc), + UnsupportedFont, + UnsupportedStyle, + // NOTE: This error is different from how the FreeType code handles it + MissingGlyph, +} + +impl ::std::error::Error for Error { + fn description(&self) -> &str { + match *self { + Error::MissingFont(ref _desc) => "couldn't find the requested font", + Error::UnsupportedFont => "only TrueType fonts are supported", + Error::UnsupportedStyle => "the selected style is not supported by rusttype", + Error::MissingGlyph => "the selected font did not have the requested glyph", + } + } +} + +impl ::std::fmt::Display for Error { + fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { + match *self { + Error::MissingFont(ref desc) => write!( + f, + "Couldn't find a font with {}\ + \n\tPlease check the font config in your alacritty.yml.", + desc + ), + Error::UnsupportedFont => write!( + f, + "Rusttype only supports TrueType fonts.\n\tPlease select a TrueType font instead." + ), + Error::UnsupportedStyle => { + write!(f, "The selected font style is not supported by rusttype.") + } + Error::MissingGlyph => write!(f, "The selected font did not have the requested glyph."), + } + } +} |