From 7a4b4a6a24e3894051fd31da63ee6baca16c70ce Mon Sep 17 00:00:00 2001 From: Christian Duerr Date: Sat, 18 Jul 2020 01:27:41 +0000 Subject: [PATCH] Rename font crate to crossfont --- Cargo.lock | 42 +- Cargo.toml | 1 - alacritty/Cargo.toml | 2 +- alacritty/src/config/font.rs | 2 +- alacritty/src/cursor.rs | 4 +- alacritty/src/display.rs | 20 +- alacritty/src/event.rs | 4 +- alacritty/src/renderer/mod.rs | 34 +- alacritty/src/renderer/rects.rs | 2 +- alacritty/src/url.rs | 3 +- alacritty/src/window.rs | 6 +- font/Cargo.toml | 31 -- font/src/darwin/byte_order.rs | 57 --- font/src/darwin/mod.rs | 634 ------------------------- font/src/directwrite/mod.rs | 335 -------------- font/src/ft/fc/char_set.rs | 70 --- font/src/ft/fc/config.rs | 29 -- font/src/ft/fc/font_set.rs | 87 ---- font/src/ft/fc/mod.rs | 337 -------------- font/src/ft/fc/object_set.rs | 48 -- font/src/ft/fc/pattern.rs | 627 ------------------------- font/src/ft/mod.rs | 787 -------------------------------- font/src/lib.rs | 226 --------- 23 files changed, 58 insertions(+), 3330 deletions(-) delete mode 100644 font/Cargo.toml delete mode 100644 font/src/darwin/byte_order.rs delete mode 100644 font/src/darwin/mod.rs delete mode 100644 font/src/directwrite/mod.rs delete mode 100644 font/src/ft/fc/char_set.rs delete mode 100644 font/src/ft/fc/config.rs delete mode 100644 font/src/ft/fc/font_set.rs delete mode 100644 font/src/ft/fc/mod.rs delete mode 100644 font/src/ft/fc/object_set.rs delete mode 100644 font/src/ft/fc/pattern.rs delete mode 100644 font/src/ft/mod.rs delete mode 100644 font/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 663cf8ef..aeb4f68a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -25,10 +25,10 @@ dependencies = [ "alacritty_terminal 0.5.0-dev", "clap 2.33.1 (registry+https://github.com/rust-lang/crates.io-index)", "copypasta 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crossfont 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "embed-resource 1.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)", - "font 0.1.0", "gl_generator 0.14.0 (registry+https://github.com/rust-lang/crates.io-index)", "glutin 0.24.1 (registry+https://github.com/rust-lang/crates.io-index)", "image 0.23.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -413,6 +413,26 @@ dependencies = [ "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crossfont" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "cocoa 0.20.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "core-graphics 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)", + "core-text 15.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "dwrote 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", + "euclid 0.20.14 (registry+https://github.com/rust-lang/crates.io-index)", + "foreign-types 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", + "freetype-rs 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.72 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", + "servo-fontconfig 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "deflate" version = "0.8.6" @@ -552,25 +572,6 @@ name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "font" -version = "0.1.0" -dependencies = [ - "cocoa 0.20.2 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "core-foundation-sys 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "core-graphics 0.19.2 (registry+https://github.com/rust-lang/crates.io-index)", - "core-text 15.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "dwrote 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", - "euclid 0.20.14 (registry+https://github.com/rust-lang/crates.io-index)", - "foreign-types 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", - "freetype-rs 0.26.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.72 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", - "servo-fontconfig 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "foreign-types" version = "0.3.2" @@ -2316,6 +2317,7 @@ dependencies = [ "checksum core-video-sys 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "34ecad23610ad9757664d644e369246edde1803fcb43ed72876565098a5d3828" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum crossbeam-utils 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8" +"checksum crossfont 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fb710de01349371230ec5f5e65410826682448dfad14d97b473a69d850f686bd" "checksum deflate 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174" "checksum derivative 2.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "cb582b60359da160a9477ee80f15c8d784c477e69c217ef2cdd4169c24ea380f" "checksum dirs 2.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" diff --git a/Cargo.toml b/Cargo.toml index b904b2f8..0198afd7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,7 +2,6 @@ members = [ "alacritty", "alacritty_terminal", - "font", ] [profile.release] diff --git a/alacritty/Cargo.toml b/alacritty/Cargo.toml index c30bc370..1bd3ab82 100644 --- a/alacritty/Cargo.toml +++ b/alacritty/Cargo.toml @@ -20,7 +20,7 @@ serde_json = "1" glutin = { version = "0.24.0", features = ["serde"] } notify = "4" parking_lot = "0.10.2" -font = { path = "../font", features = ["force_system_fontconfig"] } +crossfont = { version = "0.1.0", features = ["force_system_fontconfig"] } urlocator = "0.1.3" copypasta = { version = "0.7.0", default-features = false } libc = "0.2" diff --git a/alacritty/src/config/font.rs b/alacritty/src/config/font.rs index f718587c..9982352f 100644 --- a/alacritty/src/config/font.rs +++ b/alacritty/src/config/font.rs @@ -1,6 +1,6 @@ use std::fmt; -use font::Size; +use crossfont::Size; use log::error; use serde::de::Visitor; use serde::{Deserialize, Deserializer}; diff --git a/alacritty/src/cursor.rs b/alacritty/src/cursor.rs index 25a2f657..2ee2916c 100644 --- a/alacritty/src/cursor.rs +++ b/alacritty/src/cursor.rs @@ -2,9 +2,9 @@ use std::cmp; -use alacritty_terminal::ansi::CursorStyle; +use crossfont::{BitmapBuffer, Metrics, RasterizedGlyph}; -use font::{BitmapBuffer, Metrics, RasterizedGlyph}; +use alacritty_terminal::ansi::CursorStyle; pub fn get_cursor_glyph( cursor: CursorStyle, diff --git a/alacritty/src/display.rs b/alacritty/src/display.rs index abbb3666..ad22c852 100644 --- a/alacritty/src/display.rs +++ b/alacritty/src/display.rs @@ -21,12 +21,12 @@ use unicode_width::UnicodeWidthChar; use wayland_client::{Display as WaylandDisplay, EventQueue}; #[cfg(target_os = "macos")] -use font::set_font_smoothing; -use font::{self, Rasterize, Rasterizer}; +use crossfont::set_font_smoothing; +use crossfont::{self, Rasterize, Rasterizer}; use alacritty_terminal::event::{EventListener, OnResize}; -use alacritty_terminal::index::{Line, Direction}; use alacritty_terminal::index::{Column, Point}; +use alacritty_terminal::index::{Direction, Line}; use alacritty_terminal::selection::Selection; use alacritty_terminal::term::{RenderableCell, SizeInfo, Term, TermMode}; @@ -50,7 +50,7 @@ pub enum Error { Window(window::Error), /// Error dealing with fonts. - Font(font::Error), + Font(crossfont::Error), /// Error in renderer. Render(renderer::Error), @@ -87,8 +87,8 @@ impl From for Error { } } -impl From for Error { - fn from(val: font::Error) -> Self { +impl From for Error { + fn from(val: crossfont::Error) -> Self { Error::Font(val) } } @@ -621,11 +621,7 @@ impl Display { } /// Format search regex to account for the cursor and fullwidth characters. - fn format_search( - size_info: &SizeInfo, - search_regex: &str, - search_label: &str, - ) -> String { + fn format_search(size_info: &SizeInfo, search_regex: &str, search_label: &str) -> String { // Add spacers for wide chars. let mut formatted_regex = String::with_capacity(search_regex.len()); for c in search_regex.chars() { @@ -721,7 +717,7 @@ fn dynamic_padding(padding: f32, dimension: f32, cell_dimension: f32) -> f32 { /// Calculate the cell dimensions based on font metrics. #[inline] -fn compute_cell_size(config: &Config, metrics: &font::Metrics) -> (f32, f32) { +fn compute_cell_size(config: &Config, metrics: &crossfont::Metrics) -> (f32, f32) { let offset_x = f64::from(config.ui_config.font.offset.x); let offset_y = f64::from(config.ui_config.font.offset.y); ( diff --git a/alacritty/src/event.rs b/alacritty/src/event.rs index 9fff5260..56e5b841 100644 --- a/alacritty/src/event.rs +++ b/alacritty/src/event.rs @@ -25,8 +25,8 @@ use log::info; use serde_json as json; #[cfg(target_os = "macos")] -use font::set_font_smoothing; -use font::{self, Size}; +use crossfont::set_font_smoothing; +use crossfont::{self, Size}; use alacritty_terminal::config::LOG_TARGET_CONFIG; use alacritty_terminal::event::{Event as TerminalEvent, EventListener, Notify, OnResize}; diff --git a/alacritty/src/renderer/mod.rs b/alacritty/src/renderer/mod.rs index 09682e6e..58d43406 100644 --- a/alacritty/src/renderer/mod.rs +++ b/alacritty/src/renderer/mod.rs @@ -9,18 +9,14 @@ use std::ptr; use std::sync::mpsc; use std::time::Duration; -use fnv::FnvHasher; -use font::{ +use crossfont::{ BitmapBuffer, FontDesc, FontKey, GlyphKey, Rasterize, RasterizedGlyph, Rasterizer, Size, Slant, Style, Weight, }; +use fnv::FnvHasher; use log::{error, info}; use notify::{watcher, DebouncedEvent, RecursiveMode, Watcher}; -use crate::cursor; -use crate::gl; -use crate::gl::types::*; -use crate::renderer::rects::RenderRect; use alacritty_terminal::config::Cursor; use alacritty_terminal::index::{Column, Line}; use alacritty_terminal::term::cell::{self, Flags}; @@ -28,11 +24,15 @@ use alacritty_terminal::term::color::Rgb; use alacritty_terminal::term::{CursorKey, RenderableCell, RenderableCellContent, SizeInfo}; use alacritty_terminal::thread; -pub mod rects; - use crate::config::font::{Font, FontDescription}; use crate::config::ui_config::{Delta, UIConfig}; use crate::config::window::{StartupMode, WindowConfig}; +use crate::cursor; +use crate::gl; +use crate::gl::types::*; +use crate::renderer::rects::RenderRect; + +pub mod rects; // Shader paths for live reload. static TEXT_SHADER_F_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../res/text.f.glsl"); @@ -166,13 +166,13 @@ pub struct GlyphCache { bold_italic_key: FontKey, /// Font size. - font_size: font::Size, + font_size: crossfont::Size, /// Glyph offset. glyph_offset: Delta, /// Font metrics. - metrics: font::Metrics, + metrics: crossfont::Metrics, } impl GlyphCache { @@ -180,7 +180,7 @@ impl GlyphCache { mut rasterizer: Rasterizer, font: &Font, loader: &mut L, - ) -> Result + ) -> Result where L: LoadGlyph, { @@ -222,7 +222,7 @@ impl GlyphCache { fn compute_font_keys( font: &Font, rasterizer: &mut Rasterizer, - ) -> Result<(FontKey, FontKey, FontKey, FontKey), font::Error> { + ) -> Result<(FontKey, FontKey, FontKey, FontKey), crossfont::Error> { let size = font.size; // Load regular font. @@ -261,7 +261,7 @@ impl GlyphCache { rasterizer: &mut Rasterizer, description: &FontDesc, size: Size, - ) -> Result { + ) -> Result { match rasterizer.load_font(description, size) { Ok(font) => Ok(font), Err(err) => { @@ -316,7 +316,7 @@ impl GlyphCache { font: &Font, dpr: f64, loader: &mut L, - ) -> Result<(), font::Error> { + ) -> Result<(), crossfont::Error> { // Update dpi scaling. self.rasterizer.update_dpr(dpr as f32); @@ -341,7 +341,7 @@ impl GlyphCache { Ok(()) } - pub fn font_metrics(&self) -> font::Metrics { + pub fn font_metrics(&self) -> crossfont::Metrics { self.metrics } @@ -354,8 +354,8 @@ impl GlyphCache { } /// Calculate font metrics without access to a glyph cache. - pub fn static_metrics(font: Font, dpr: f64) -> Result { - let mut rasterizer = font::Rasterizer::new(dpr as f32, font.use_thin_strokes())?; + pub fn static_metrics(font: Font, dpr: f64) -> Result { + let mut rasterizer = crossfont::Rasterizer::new(dpr as f32, font.use_thin_strokes())?; let regular_desc = GlyphCache::make_desc(&font.normal(), Slant::Normal, Weight::Normal); let regular = Self::load_regular_font(&mut rasterizer, ®ular_desc, font.size)?; rasterizer.get_glyph(GlyphKey { font_key: regular, c: 'm', size: font.size })?; diff --git a/alacritty/src/renderer/rects.rs b/alacritty/src/renderer/rects.rs index 15f181e2..5cd856ab 100644 --- a/alacritty/src/renderer/rects.rs +++ b/alacritty/src/renderer/rects.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; -use font::Metrics; +use crossfont::Metrics; use alacritty_terminal::index::{Column, Point}; use alacritty_terminal::term::cell::Flags; diff --git a/alacritty/src/url.rs b/alacritty/src/url.rs index 08a85f1b..f972099d 100644 --- a/alacritty/src/url.rs +++ b/alacritty/src/url.rs @@ -1,11 +1,10 @@ use std::cmp::min; use std::mem; +use crossfont::Metrics; use glutin::event::{ElementState, ModifiersState}; use urlocator::{UrlLocation, UrlLocator}; -use font::Metrics; - use alacritty_terminal::index::{Column, Point}; use alacritty_terminal::term::cell::Flags; use alacritty_terminal::term::color::Rgb; diff --git a/alacritty/src/window.rs b/alacritty/src/window.rs index c2a7457e..81a61218 100644 --- a/alacritty/src/window.rs +++ b/alacritty/src/window.rs @@ -55,7 +55,7 @@ pub enum Error { ContextCreation(glutin::CreationError), /// Error dealing with fonts. - Font(font::Error), + Font(crossfont::Error), /// Error manipulating the rendering context. Context(glutin::ContextError), @@ -96,8 +96,8 @@ impl From for Error { } } -impl From for Error { - fn from(val: font::Error) -> Self { +impl From for Error { + fn from(val: crossfont::Error) -> Self { Error::Font(val) } } diff --git a/font/Cargo.toml b/font/Cargo.toml deleted file mode 100644 index 89696a47..00000000 --- a/font/Cargo.toml +++ /dev/null @@ -1,31 +0,0 @@ -[package] -name = "font" -version = "0.1.0" -authors = ["Christian Duerr ", "Joe Wilm "] -description = "Font rendering using the best available solution per platform" -license = "Apache-2.0" -edition = "2018" - -[dependencies] -euclid = "0.20" -libc = "0.2" -foreign-types = "0.5" -log = "0.4" - -[target.'cfg(not(any(target_os = "macos", windows)))'.dependencies] -servo-fontconfig = "0.5.1" -freetype-rs = "0.26" - -[target.'cfg(target_os = "macos")'.dependencies] -cocoa = "0.20.1" -core-foundation = "0.7" -core-text = "15" -core-graphics = "0.19" -core-foundation-sys = "0.7" - -[target.'cfg(windows)'.dependencies] -dwrote = { version = "0.11" } -winapi = { version = "0.3", features = ["impl-default"] } - -[features] -force_system_fontconfig = ["servo-fontconfig/force_system_lib"] diff --git a/font/src/darwin/byte_order.rs b/font/src/darwin/byte_order.rs deleted file mode 100644 index 3cd767f0..00000000 --- a/font/src/darwin/byte_order.rs +++ /dev/null @@ -1,57 +0,0 @@ -//! Constants for bitmap byte order. - -#![allow(non_upper_case_globals)] -pub const kCGBitmapByteOrder32Little: u32 = 2 << 12; -pub const kCGBitmapByteOrder32Big: u32 = 4 << 12; - -#[cfg(target_endian = "little")] -pub const kCGBitmapByteOrder32Host: u32 = kCGBitmapByteOrder32Little; - -#[cfg(target_endian = "big")] -pub const kCGBitmapByteOrder32Host: u32 = kCGBitmapByteOrder32Big; - -#[cfg(target_endian = "little")] -pub fn extract_rgba(bytes: &[u8]) -> Vec { - let pixels = bytes.len() / 4; - let mut rgb = Vec::with_capacity(pixels * 4); - - for i in 0..pixels { - let offset = i * 4; - rgb.push(bytes[offset + 2]); - rgb.push(bytes[offset + 1]); - rgb.push(bytes[offset]); - rgb.push(bytes[offset + 3]); - } - - rgb -} - -#[cfg(target_endian = "big")] -pub fn extract_rgba(bytes: Vec) -> Vec { - bytes -} - -#[cfg(target_endian = "little")] -pub fn extract_rgb(bytes: &[u8]) -> Vec { - let pixels = bytes.len() / 4; - let mut rgb = Vec::with_capacity(pixels * 3); - - for i in 0..pixels { - let offset = i * 4; - rgb.push(bytes[offset + 2]); - rgb.push(bytes[offset + 1]); - rgb.push(bytes[offset]); - } - - rgb -} - -#[cfg(target_endian = "big")] -pub fn extract_rgb(bytes: Vec) -> Vec { - bytes - .into_iter() - .enumerate() - .filter(|&(index, _)| ((index) % 4) != 0) - .map(|(_, val)| val) - .collect::>() -} diff --git a/font/src/darwin/mod.rs b/font/src/darwin/mod.rs deleted file mode 100644 index 0194925f..00000000 --- a/font/src/darwin/mod.rs +++ /dev/null @@ -1,634 +0,0 @@ -//! Font rendering based on CoreText. - -#![allow(improper_ctypes)] -use std::collections::HashMap; -use std::path::PathBuf; -use std::ptr; - -use core_foundation::array::{CFArray, CFIndex}; -use core_foundation::string::CFString; -use core_graphics::base::kCGImageAlphaPremultipliedFirst; -use core_graphics::color_space::CGColorSpace; -use core_graphics::context::CGContext; -use core_graphics::font::{CGFont, CGGlyph}; -use core_graphics::geometry::{CGPoint, CGRect, CGSize}; -use core_text::font::{ - cascade_list_for_languages as ct_cascade_list_for_languages, - new_from_descriptor as ct_new_from_descriptor, CTFont, -}; -use core_text::font_collection::create_for_family; -use core_text::font_collection::get_family_names as ct_get_family_names; -use core_text::font_descriptor::kCTFontColorGlyphsTrait; -use core_text::font_descriptor::kCTFontDefaultOrientation; -use core_text::font_descriptor::kCTFontHorizontalOrientation; -use core_text::font_descriptor::kCTFontVerticalOrientation; -use core_text::font_descriptor::SymbolicTraitAccessors; -use core_text::font_descriptor::{CTFontDescriptor, CTFontOrientation}; - -use cocoa::base::{id, nil, NO}; -use cocoa::foundation::{NSOperatingSystemVersion, NSProcessInfo, NSString, NSUserDefaults}; - -use euclid::{Point2D, Rect, Size2D}; - -use log::{trace, warn}; - -pub mod byte_order; -use byte_order::kCGBitmapByteOrder32Host; - -use super::{ - BitmapBuffer, FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight, -}; - -/// Font descriptor. -/// -/// The descriptor provides data about a font and supports creating a font. -#[derive(Debug)] -pub struct Descriptor { - family_name: String, - font_name: String, - style_name: String, - display_name: String, - font_path: PathBuf, - - ct_descriptor: CTFontDescriptor, -} - -impl Descriptor { - fn new(desc: CTFontDescriptor) -> Descriptor { - Descriptor { - family_name: desc.family_name(), - font_name: desc.font_name(), - style_name: desc.style_name(), - display_name: desc.display_name(), - font_path: desc.font_path().unwrap_or_else(PathBuf::new), - ct_descriptor: desc, - } - } -} - -/// Rasterizer, the main type exported by this package. -/// -/// Given a fontdesc, can rasterize fonts. -pub struct Rasterizer { - fonts: HashMap, - keys: HashMap<(FontDesc, Size), FontKey>, - device_pixel_ratio: f32, - use_thin_strokes: bool, -} - -/// Errors occurring when using the core text rasterizer. -#[derive(Debug)] -pub enum Error { - /// Tried to rasterize a glyph but it was not available. - MissingGlyph(char), - - /// Couldn't find font matching description. - MissingFont(FontDesc), - - /// Requested an operation with a FontKey that isn't known to the rasterizer. - FontNotLoaded, -} - -impl ::std::error::Error for Error { - fn description(&self) -> &str { - match *self { - Error::MissingGlyph(ref _c) => "Unable to find the requested glyph", - Error::MissingFont(ref _desc) => "Unable to find the requested font", - Error::FontNotLoaded => "Tried to operate on font that hasn't been loaded", - } - } -} - -impl ::std::fmt::Display for Error { - fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { - match *self { - Error::MissingGlyph(ref c) => write!(f, "Glyph not found for char {:?}", c), - Error::MissingFont(ref desc) => write!(f, "Unable to find the font {}", desc), - Error::FontNotLoaded => f.write_str("Tried to use a font that hasn't been loaded"), - } - } -} - -impl crate::Rasterize for Rasterizer { - type Err = Error; - - fn new(device_pixel_ratio: f32, use_thin_strokes: bool) -> Result { - Ok(Rasterizer { - fonts: HashMap::new(), - keys: HashMap::new(), - device_pixel_ratio, - use_thin_strokes, - }) - } - - /// Get metrics for font specified by FontKey. - fn metrics(&self, key: FontKey, _size: Size) -> Result { - let font = self.fonts.get(&key).ok_or(Error::FontNotLoaded)?; - - Ok(font.metrics()) - } - - fn load_font(&mut self, desc: &FontDesc, size: Size) -> Result { - let scaled_size = Size::new(size.as_f32_pts() * self.device_pixel_ratio); - self.keys.get(&(desc.to_owned(), scaled_size)).map(|k| Ok(*k)).unwrap_or_else(|| { - let font = self.get_font(desc, size)?; - let key = FontKey::next(); - - self.fonts.insert(key, font); - self.keys.insert((desc.clone(), scaled_size), key); - - Ok(key) - }) - } - - /// Get rasterized glyph for given glyph key. - fn get_glyph(&mut self, glyph: GlyphKey) -> Result { - // Get loaded font. - let font = self.fonts.get(&glyph.font_key).ok_or(Error::FontNotLoaded)?; - - // First try the font itself as a direct hit. - self.maybe_get_glyph(glyph, font).unwrap_or_else(|| { - // Then try fallbacks. - for fallback in &font.fallbacks { - if let Some(result) = self.maybe_get_glyph(glyph, &fallback) { - // Found a fallback. - return result; - } - } - // No fallback, give up. - Err(Error::MissingGlyph(glyph.c)) - }) - } - - fn update_dpr(&mut self, device_pixel_ratio: f32) { - self.device_pixel_ratio = device_pixel_ratio; - } -} - -impl Rasterizer { - fn get_specific_face( - &mut self, - desc: &FontDesc, - style: &str, - size: Size, - ) -> Result { - let descriptors = descriptors_for_family(&desc.name[..]); - for descriptor in descriptors { - if descriptor.style_name == style { - // Found the font we want. - let scaled_size = f64::from(size.as_f32_pts()) * f64::from(self.device_pixel_ratio); - let font = descriptor.to_font(scaled_size, true); - return Ok(font); - } - } - - Err(Error::MissingFont(desc.to_owned())) - } - - fn get_matching_face( - &mut self, - desc: &FontDesc, - slant: Slant, - weight: Weight, - size: Size, - ) -> Result { - let bold = match weight { - Weight::Bold => true, - _ => false, - }; - let italic = match slant { - Slant::Normal => false, - _ => true, - }; - let scaled_size = f64::from(size.as_f32_pts()) * f64::from(self.device_pixel_ratio); - - let descriptors = descriptors_for_family(&desc.name[..]); - for descriptor in descriptors { - let font = descriptor.to_font(scaled_size, true); - if font.is_bold() == bold && font.is_italic() == italic { - // Found the font we want. - return Ok(font); - } - } - - Err(Error::MissingFont(desc.to_owned())) - } - - fn get_font(&mut self, desc: &FontDesc, size: Size) -> Result { - match desc.style { - Style::Specific(ref style) => self.get_specific_face(desc, style, size), - Style::Description { slant, weight } => { - self.get_matching_face(desc, slant, weight, size) - }, - } - } - - /// Helper to try and get a glyph for a given font. Used for font fallback. - fn maybe_get_glyph( - &self, - glyph: GlyphKey, - font: &Font, - ) -> Option> { - let scaled_size = self.device_pixel_ratio * glyph.size.as_f32_pts(); - font.get_glyph(glyph.c, f64::from(scaled_size), self.use_thin_strokes) - .map(|r| Some(Ok(r))) - .unwrap_or_else(|e| match e { - Error::MissingGlyph(_) => None, - _ => Some(Err(e)), - }) - } -} - -/// Specifies the intended rendering orientation of the font for obtaining glyph metrics. -#[derive(Debug)] -pub enum FontOrientation { - Default = kCTFontDefaultOrientation as isize, - Horizontal = kCTFontHorizontalOrientation as isize, - Vertical = kCTFontVerticalOrientation as isize, -} - -impl Default for FontOrientation { - fn default() -> FontOrientation { - FontOrientation::Default - } -} - -/// A font. -#[derive(Clone)] -pub struct Font { - ct_font: CTFont, - cg_font: CGFont, - fallbacks: Vec, -} - -unsafe impl Send for Font {} - -/// Set subpixel anti-aliasing on macOS. -/// -/// Sub-pixel anti-aliasing has been disabled since macOS Mojave by default. This function allows -/// overriding the global `CGFontRenderingFontSmoothingDisabled` setting on a per-application basis -/// to re-enable it. -/// -/// This is a no-op on systems running High Sierra or earlier (< 10.14.0). -pub fn set_font_smoothing(enable: bool) { - let min_macos_version = NSOperatingSystemVersion::new(10, 14, 0); - unsafe { - // Check that we're running at least Mojave (10.14.0+). - if !NSProcessInfo::processInfo(nil).isOperatingSystemAtLeastVersion(min_macos_version) { - return; - } - - let key = NSString::alloc(nil).init_str("CGFontRenderingFontSmoothingDisabled"); - if enable { - id::standardUserDefaults().setBool_forKey_(NO, key); - } else { - id::standardUserDefaults().removeObject_forKey_(key); - } - } -} - -/// List all family names. -pub fn get_family_names() -> Vec { - // CFArray of CFStringRef. - let names = ct_get_family_names(); - let mut owned_names = Vec::new(); - - for name in names.iter() { - owned_names.push(name.to_string()); - } - - owned_names -} - -/// Return fallback descriptors for font/language list. -fn cascade_list_for_languages(ct_font: &CTFont, languages: &[String]) -> Vec { - // Convert language type &Vec -> CFArray. - let langarr: CFArray = { - let tmp: Vec = - languages.iter().map(|language| CFString::new(&language)).collect(); - CFArray::from_CFTypes(&tmp) - }; - - // CFArray of CTFontDescriptorRef (again). - let list = ct_cascade_list_for_languages(ct_font, &langarr); - - // Convert CFArray to Vec. - list.into_iter().map(|fontdesc| Descriptor::new(fontdesc.clone())).collect() -} - -/// Get descriptors for family name. -pub fn descriptors_for_family(family: &str) -> Vec { - let mut out = Vec::new(); - - trace!("Family: {}", family); - let ct_collection = create_for_family(family).unwrap_or_else(|| { - // Fallback to Menlo if we can't find the config specified font family. - warn!("Unable to load specified font {}, falling back to Menlo", &family); - create_for_family("Menlo").expect("Menlo exists") - }); - - // CFArray of CTFontDescriptorRef (i think). - let descriptors = ct_collection.get_descriptors(); - if let Some(descriptors) = descriptors { - for descriptor in descriptors.iter() { - out.push(Descriptor::new(descriptor.clone())); - } - } - - out -} - -impl Descriptor { - /// Create a Font from this descriptor. - pub fn to_font(&self, size: f64, load_fallbacks: bool) -> Font { - let ct_font = ct_new_from_descriptor(&self.ct_descriptor, size); - let cg_font = ct_font.copy_to_CGFont(); - - let fallbacks = if load_fallbacks { - descriptors_for_family("Menlo") - .into_iter() - .find(|d| d.font_name == "Menlo-Regular") - .map(|descriptor| { - let menlo = ct_new_from_descriptor(&descriptor.ct_descriptor, size); - - // TODO fixme, hardcoded en for english. - let mut fallbacks = cascade_list_for_languages(&menlo, &["en".to_owned()]) - .into_iter() - .filter(|desc| !desc.font_path.as_os_str().is_empty()) - .map(|desc| desc.to_font(size, false)) - .collect::>(); - - // TODO, we can't use apple's proposed - // .Apple Symbol Fallback (filtered out below), - // but not having these makes us not able to render - // many chars. We add the symbols back in. - // Investigate if we can actually use the .-prefixed - // fallbacks somehow. - if let Some(descriptor) = - descriptors_for_family("Apple Symbols").into_iter().next() - { - fallbacks.push(descriptor.to_font(size, false)) - }; - - // Include Menlo in the fallback list as well. - fallbacks.insert(0, Font { - cg_font: menlo.copy_to_CGFont(), - ct_font: menlo, - fallbacks: Vec::new(), - }); - - fallbacks - }) - .unwrap_or_else(Vec::new) - } else { - Vec::new() - }; - - Font { ct_font, cg_font, fallbacks } - } -} - -impl Font { - /// The the bounding rect of a glyph. - pub fn bounding_rect_for_glyph( - &self, - orientation: FontOrientation, - index: u32, - ) -> Rect { - let cg_rect = self - .ct_font - .get_bounding_rects_for_glyphs(orientation as CTFontOrientation, &[index as CGGlyph]); - - Rect::new( - Point2D::new(cg_rect.origin.x, cg_rect.origin.y), - Size2D::new(cg_rect.size.width, cg_rect.size.height), - ) - } - - pub fn metrics(&self) -> Metrics { - let average_advance = self.glyph_advance('0'); - - let ascent = self.ct_font.ascent() as f64; - let descent = self.ct_font.descent() as f64; - let leading = self.ct_font.leading() as f64; - let line_height = (ascent + descent + leading + 0.5).floor(); - - // Strikeout and underline metrics. - // CoreText doesn't provide strikeout so we provide our own. - let underline_position = (self.ct_font.underline_position() - descent) as f32; - let underline_thickness = self.ct_font.underline_thickness() as f32; - let strikeout_position = (line_height / 2. - descent) as f32; - let strikeout_thickness = underline_thickness; - - Metrics { - average_advance, - line_height, - descent: -(descent as f32), - underline_position, - underline_thickness, - strikeout_position, - strikeout_thickness, - } - } - - pub fn is_bold(&self) -> bool { - self.ct_font.symbolic_traits().is_bold() - } - - pub fn is_italic(&self) -> bool { - self.ct_font.symbolic_traits().is_italic() - } - - pub fn is_colored(&self) -> bool { - (self.ct_font.symbolic_traits() & kCTFontColorGlyphsTrait) != 0 - } - - fn glyph_advance(&self, character: char) -> f64 { - let index = self.glyph_index(character).unwrap(); - - let indices = [index as CGGlyph]; - - unsafe { - self.ct_font.get_advances_for_glyphs( - FontOrientation::Default as _, - &indices[0], - ptr::null_mut(), - 1, - ) - } - } - - pub fn get_glyph( - &self, - character: char, - _size: f64, - use_thin_strokes: bool, - ) -> Result { - let glyph_index = - self.glyph_index(character).ok_or_else(|| Error::MissingGlyph(character))?; - - let bounds = self.bounding_rect_for_glyph(Default::default(), glyph_index); - - let rasterized_left = bounds.origin.x.floor() as i32; - let rasterized_width = - (bounds.origin.x - f64::from(rasterized_left) + bounds.size.width).ceil() as u32; - let rasterized_descent = (-bounds.origin.y).ceil() as i32; - let rasterized_ascent = (bounds.size.height + bounds.origin.y).ceil() as i32; - let rasterized_height = (rasterized_descent + rasterized_ascent) as u32; - - if rasterized_width == 0 || rasterized_height == 0 { - return Ok(RasterizedGlyph { - c: ' ', - width: 0, - height: 0, - top: 0, - left: 0, - buf: BitmapBuffer::RGB(Vec::new()), - }); - } - - let mut cg_context = CGContext::create_bitmap_context( - None, - rasterized_width as usize, - rasterized_height as usize, - 8, // bits per component - rasterized_width as usize * 4, - &CGColorSpace::create_device_rgb(), - kCGImageAlphaPremultipliedFirst | kCGBitmapByteOrder32Host, - ); - - let is_colored = self.is_colored(); - - // Set background color for graphics context. - let bg_a = if is_colored { 0.0 } else { 1.0 }; - cg_context.set_rgb_fill_color(0.0, 0.0, 0.0, bg_a); - - let context_rect = CGRect::new( - &CGPoint::new(0.0, 0.0), - &CGSize::new(f64::from(rasterized_width), f64::from(rasterized_height)), - ); - - cg_context.fill_rect(context_rect); - - if use_thin_strokes { - cg_context.set_font_smoothing_style(16); - } - - cg_context.set_allows_font_smoothing(true); - cg_context.set_should_smooth_fonts(true); - cg_context.set_allows_font_subpixel_quantization(true); - cg_context.set_should_subpixel_quantize_fonts(true); - cg_context.set_allows_font_subpixel_positioning(true); - cg_context.set_should_subpixel_position_fonts(true); - cg_context.set_allows_antialiasing(true); - cg_context.set_should_antialias(true); - - // Set fill color to white for drawing the glyph. - cg_context.set_rgb_fill_color(1.0, 1.0, 1.0, 1.0); - let rasterization_origin = - CGPoint { x: f64::from(-rasterized_left), y: f64::from(rasterized_descent) }; - - self.ct_font.draw_glyphs( - &[glyph_index as CGGlyph], - &[rasterization_origin], - cg_context.clone(), - ); - - let rasterized_pixels = cg_context.data().to_vec(); - - let buf = if is_colored { - BitmapBuffer::RGBA(byte_order::extract_rgba(&rasterized_pixels)) - } else { - BitmapBuffer::RGB(byte_order::extract_rgb(&rasterized_pixels)) - }; - - Ok(RasterizedGlyph { - c: character, - left: rasterized_left, - top: (bounds.size.height + bounds.origin.y).ceil() as i32, - width: rasterized_width as i32, - height: rasterized_height as i32, - buf, - }) - } - - fn glyph_index(&self, character: char) -> Option { - // Encode this char as utf-16. - let mut buf = [0; 2]; - let encoded: &[u16] = character.encode_utf16(&mut buf); - // And use the utf-16 buffer to get the index. - self.glyph_index_utf16(encoded) - } - - fn glyph_index_utf16(&self, encoded: &[u16]) -> Option { - // Output buffer for the glyph. for non-BMP glyphs, like - // emojis, this will be filled with two chars the second - // always being a 0. - let mut glyphs: [CGGlyph; 2] = [0; 2]; - - let res = unsafe { - self.ct_font.get_glyphs_for_characters( - encoded.as_ptr(), - glyphs.as_mut_ptr(), - encoded.len() as CFIndex, - ) - }; - - if res { - Some(u32::from(glyphs[0])) - } else { - None - } - } -} - -#[cfg(test)] -mod tests { - use super::BitmapBuffer; - - #[test] - fn get_family_names() { - let names = super::get_family_names(); - assert!(names.contains(&String::from("Menlo"))); - assert!(names.contains(&String::from("Monaco"))); - } - - #[test] - fn get_descriptors_and_build_font() { - let list = super::descriptors_for_family("Menlo"); - assert!(!list.is_empty()); - println!("{:?}", list); - - // Check to_font. - let fonts = list.iter().map(|desc| desc.to_font(72., false)).collect::>(); - - for font in fonts { - // Get a glyph. - for c in &['a', 'b', 'c', 'd'] { - let glyph = font.get_glyph(*c, 72., false).unwrap(); - - let buf = match &glyph.buf { - BitmapBuffer::RGB(buf) => buf, - BitmapBuffer::RGBA(buf) => buf, - }; - - // Debug the glyph.. sigh. - for row in 0..glyph.height { - for col in 0..glyph.width { - let index = ((glyph.width * 3 * row) + (col * 3)) as usize; - let value = buf[index]; - let c = match value { - 0..=50 => ' ', - 51..=100 => '.', - 101..=150 => '~', - 151..=200 => '*', - 201..=255 => '#', - }; - print!("{}", c); - } - println!(); - } - } - } - } -} diff --git a/font/src/directwrite/mod.rs b/font/src/directwrite/mod.rs deleted file mode 100644 index d139b35e..00000000 --- a/font/src/directwrite/mod.rs +++ /dev/null @@ -1,335 +0,0 @@ -//! Rasterization powered by DirectWrite. - -use std::borrow::Cow; -use std::collections::HashMap; -use std::ffi::OsString; -use std::fmt::{self, Display, Formatter}; -use std::os::windows::ffi::OsStringExt; - -use dwrote::{ - FontCollection, FontFace, FontFallback, FontStretch, FontStyle, FontWeight, GlyphOffset, - GlyphRunAnalysis, TextAnalysisSource, TextAnalysisSourceMethods, DWRITE_GLYPH_RUN, -}; - -use winapi::shared::ntdef::{HRESULT, LOCALE_NAME_MAX_LENGTH}; -use winapi::um::dwrite; -use winapi::um::winnls::GetUserDefaultLocaleName; - -use super::{ - BitmapBuffer, FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight, -}; - -/// Cached DirectWrite font. -struct Font { - face: FontFace, - family_name: String, - weight: FontWeight, - style: FontStyle, - stretch: FontStretch, -} - -pub struct DirectWriteRasterizer { - fonts: HashMap, - keys: HashMap, - device_pixel_ratio: f32, - available_fonts: FontCollection, - fallback_sequence: Option, -} - -impl DirectWriteRasterizer { - fn rasterize_glyph( - &self, - face: &FontFace, - size: Size, - c: char, - ) -> Result { - let glyph_index = self.get_glyph_index(face, c)?; - - let em_size = em_size(size); - - let glyph_run = DWRITE_GLYPH_RUN { - fontFace: unsafe { face.as_ptr() }, - fontEmSize: em_size, - glyphCount: 1, - glyphIndices: &glyph_index, - glyphAdvances: &0.0, - glyphOffsets: &GlyphOffset::default(), - isSideways: 0, - bidiLevel: 0, - }; - - let rendering_mode = face.get_recommended_rendering_mode_default_params( - em_size, - self.device_pixel_ratio, - dwrote::DWRITE_MEASURING_MODE_NATURAL, - ); - - let glyph_analysis = GlyphRunAnalysis::create( - &glyph_run, - self.device_pixel_ratio, - None, - rendering_mode, - dwrote::DWRITE_MEASURING_MODE_NATURAL, - 0.0, - 0.0, - ) - .map_err(Error::DirectWriteError)?; - - let bounds = glyph_analysis - .get_alpha_texture_bounds(dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1) - .map_err(Error::DirectWriteError)?; - - let buf = glyph_analysis - .create_alpha_texture(dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1, bounds) - .map_err(Error::DirectWriteError)?; - - Ok(RasterizedGlyph { - c, - width: (bounds.right - bounds.left) as i32, - height: (bounds.bottom - bounds.top) as i32, - top: -bounds.top, - left: bounds.left, - buf: BitmapBuffer::RGB(buf), - }) - } - - fn get_loaded_font(&self, font_key: FontKey) -> Result<&Font, Error> { - self.fonts.get(&font_key).ok_or(Error::FontNotLoaded) - } - - fn get_glyph_index(&self, face: &FontFace, c: char) -> Result { - let idx = *face - .get_glyph_indices(&[c as u32]) - .first() - // DirectWrite returns 0 if the glyph does not exist in the font. - .filter(|glyph_index| **glyph_index != 0) - .ok_or_else(|| Error::MissingGlyph(c))?; - - Ok(idx) - } - - fn get_fallback_font(&self, loaded_font: &Font, c: char) -> Option { - let fallback = self.fallback_sequence.as_ref()?; - - let mut buf = [0u16; 2]; - c.encode_utf16(&mut buf); - - let length = c.len_utf16() as u32; - let utf16_codepoints = &buf[..length as usize]; - - let locale = get_current_locale(); - - let text_analysis_source_data = TextAnalysisSourceData { locale: &locale, length }; - let text_analysis_source = TextAnalysisSource::from_text( - Box::new(text_analysis_source_data), - Cow::Borrowed(utf16_codepoints), - ); - - let fallback_result = fallback.map_characters( - &text_analysis_source, - 0, - length, - &self.available_fonts, - Some(&loaded_font.family_name), - loaded_font.weight, - loaded_font.style, - loaded_font.stretch, - ); - - fallback_result.mapped_font - } -} - -impl crate::Rasterize for DirectWriteRasterizer { - type Err = Error; - - fn new(device_pixel_ratio: f32, _: bool) -> Result { - Ok(DirectWriteRasterizer { - fonts: HashMap::new(), - keys: HashMap::new(), - device_pixel_ratio, - available_fonts: FontCollection::system(), - fallback_sequence: FontFallback::get_system_fallback(), - }) - } - - fn metrics(&self, key: FontKey, size: Size) -> Result { - let face = &self.get_loaded_font(key)?.face; - let vmetrics = face.metrics().metrics0(); - - let scale = em_size(size) * self.device_pixel_ratio / f32::from(vmetrics.designUnitsPerEm); - - let underline_position = f32::from(vmetrics.underlinePosition) * scale; - let underline_thickness = f32::from(vmetrics.underlineThickness) * scale; - - let strikeout_position = f32::from(vmetrics.strikethroughPosition) * scale; - let strikeout_thickness = f32::from(vmetrics.strikethroughThickness) * scale; - - let ascent = f32::from(vmetrics.ascent) * scale; - let descent = -f32::from(vmetrics.descent) * scale; - let line_gap = f32::from(vmetrics.lineGap) * scale; - - let line_height = f64::from(ascent - descent + line_gap); - - // Since all monospace characters have the same width, we use `!` for horizontal metrics. - let c = '!'; - let glyph_index = self.get_glyph_index(face, c)?; - - let glyph_metrics = face.get_design_glyph_metrics(&[glyph_index], false); - let hmetrics = glyph_metrics.first().ok_or_else(|| Error::MissingGlyph(c))?; - - let average_advance = f64::from(hmetrics.advanceWidth) * f64::from(scale); - - Ok(Metrics { - descent, - average_advance, - line_height, - underline_position, - underline_thickness, - strikeout_position, - strikeout_thickness, - }) - } - - fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result { - // Fast path if face is already loaded. - if let Some(key) = self.keys.get(desc) { - return Ok(*key); - } - - let family = self - .available_fonts - .get_font_family_by_name(&desc.name) - .ok_or_else(|| Error::MissingFont(desc.clone()))?; - - let font = match desc.style { - Style::Description { weight, slant } => { - // This searches for the "best" font - should mean we don't have to worry about - // fallbacks if our exact desired weight/style isn't available. - Ok(family.get_first_matching_font(weight.into(), FontStretch::Normal, slant.into())) - }, - Style::Specific(ref style) => { - let mut idx = 0; - let count = family.get_font_count(); - - loop { - if idx == count { - break Err(Error::MissingFont(desc.clone())); - } - - let font = family.get_font(idx); - - if font.face_name() == *style { - break Ok(font); - } - - idx += 1; - } - }, - }?; - - let key = FontKey::next(); - self.keys.insert(desc.clone(), key); - self.fonts.insert(key, font.into()); - - Ok(key) - } - - fn get_glyph(&mut self, glyph: GlyphKey) -> Result { - let loaded_font = self.get_loaded_font(glyph.font_key)?; - - match self.rasterize_glyph(&loaded_font.face, glyph.size, glyph.c) { - Err(err @ Error::MissingGlyph(_)) => { - let fallback_font = self.get_fallback_font(&loaded_font, glyph.c).ok_or(err)?; - self.rasterize_glyph(&fallback_font.create_font_face(), glyph.size, glyph.c) - }, - result => result, - } - } - - fn update_dpr(&mut self, device_pixel_ratio: f32) { - self.device_pixel_ratio = device_pixel_ratio; - } -} - -#[derive(Debug)] -pub enum Error { - MissingFont(FontDesc), - MissingGlyph(char), - FontNotLoaded, - DirectWriteError(HRESULT), -} - -impl std::error::Error for Error {} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Error::MissingGlyph(c) => write!(f, "Glyph not found for char {:?}", c), - Error::MissingFont(desc) => write!(f, "Unable to find the font {}", desc), - Error::FontNotLoaded => f.write_str("Tried to use a font that hasn't been loaded"), - Error::DirectWriteError(hresult) => { - write!(f, "A DirectWrite rendering error occurred: {:#X}", hresult) - }, - } - } -} - -fn em_size(size: Size) -> f32 { - size.as_f32_pts() * (96.0 / 72.0) -} - -impl From for Font { - fn from(font: dwrote::Font) -> Font { - Font { - face: font.create_font_face(), - family_name: font.family_name(), - weight: font.weight(), - style: font.style(), - stretch: font.stretch(), - } - } -} - -impl From for FontWeight { - fn from(weight: Weight) -> FontWeight { - match weight { - Weight::Bold => FontWeight::Bold, - Weight::Normal => FontWeight::Regular, - } - } -} - -impl From for FontStyle { - fn from(slant: Slant) -> FontStyle { - match slant { - Slant::Oblique => FontStyle::Oblique, - Slant::Italic => FontStyle::Italic, - Slant::Normal => FontStyle::Normal, - } - } -} - -fn get_current_locale() -> String { - let mut buf = vec![0u16; LOCALE_NAME_MAX_LENGTH]; - let len = unsafe { GetUserDefaultLocaleName(buf.as_mut_ptr(), buf.len() as i32) as usize }; - - // `len` includes null byte, which we don't need in Rust. - OsString::from_wide(&buf[..len - 1]).into_string().expect("Locale not valid unicode") -} - -/// Font fallback information for dwrote's TextAnalysisSource. -struct TextAnalysisSourceData<'a> { - locale: &'a str, - length: u32, -} - -impl TextAnalysisSourceMethods for TextAnalysisSourceData<'_> { - fn get_locale_name(&self, _text_position: u32) -> (Cow, u32) { - (Cow::Borrowed(self.locale), self.length) - } - - fn get_paragraph_reading_direction(&self) -> dwrite::DWRITE_READING_DIRECTION { - dwrite::DWRITE_READING_DIRECTION_LEFT_TO_RIGHT - } -} diff --git a/font/src/ft/fc/char_set.rs b/font/src/ft/fc/char_set.rs deleted file mode 100644 index 9579b7b9..00000000 --- a/font/src/ft/fc/char_set.rs +++ /dev/null @@ -1,70 +0,0 @@ -use std::ptr::NonNull; - -use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; - -use super::ffi::FcCharSetCreate; -use super::ffi::{ - FcBool, FcCharSet, FcCharSetAddChar, FcCharSetCopy, FcCharSetCount, FcCharSetDestroy, - FcCharSetHasChar, FcCharSetMerge, FcCharSetSubtract, FcCharSetUnion, -}; - -foreign_type! { - pub unsafe type CharSet { - type CType = FcCharSet; - fn drop = FcCharSetDestroy; - fn clone = FcCharSetCopy; - } -} - -impl CharSet { - pub fn new() -> Self { - Self::default() - } -} - -impl Default for CharSet { - fn default() -> Self { - CharSet(unsafe { NonNull::new(FcCharSetCreate()).unwrap() }) - } -} - -impl CharSetRef { - pub fn add(&mut self, glyph: char) -> bool { - unsafe { FcCharSetAddChar(self.as_ptr(), glyph as _) == 1 } - } - - pub fn has_char(&self, glyph: char) -> bool { - unsafe { FcCharSetHasChar(self.as_ptr(), glyph as _) == 1 } - } - - pub fn count(&self) -> u32 { - unsafe { FcCharSetCount(self.as_ptr()) as u32 } - } - - pub fn union(&self, other: &CharSetRef) -> CharSet { - unsafe { - let ptr = FcCharSetUnion(self.as_ptr() as _, other.as_ptr() as _); - CharSet::from_ptr(ptr) - } - } - - pub fn subtract(&self, other: &CharSetRef) -> CharSet { - unsafe { - let ptr = FcCharSetSubtract(self.as_ptr() as _, other.as_ptr() as _); - CharSet::from_ptr(ptr) - } - } - - pub fn merge(&self, other: &CharSetRef) -> Result { - unsafe { - // Value is just an indicator whether something was added or not. - let mut value: FcBool = 0; - let res = FcCharSetMerge(self.as_ptr() as _, other.as_ptr() as _, &mut value); - if res == 0 { - Err(()) - } else { - Ok(value != 0) - } - } - } -} diff --git a/font/src/ft/fc/config.rs b/font/src/ft/fc/config.rs deleted file mode 100644 index ac87a284..00000000 --- a/font/src/ft/fc/config.rs +++ /dev/null @@ -1,29 +0,0 @@ -use foreign_types::{foreign_type, ForeignTypeRef}; - -use super::ffi::{FcConfig, FcConfigDestroy, FcConfigGetCurrent, FcConfigGetFonts}; -use super::{FontSetRef, SetName}; - -foreign_type! { - pub unsafe type Config { - type CType = FcConfig; - fn drop = FcConfigDestroy; - } -} - -impl Config { - /// Get the current configuration. - pub fn get_current() -> &'static ConfigRef { - unsafe { ConfigRef::from_ptr(FcConfigGetCurrent()) } - } -} - -impl ConfigRef { - /// Returns one of the two sets of fonts from the configuration as - /// specified by `set`. - pub fn get_fonts(&self, set: SetName) -> &FontSetRef { - unsafe { - let ptr = FcConfigGetFonts(self.as_ptr(), set as u32); - FontSetRef::from_ptr(ptr) - } - } -} diff --git a/font/src/ft/fc/font_set.rs b/font/src/ft/fc/font_set.rs deleted file mode 100644 index 2f003312..00000000 --- a/font/src/ft/fc/font_set.rs +++ /dev/null @@ -1,87 +0,0 @@ -use std::ops::Deref; -use std::ptr::NonNull; - -use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; -use log::trace; - -use super::{ConfigRef, ObjectSetRef, PatternRef}; - -use super::ffi::{FcFontSet, FcFontSetDestroy, FcFontSetList}; - -foreign_type! { - pub unsafe type FontSet { - type CType = FcFontSet; - fn drop = FcFontSetDestroy; - } -} - -impl FontSet { - pub fn list( - config: &ConfigRef, - source: &mut FontSetRef, - pattern: &PatternRef, - objects: &ObjectSetRef, - ) -> FontSet { - let raw = unsafe { - FcFontSetList( - config.as_ptr(), - &mut source.as_ptr(), - 1, // nsets. - pattern.as_ptr(), - objects.as_ptr(), - ) - }; - FontSet(NonNull::new(raw).unwrap()) - } -} - -/// Iterator over a font set. -pub struct Iter<'a> { - font_set: &'a FontSetRef, - num_fonts: usize, - current: usize, -} - -impl<'a> IntoIterator for &'a FontSet { - type IntoIter = Iter<'a>; - type Item = &'a PatternRef; - - fn into_iter(self) -> Iter<'a> { - let num_fonts = unsafe { (*self.as_ptr()).nfont as isize }; - - trace!("Number of fonts is {}", num_fonts); - - Iter { font_set: self.deref(), num_fonts: num_fonts as _, current: 0 } - } -} - -impl<'a> IntoIterator for &'a FontSetRef { - type IntoIter = Iter<'a>; - type Item = &'a PatternRef; - - fn into_iter(self) -> Iter<'a> { - let num_fonts = unsafe { (*self.as_ptr()).nfont as isize }; - - trace!("Number of fonts is {}", num_fonts); - - Iter { font_set: self, num_fonts: num_fonts as _, current: 0 } - } -} - -impl<'a> Iterator for Iter<'a> { - type Item = &'a PatternRef; - - fn next(&mut self) -> Option { - if self.current == self.num_fonts { - None - } else { - let pattern = unsafe { - let ptr = *(*self.font_set.as_ptr()).fonts.add(self.current); - PatternRef::from_ptr(ptr) - }; - - self.current += 1; - Some(pattern) - } - } -} diff --git a/font/src/ft/fc/mod.rs b/font/src/ft/fc/mod.rs deleted file mode 100644 index 1058bea3..00000000 --- a/font/src/ft/fc/mod.rs +++ /dev/null @@ -1,337 +0,0 @@ -use std::fmt; -use std::ptr; - -use foreign_types::{ForeignType, ForeignTypeRef}; - -use fontconfig::fontconfig as ffi; - -use ffi::FcResultNoMatch; -use ffi::{FcFontList, FcFontMatch, FcFontSort}; -use ffi::{FcMatchFont, FcMatchPattern, FcMatchScan}; -use ffi::{FcSetApplication, FcSetSystem}; -use ffi::{FC_SLANT_ITALIC, FC_SLANT_OBLIQUE, FC_SLANT_ROMAN}; -use ffi::{FC_WEIGHT_BLACK, FC_WEIGHT_BOLD, FC_WEIGHT_EXTRABLACK, FC_WEIGHT_EXTRABOLD}; -use ffi::{FC_WEIGHT_BOOK, FC_WEIGHT_MEDIUM, FC_WEIGHT_REGULAR, FC_WEIGHT_SEMIBOLD}; -use ffi::{FC_WEIGHT_EXTRALIGHT, FC_WEIGHT_LIGHT, FC_WEIGHT_THIN}; - -pub mod config; -pub use config::{Config, ConfigRef}; - -pub mod font_set; -pub use font_set::{FontSet, FontSetRef}; - -pub mod object_set; -pub use object_set::{ObjectSet, ObjectSetRef}; - -pub mod char_set; -pub use char_set::{CharSet, CharSetRef}; - -pub mod pattern; -pub use pattern::{FTFaceLocation, Pattern, PatternHash, PatternRef}; - -/// Find the font closest matching the provided pattern. -/// -/// The returned pattern is the result of Pattern::render_prepare. -pub fn font_match(config: &ConfigRef, pattern: &PatternRef) -> Option { - unsafe { - // What is this result actually used for? Seems redundant with - // return type. - let mut result = FcResultNoMatch; - let ptr = FcFontMatch(config.as_ptr(), pattern.as_ptr(), &mut result); - - if ptr.is_null() { - None - } else { - Some(Pattern::from_ptr(ptr)) - } - } -} - -/// List fonts by closeness to the pattern. -pub fn font_sort(config: &ConfigRef, pattern: &PatternRef) -> Option { - unsafe { - // What is this result actually used for? Seems redundant with - // return type. - let mut result = FcResultNoMatch; - - let mut charsets: *mut _ = ptr::null_mut(); - let ptr = FcFontSort( - config.as_ptr(), - pattern.as_ptr(), - 1, // Trim font list. - &mut charsets, - &mut result, - ); - - if ptr.is_null() { - None - } else { - Some(FontSet::from_ptr(ptr)) - } - } -} - -/// List fonts matching pattern. -pub fn font_list( - config: &ConfigRef, - pattern: &PatternRef, - objects: &ObjectSetRef, -) -> Option { - unsafe { - let ptr = FcFontList(config.as_ptr(), pattern.as_ptr(), objects.as_ptr()); - - if ptr.is_null() { - None - } else { - Some(FontSet::from_ptr(ptr)) - } - } -} - -/// Available font sets. -#[derive(Debug, Copy, Clone)] -pub enum SetName { - System = FcSetSystem as isize, - Application = FcSetApplication as isize, -} - -/// When matching, how to match. -#[derive(Debug, Copy, Clone)] -pub enum MatchKind { - Font = FcMatchFont as isize, - Pattern = FcMatchPattern as isize, - Scan = FcMatchScan as isize, -} - -#[derive(Debug, Copy, Clone)] -pub enum Slant { - Italic = FC_SLANT_ITALIC as isize, - Oblique = FC_SLANT_OBLIQUE as isize, - Roman = FC_SLANT_ROMAN as isize, -} - -#[derive(Debug, Copy, Clone)] -pub enum Weight { - Thin = FC_WEIGHT_THIN as isize, - Extralight = FC_WEIGHT_EXTRALIGHT as isize, - Light = FC_WEIGHT_LIGHT as isize, - Book = FC_WEIGHT_BOOK as isize, - Regular = FC_WEIGHT_REGULAR as isize, - Medium = FC_WEIGHT_MEDIUM as isize, - Semibold = FC_WEIGHT_SEMIBOLD as isize, - Bold = FC_WEIGHT_BOLD as isize, - Extrabold = FC_WEIGHT_EXTRABOLD as isize, - Black = FC_WEIGHT_BLACK as isize, - Extrablack = FC_WEIGHT_EXTRABLACK as isize, -} - -#[derive(Debug, Copy, Clone)] -pub enum Width { - Ultracondensed, - Extracondensed, - Condensed, - Semicondensed, - Normal, - Semiexpanded, - Expanded, - Extraexpanded, - Ultraexpanded, - Other(i32), -} - -impl Width { - fn to_isize(self) -> isize { - match self { - Width::Ultracondensed => 50, - Width::Extracondensed => 63, - Width::Condensed => 75, - Width::Semicondensed => 87, - Width::Normal => 100, - Width::Semiexpanded => 113, - Width::Expanded => 125, - Width::Extraexpanded => 150, - Width::Ultraexpanded => 200, - Width::Other(value) => value as isize, - } - } -} - -impl From for Width { - fn from(value: isize) -> Self { - match value { - 50 => Width::Ultracondensed, - 63 => Width::Extracondensed, - 75 => Width::Condensed, - 87 => Width::Semicondensed, - 100 => Width::Normal, - 113 => Width::Semiexpanded, - 125 => Width::Expanded, - 150 => Width::Extraexpanded, - 200 => Width::Ultraexpanded, - _ => Width::Other(value as _), - } - } -} - -/// Subpixel geometry. -#[derive(Debug)] -pub enum Rgba { - Unknown, - Rgb, - Bgr, - Vrgb, - Vbgr, - None, -} - -impl Rgba { - fn to_isize(&self) -> isize { - match *self { - Rgba::Unknown => 0, - Rgba::Rgb => 1, - Rgba::Bgr => 2, - Rgba::Vrgb => 3, - Rgba::Vbgr => 4, - Rgba::None => 5, - } - } -} - -impl fmt::Display for Rgba { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(match *self { - Rgba::Unknown => "unknown", - Rgba::Rgb => "rgb", - Rgba::Bgr => "bgr", - Rgba::Vrgb => "vrgb", - Rgba::Vbgr => "vbgr", - Rgba::None => "none", - }) - } -} - -impl From for Rgba { - fn from(val: isize) -> Rgba { - match val { - 1 => Rgba::Rgb, - 2 => Rgba::Bgr, - 3 => Rgba::Vrgb, - 4 => Rgba::Vbgr, - 5 => Rgba::None, - _ => Rgba::Unknown, - } - } -} - -/// Hinting Style. -#[derive(Debug, Copy, Clone)] -pub enum HintStyle { - None, - Slight, - Medium, - Full, -} - -impl fmt::Display for HintStyle { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(match *self { - HintStyle::None => "none", - HintStyle::Slight => "slight", - HintStyle::Medium => "medium", - HintStyle::Full => "full", - }) - } -} - -/// Lcd filter, used to reduce color fringing with subpixel rendering. -pub enum LcdFilter { - None, - Default, - Light, - Legacy, -} - -impl fmt::Display for LcdFilter { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(match *self { - LcdFilter::None => "none", - LcdFilter::Default => "default", - LcdFilter::Light => "light", - LcdFilter::Legacy => "legacy", - }) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn font_match() { - let mut pattern = Pattern::new(); - pattern.add_family("monospace"); - pattern.add_style("regular"); - - let config = Config::get_current(); - pattern.config_substitute(config, MatchKind::Pattern); - pattern.default_substitute(); - let font = super::font_match(config, &pattern).expect("match font monospace"); - - print!("index={:?}; ", font.index()); - print!("family={:?}; ", font.family()); - print!("style={:?}; ", font.style()); - print!("antialias={:?}; ", font.antialias()); - print!("autohint={:?}; ", font.autohint()); - print!("hinting={:?}; ", font.hinting()); - print!("rgba={:?}; ", font.rgba()); - print!("embeddedbitmap={:?}; ", font.embeddedbitmap()); - print!("lcdfilter={:?}; ", font.lcdfilter()); - print!("hintstyle={:?}", font.hintstyle()); - println!(); - } - - #[test] - fn font_sort() { - let mut pattern = Pattern::new(); - pattern.add_family("monospace"); - pattern.set_slant(Slant::Italic); - - let config = Config::get_current(); - pattern.config_substitute(config, MatchKind::Pattern); - pattern.default_substitute(); - let fonts = super::font_sort(config, &pattern).expect("sort font monospace"); - - for font in fonts.into_iter().take(10) { - let font = pattern.render_prepare(&config, &font); - print!("index={:?}; ", font.index()); - print!("family={:?}; ", font.family()); - print!("style={:?}; ", font.style()); - print!("rgba={:?}", font.rgba()); - print!("rgba={:?}", font.rgba()); - println!(); - } - } - - #[test] - fn font_sort_with_glyph() { - let mut charset = CharSet::new(); - charset.add('💖'); - let mut pattern = Pattern::new(); - pattern.add_charset(&charset); - drop(charset); - - let config = Config::get_current(); - pattern.config_substitute(config, MatchKind::Pattern); - pattern.default_substitute(); - let fonts = super::font_sort(config, &pattern).expect("font_sort"); - - for font in fonts.into_iter().take(10) { - let font = pattern.render_prepare(&config, &font); - print!("index={:?}; ", font.index()); - print!("family={:?}; ", font.family()); - print!("style={:?}; ", font.style()); - print!("rgba={:?}", font.rgba()); - println!(); - } - } -} diff --git a/font/src/ft/fc/object_set.rs b/font/src/ft/fc/object_set.rs deleted file mode 100644 index 74faabbf..00000000 --- a/font/src/ft/fc/object_set.rs +++ /dev/null @@ -1,48 +0,0 @@ -use std::ptr::NonNull; - -use libc::c_char; - -use super::ffi::{FcObjectSet, FcObjectSetAdd, FcObjectSetCreate, FcObjectSetDestroy}; -use foreign_types::{foreign_type, ForeignTypeRef}; - -foreign_type! { - pub unsafe type ObjectSet { - type CType = FcObjectSet; - fn drop = FcObjectSetDestroy; - } -} - -impl ObjectSet { - pub fn new() -> Self { - Self::default() - } -} - -impl Default for ObjectSet { - fn default() -> Self { - ObjectSet(unsafe { NonNull::new(FcObjectSetCreate()).unwrap() }) - } -} - -impl ObjectSetRef { - fn add(&mut self, property: &[u8]) { - unsafe { - FcObjectSetAdd(self.as_ptr(), property.as_ptr() as *mut c_char); - } - } - - #[inline] - pub fn add_file(&mut self) { - self.add(b"file\0"); - } - - #[inline] - pub fn add_index(&mut self) { - self.add(b"index\0"); - } - - #[inline] - pub fn add_style(&mut self) { - self.add(b"style\0"); - } -} diff --git a/font/src/ft/fc/pattern.rs b/font/src/ft/fc/pattern.rs deleted file mode 100644 index 19d72b47..00000000 --- a/font/src/ft/fc/pattern.rs +++ /dev/null @@ -1,627 +0,0 @@ -use std::ffi::{CStr, CString}; -use std::fmt; -use std::mem; -use std::path::PathBuf; -use std::ptr::{self, NonNull}; -use std::str; - -use foreign_types::{foreign_type, ForeignType, ForeignTypeRef}; -use libc::{c_char, c_double, c_int}; - -use super::ffi::FcMatrix; -use super::ffi::FcResultMatch; -use super::ffi::{FcBool, FcFontRenderPrepare, FcPatternGetBool, FcPatternGetDouble}; -use super::ffi::{FcChar8, FcConfigSubstitute, FcDefaultSubstitute, FcPattern, FcPatternHash}; -use super::ffi::{ - FcPatternAddCharSet, FcPatternDestroy, FcPatternDuplicate, FcPatternGetCharSet, - FcPatternGetMatrix, -}; -use super::ffi::{FcPatternAddDouble, FcPatternAddString, FcPatternCreate, FcPatternGetString}; -use super::ffi::{FcPatternAddInteger, FcPatternGetInteger, FcPatternPrint}; - -use super::{CharSetRef, ConfigRef, HintStyle, LcdFilter, MatchKind, Rgba, Slant, Weight, Width}; - -pub struct StringPropertyIter<'a> { - pattern: &'a PatternRef, - object: &'a [u8], - index: usize, -} - -impl<'a> StringPropertyIter<'a> { - fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> StringPropertyIter<'b> { - StringPropertyIter { pattern, object, index: 0 } - } - - fn get_value(&self, index: usize) -> Option<&'a str> { - let mut value: *mut FcChar8 = ptr::null_mut(); - - let result = unsafe { - FcPatternGetString( - self.pattern.as_ptr(), - self.object.as_ptr() as *mut c_char, - index as c_int, - &mut value, - ) - }; - - if result == FcResultMatch { - // Transmute here is to extend lifetime of the str to that of the iterator. - // - // Potential unsafety? What happens if the pattern is modified while this ptr is - // borrowed out? - Some(unsafe { - mem::transmute(CStr::from_ptr(value as *const c_char).to_str().unwrap()) - }) - } else { - None - } - } -} - -/// Iterator over integer properties. -pub struct BooleanPropertyIter<'a> { - pattern: &'a PatternRef, - object: &'a [u8], - index: usize, -} - -impl<'a> BooleanPropertyIter<'a> { - fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> BooleanPropertyIter<'b> { - BooleanPropertyIter { pattern, object, index: 0 } - } - - fn get_value(&self, index: usize) -> Option { - let mut value: FcBool = 0; - - let result = unsafe { - FcPatternGetBool( - self.pattern.as_ptr(), - self.object.as_ptr() as *mut c_char, - index as c_int, - &mut value, - ) - }; - - if result == FcResultMatch { - Some(value != 0) - } else { - None - } - } -} - -/// Iterator over integer properties. -pub struct IntPropertyIter<'a> { - pattern: &'a PatternRef, - object: &'a [u8], - index: usize, -} - -impl<'a> IntPropertyIter<'a> { - fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> IntPropertyIter<'b> { - IntPropertyIter { pattern, object, index: 0 } - } - - fn get_value(&self, index: usize) -> Option { - let mut value = 0 as c_int; - - let result = unsafe { - FcPatternGetInteger( - self.pattern.as_ptr(), - self.object.as_ptr() as *mut c_char, - index as c_int, - &mut value, - ) - }; - - if result == FcResultMatch { - Some(value as isize) - } else { - None - } - } -} - -pub struct RgbaPropertyIter<'a> { - inner: IntPropertyIter<'a>, -} - -impl<'a> RgbaPropertyIter<'a> { - fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> RgbaPropertyIter<'b> { - RgbaPropertyIter { inner: IntPropertyIter::new(pattern, object) } - } - - #[inline] - fn inner<'b>(&'b mut self) -> &'b mut IntPropertyIter<'a> { - &mut self.inner - } - - fn get_value(&self, index: usize) -> Option { - self.inner.get_value(index).map(Rgba::from) - } -} - -pub struct HintStylePropertyIter<'a> { - inner: IntPropertyIter<'a>, -} - -impl<'a> HintStylePropertyIter<'a> { - fn new(pattern: &PatternRef) -> HintStylePropertyIter { - HintStylePropertyIter { inner: IntPropertyIter::new(pattern, b"hintstyle\0") } - } - - #[inline] - fn inner<'b>(&'b mut self) -> &'b mut IntPropertyIter<'a> { - &mut self.inner - } - - fn get_value(&self, index: usize) -> Option { - self.inner.get_value(index).and_then(|hint_style| { - Some(match hint_style { - 0 => HintStyle::None, - 1 => HintStyle::Slight, - 2 => HintStyle::Medium, - 3 => HintStyle::Full, - _ => return None, - }) - }) - } -} - -pub struct LcdFilterPropertyIter<'a> { - inner: IntPropertyIter<'a>, -} - -impl<'a> LcdFilterPropertyIter<'a> { - fn new(pattern: &PatternRef) -> LcdFilterPropertyIter { - LcdFilterPropertyIter { inner: IntPropertyIter::new(pattern, b"lcdfilter\0") } - } - - #[inline] - fn inner<'b>(&'b mut self) -> &'b mut IntPropertyIter<'a> { - &mut self.inner - } - - fn get_value(&self, index: usize) -> Option { - self.inner.get_value(index).and_then(|hint_style| { - Some(match hint_style { - 0 => LcdFilter::None, - 1 => LcdFilter::Default, - 2 => LcdFilter::Light, - 3 => LcdFilter::Legacy, - _ => return None, - }) - }) - } -} - -/// Iterator over integer properties. -pub struct DoublePropertyIter<'a> { - pattern: &'a PatternRef, - object: &'a [u8], - index: usize, -} - -impl<'a> DoublePropertyIter<'a> { - fn new<'b>(pattern: &'b PatternRef, object: &'b [u8]) -> DoublePropertyIter<'b> { - DoublePropertyIter { pattern, object, index: 0 } - } - - fn get_value(&self, index: usize) -> Option { - let mut value = f64::from(0); - - let result = unsafe { - FcPatternGetDouble( - self.pattern.as_ptr(), - self.object.as_ptr() as *mut c_char, - index as c_int, - &mut value, - ) - }; - - if result == FcResultMatch { - Some(value as f64) - } else { - None - } - } -} - -/// Implement debug for a property iterator. -macro_rules! impl_property_iter_debug { - ($iter:ty => $item:ty) => { - impl<'a> fmt::Debug for $iter { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "[")?; - for i in 0.. { - match self.get_value(i) { - Some(val) => { - if i > 0 { - write!(f, ", {}", val)?; - } else { - write!(f, "{}", val)?; - } - }, - _ => break, - } - } - write!(f, "]") - } - } - }; -} - -/// Implement Iterator and Debug for a property iterator. -macro_rules! impl_property_iter { - ($($iter:ty => $item:ty),*) => { - $( - impl<'a> Iterator for $iter { - type Item = $item; - - fn next(&mut self) -> Option { - let res = self.get_value(self.index); - self.index += 1; - res - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.index += n; - self.next() - } - } - impl_property_iter_debug!($iter => $item); - )* - } -} - -/// Implement Iterator and Debug for a property iterator which internally relies -/// on another property iterator. -macro_rules! impl_derived_property_iter { - ($($iter:ty => $item:ty),*) => { - $( - impl<'a> Iterator for $iter { - type Item = $item; - - fn next(&mut self) -> Option { - let index = { self.inner().index }; - let res = self.get_value(index); - self.inner().index += 1; - res - } - - #[inline] - fn nth(&mut self, n: usize) -> Option { - self.inner().index += n; - self.next() - } - } - impl_property_iter_debug!($iter => $item); - )* - } -} - -// Basic Iterators. -impl_property_iter! { - StringPropertyIter<'a> => &'a str, - IntPropertyIter<'a> => isize, - DoublePropertyIter<'a> => f64, - BooleanPropertyIter<'a> => bool -} - -// Derived Iterators. -impl_derived_property_iter! { - RgbaPropertyIter<'a> => Rgba, - HintStylePropertyIter<'a> => HintStyle, - LcdFilterPropertyIter<'a> => LcdFilter -} - -foreign_type! { - pub unsafe type Pattern { - type CType = FcPattern; - fn drop = FcPatternDestroy; - fn clone = FcPatternDuplicate; - } -} - -macro_rules! string_accessor { - ($([$getter:ident, $setter:ident] => $object_name:expr),*) => { - $( - #[inline] - pub fn $setter(&mut self, value: &str) -> bool { - unsafe { - self.add_string($object_name, value) - } - } - - #[inline] - pub fn $getter(&self) -> StringPropertyIter { - unsafe { - self.get_string($object_name) - } - } - )* - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct PatternHash(pub u32); - -#[derive(Hash, Eq, PartialEq, Debug)] -pub struct FTFaceLocation { - pub path: PathBuf, - pub index: isize, -} - -impl FTFaceLocation { - pub fn new(path: PathBuf, index: isize) -> Self { - Self { path, index } - } -} - -impl Pattern { - pub fn new() -> Self { - Self::default() - } -} - -impl Default for Pattern { - fn default() -> Self { - Pattern(unsafe { NonNull::new(FcPatternCreate()).unwrap() }) - } -} - -macro_rules! pattern_get_integer { - ($($method:ident() => $property:expr),+) => { - $( - pub fn $method(&self) -> IntPropertyIter { - unsafe { - self.get_integer($property) - } - } - )+ - }; -} - -macro_rules! boolean_getter { - ($($method:ident() => $property:expr),*) => { - $( - pub fn $method(&self) -> BooleanPropertyIter { - unsafe { - self.get_boolean($property) - } - } - )* - } -} - -macro_rules! double_getter { - ($($method:ident() => $property:expr),*) => { - $( - pub fn $method(&self) -> DoublePropertyIter { - unsafe { - self.get_double($property) - } - } - )* - } -} - -impl PatternRef { - boolean_getter! { - antialias() => b"antialias\0", - hinting() => b"hinting\0", - verticallayout() => b"verticallayout\0", - autohint() => b"autohint\0", - globaladvance() => b"globaladvance\0", - scalable() => b"scalable\0", - symbol() => b"symbol\0", - color() => b"color\0", - minspace() => b"minspace\0", - embolden() => b"embolden\0", - embeddedbitmap() => b"embeddedbitmap\0", - decorative() => b"decorative\0" - } - - double_getter! { - size() => b"size\0", - aspect() => b"aspect\0", - pixelsize() => b"pixelsize\0", - pixelsizefixupfactor() => b"pixelsizefixupfactor\0", - scale() => b"scale\0", - dpi() => b"dpi\0" - } - - string_accessor! { - [family, add_family] => b"family\0", - [familylang, add_familylang] => b"familylang\0", - [style, add_style] => b"style\0", - [stylelang, add_stylelang] => b"stylelang\0", - [fullname, add_fullname] => b"fullname\0", - [fullnamelang, add_fullnamelang] => b"fullnamelang\0", - [foundry, add_foundry] => b"foundry\0", - [capability, add_capability] => b"capability\0", - [fontformat, add_fontformat] => b"fontformat\0", - [fontfeatures, add_fontfeatures] => b"fontfeatures\0", - [namelang, add_namelang] => b"namelang\0", - [postscriptname, add_postscriptname] => b"postscriptname\0" - } - - pattern_get_integer! { - index() => b"index\0" - } - - /// Prints the pattern to stdout. - /// - /// FontConfig doesn't expose a way to iterate over all members of a pattern; - /// instead, we just defer to FcPatternPrint. Otherwise, this could have been - /// a `fmt::Debug` impl. - pub fn print(&self) { - unsafe { FcPatternPrint(self.as_ptr()) } - } - - /// Add a string value to the pattern. - /// - /// If the returned value is `true`, the value is added at the end of - /// any existing list, otherwise it is inserted at the beginning. - /// - /// # Unsafety - /// - /// `object` is not checked to be a valid null-terminated string. - unsafe fn add_string(&mut self, object: &[u8], value: &str) -> bool { - let value = CString::new(&value[..]).unwrap(); - let value = value.as_ptr(); - - FcPatternAddString(self.as_ptr(), object.as_ptr() as *mut c_char, value as *mut FcChar8) - == 1 - } - - unsafe fn add_integer(&self, object: &[u8], int: isize) -> bool { - FcPatternAddInteger(self.as_ptr(), object.as_ptr() as *mut c_char, int as c_int) == 1 - } - - unsafe fn add_double(&self, object: &[u8], value: f64) -> bool { - FcPatternAddDouble(self.as_ptr(), object.as_ptr() as *mut c_char, value as c_double) == 1 - } - - unsafe fn get_string<'a>(&'a self, object: &'a [u8]) -> StringPropertyIter<'a> { - StringPropertyIter::new(self, object) - } - - unsafe fn get_integer<'a>(&'a self, object: &'a [u8]) -> IntPropertyIter<'a> { - IntPropertyIter::new(self, object) - } - - unsafe fn get_double<'a>(&'a self, object: &'a [u8]) -> DoublePropertyIter<'a> { - DoublePropertyIter::new(self, object) - } - - unsafe fn get_boolean<'a>(&'a self, object: &'a [u8]) -> BooleanPropertyIter<'a> { - BooleanPropertyIter::new(self, object) - } - - pub fn hintstyle(&self) -> HintStylePropertyIter { - HintStylePropertyIter::new(self) - } - - pub fn lcdfilter(&self) -> LcdFilterPropertyIter { - LcdFilterPropertyIter::new(self) - } - - pub fn set_slant(&mut self, slant: Slant) -> bool { - unsafe { self.add_integer(b"slant\0", slant as isize) } - } - - pub fn add_pixelsize(&mut self, size: f64) -> bool { - unsafe { self.add_double(b"pixelsize\0", size) } - } - - pub fn set_weight(&mut self, weight: Weight) -> bool { - unsafe { self.add_integer(b"weight\0", weight as isize) } - } - - pub fn set_width(&mut self, width: Width) -> bool { - unsafe { self.add_integer(b"width\0", width.to_isize()) } - } - - pub fn get_width(&self) -> Option { - unsafe { self.get_integer(b"width\0").next().map(Width::from) } - } - - pub fn rgba(&self) -> RgbaPropertyIter { - RgbaPropertyIter::new(self, b"rgba\0") - } - - pub fn set_rgba(&self, rgba: &Rgba) -> bool { - unsafe { self.add_integer(b"rgba\0", rgba.to_isize()) } - } - - pub fn render_prepare(&self, config: &ConfigRef, request: &PatternRef) -> Pattern { - unsafe { - let ptr = FcFontRenderPrepare(config.as_ptr(), self.as_ptr(), request.as_ptr()); - Pattern::from_ptr(ptr) - } - } - - pub fn hash(&self) -> PatternHash { - unsafe { PatternHash(FcPatternHash(self.as_ptr())) } - } - - /// Add charset to the pattern. - /// - /// The referenced charset is copied by Fontconfig internally using - /// FcValueSave so that no references to application provided memory are - /// retained. That is, the CharSet can be safely dropped immediately - /// after being added to the pattern. - pub fn add_charset(&self, charset: &CharSetRef) -> bool { - unsafe { - FcPatternAddCharSet( - self.as_ptr(), - b"charset\0".as_ptr() as *mut c_char, - charset.as_ptr(), - ) == 1 - } - } - - /// Get charset from the pattern. - pub fn get_charset(&self) -> Option<&CharSetRef> { - unsafe { - let mut charset = ptr::null_mut(); - - let result = FcPatternGetCharSet( - self.as_ptr(), - b"charset\0".as_ptr() as *mut c_char, - 0, - &mut charset, - ); - - if result == FcResultMatch { - Some(&*(charset as *const CharSetRef)) - } else { - None - } - } - } - - /// Get matrix from the pattern. - pub fn get_matrix(&self) -> Option { - unsafe { - let mut matrix = ptr::null_mut(); - let result = FcPatternGetMatrix( - self.as_ptr(), - b"matrix\0".as_ptr() as *mut c_char, - 0, - &mut matrix, - ); - - if result == FcResultMatch { - Some(*matrix) - } else { - None - } - } - } - - pub fn file(&self, index: usize) -> Option { - unsafe { self.get_string(b"file\0").nth(index) }.map(From::from) - } - - pub fn ft_face_location(&self, index: usize) -> Option { - match (self.file(index), self.index().next()) { - (Some(path), Some(index)) => Some(FTFaceLocation::new(path, index)), - _ => None, - } - } - - pub fn config_substitute(&mut self, config: &ConfigRef, kind: MatchKind) { - unsafe { - FcConfigSubstitute(config.as_ptr(), self.as_ptr(), kind as u32); - } - } - - pub fn default_substitute(&mut self) { - unsafe { - FcDefaultSubstitute(self.as_ptr()); - } - } -} diff --git a/font/src/ft/mod.rs b/font/src/ft/mod.rs deleted file mode 100644 index dd15c4bb..00000000 --- a/font/src/ft/mod.rs +++ /dev/null @@ -1,787 +0,0 @@ -//! Rasterization powered by FreeType and Fontconfig. - -use std::cmp::{min, Ordering}; -use std::collections::HashMap; -use std::fmt::{self, Display, Formatter}; -use std::rc::Rc; - -use freetype::face::LoadFlag; -use freetype::tt_os2::TrueTypeOS2Table; -use freetype::{self, Library, Matrix}; -use freetype::{freetype_sys, Face as FTFace}; -use libc::{c_long, c_uint}; -use log::{debug, trace}; - -pub mod fc; - -use fc::{CharSet, FTFaceLocation, Pattern, PatternHash, PatternRef}; - -use super::{ - BitmapBuffer, FontDesc, FontKey, GlyphKey, Metrics, Rasterize, RasterizedGlyph, Size, Slant, - Style, Weight, -}; - -struct FallbackFont { - pattern: Pattern, - key: FontKey, -} - -impl FallbackFont { - fn new(pattern: Pattern, key: FontKey) -> FallbackFont { - Self { pattern, key } - } -} - -impl FontKey { - fn from_pattern_hashes(lhs: PatternHash, rhs: PatternHash) -> Self { - // XOR two hashes to get a font ID. - Self { token: lhs.0.rotate_left(1) ^ rhs.0 } - } -} - -#[derive(Default)] -struct FallbackList { - list: Vec, - coverage: CharSet, -} - -struct FaceLoadingProperties { - load_flags: LoadFlag, - render_mode: freetype::RenderMode, - lcd_filter: c_uint, - non_scalable: Option, - colored: bool, - embolden: bool, - matrix: Option, - pixelsize_fixup_factor: Option, - ft_face: Rc, -} - -impl fmt::Debug for FaceLoadingProperties { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - f.debug_struct("Face") - .field("ft_face", &self.ft_face) - .field("load_flags", &self.load_flags) - .field("render_mode", &match self.render_mode { - freetype::RenderMode::Normal => "Normal", - freetype::RenderMode::Light => "Light", - freetype::RenderMode::Mono => "Mono", - freetype::RenderMode::Lcd => "Lcd", - freetype::RenderMode::LcdV => "LcdV", - freetype::RenderMode::Max => "Max", - }) - .field("lcd_filter", &self.lcd_filter) - .finish() - } -} - -/// Rasterizes glyphs for a single font face. -pub struct FreeTypeRasterizer { - library: Library, - faces: HashMap, - ft_faces: HashMap>, - fallback_lists: HashMap, - device_pixel_ratio: f32, -} - -#[inline] -fn to_freetype_26_6(f: f32) -> isize { - ((1i32 << 6) as f32 * f).round() as isize -} - -#[inline] -fn to_fixedpoint_16_6(f: f64) -> c_long { - (f * 65536.0) as c_long -} - -impl Rasterize for FreeTypeRasterizer { - type Err = Error; - - fn new(device_pixel_ratio: f32, _: bool) -> Result { - let library = Library::init()?; - - unsafe { - // Initialize default properties, like user preferred interpreter. - freetype_sys::FT_Set_Default_Properties(library.raw()); - }; - - Ok(FreeTypeRasterizer { - faces: HashMap::new(), - ft_faces: HashMap::new(), - fallback_lists: HashMap::new(), - library, - device_pixel_ratio, - }) - } - - fn metrics(&self, key: FontKey, _size: Size) -> Result { - let face = &mut self.faces.get(&key).ok_or(Error::FontNotLoaded)?; - let full = self.full_metrics(&face)?; - - let height = (full.size_metrics.height / 64) as f64; - let descent = (full.size_metrics.descender / 64) as f32; - - // Get underline position and thickness in device pixels. - let x_scale = full.size_metrics.x_scale as f32 / 65536.0; - let mut underline_position = f32::from(face.ft_face.underline_position()) * x_scale / 64.; - let mut underline_thickness = f32::from(face.ft_face.underline_thickness()) * x_scale / 64.; - - // Fallback for bitmap fonts which do not provide underline metrics. - if underline_position == 0. { - underline_thickness = (descent.abs() / 5.).round(); - underline_position = descent / 2.; - } - - // Get strikeout position and thickness in device pixels. - let (strikeout_position, strikeout_thickness) = - match TrueTypeOS2Table::from_face(&mut (*face.ft_face).clone()) { - Some(os2) => { - let strikeout_position = f32::from(os2.y_strikeout_position()) * x_scale / 64.; - let strikeout_thickness = f32::from(os2.y_strikeout_size()) * x_scale / 64.; - (strikeout_position, strikeout_thickness) - }, - _ => { - // Fallback if font doesn't provide info about strikeout. - trace!("Using fallback strikeout metrics"); - let strikeout_position = height as f32 / 2. + descent; - (strikeout_position, underline_thickness) - }, - }; - - Ok(Metrics { - average_advance: full.cell_width, - line_height: height, - descent, - underline_position, - underline_thickness, - strikeout_position, - strikeout_thickness, - }) - } - - fn load_font(&mut self, desc: &FontDesc, size: Size) -> Result { - self.get_face(desc, size) - } - - fn get_glyph(&mut self, glyph_key: GlyphKey) -> Result { - self.get_rendered_glyph(glyph_key) - } - - fn update_dpr(&mut self, device_pixel_ratio: f32) { - self.device_pixel_ratio = device_pixel_ratio; - } -} - -pub trait IntoFontconfigType { - type FcType; - fn into_fontconfig_type(&self) -> Self::FcType; -} - -impl IntoFontconfigType for Slant { - type FcType = fc::Slant; - - fn into_fontconfig_type(&self) -> Self::FcType { - match *self { - Slant::Normal => fc::Slant::Roman, - Slant::Italic => fc::Slant::Italic, - Slant::Oblique => fc::Slant::Oblique, - } - } -} - -impl IntoFontconfigType for Weight { - type FcType = fc::Weight; - - fn into_fontconfig_type(&self) -> Self::FcType { - match *self { - Weight::Normal => fc::Weight::Regular, - Weight::Bold => fc::Weight::Bold, - } - } -} - -struct FullMetrics { - size_metrics: freetype::ffi::FT_Size_Metrics, - cell_width: f64, -} - -impl FreeTypeRasterizer { - /// Load a font face according to `FontDesc`. - fn get_face(&mut self, desc: &FontDesc, size: Size) -> Result { - // Adjust for DPR. - let size = f64::from(size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.); - - let config = fc::Config::get_current(); - let mut pattern = Pattern::new(); - pattern.add_family(&desc.name); - pattern.add_pixelsize(size); - - // Add style to a pattern. - match desc.style { - Style::Description { slant, weight } => { - // Match nearest font. - pattern.set_weight(weight.into_fontconfig_type()); - pattern.set_slant(slant.into_fontconfig_type()); - }, - Style::Specific(ref style) => { - // If a name was specified, try and load specifically that font. - pattern.add_style(style); - }, - } - - // Hash requested pattern. - let hash = pattern.hash(); - - pattern.config_substitute(config, fc::MatchKind::Pattern); - pattern.default_substitute(); - - // Get font list using pattern. First font is the primary one while the rest are fallbacks. - let matched_fonts = - fc::font_sort(&config, &pattern).ok_or_else(|| Error::MissingFont(desc.to_owned()))?; - let mut matched_fonts = matched_fonts.into_iter(); - - let primary_font = - matched_fonts.next().ok_or_else(|| Error::MissingFont(desc.to_owned()))?; - - // We should render patterns to get values like `pixelsizefixupfactor`. - let primary_font = pattern.render_prepare(config, primary_font); - - // Hash pattern together with request pattern to include requested font size in the hash. - let primary_font_key = FontKey::from_pattern_hashes(hash, primary_font.hash()); - - // Return if we already have the same primary font. - if self.fallback_lists.contains_key(&primary_font_key) { - return Ok(primary_font_key); - } - - // Load font if we haven't loaded it yet. - if !self.faces.contains_key(&primary_font_key) { - self.face_from_pattern(&primary_font, primary_font_key) - .and_then(|pattern| pattern.ok_or_else(|| Error::MissingFont(desc.to_owned())))?; - } - - // Coverage for fallback fonts. - let coverage = CharSet::new(); - let empty_charset = CharSet::new(); - - let list: Vec = matched_fonts - .map(|fallback_font| { - let charset = fallback_font.get_charset().unwrap_or(&empty_charset); - - // Use original pattern to preserve loading flags. - let fallback_font = pattern.render_prepare(config, fallback_font); - let fallback_font_key = FontKey::from_pattern_hashes(hash, fallback_font.hash()); - - let _ = coverage.merge(&charset); - - FallbackFont::new(fallback_font, fallback_font_key) - }) - .collect(); - - self.fallback_lists.insert(primary_font_key, FallbackList { list, coverage }); - - Ok(primary_font_key) - } - - fn full_metrics(&self, face_load_props: &FaceLoadingProperties) -> Result { - let ft_face = &face_load_props.ft_face; - let size_metrics = ft_face.size_metrics().ok_or(Error::MissingSizeMetrics)?; - - let width = match ft_face.load_char('0' as usize, face_load_props.load_flags) { - Ok(_) => ft_face.glyph().metrics().horiAdvance / 64, - Err(_) => size_metrics.max_advance / 64, - } as f64; - - Ok(FullMetrics { size_metrics, cell_width: width }) - } - - fn load_ft_face(&mut self, ft_face_location: FTFaceLocation) -> Result, Error> { - let mut ft_face = self.library.new_face(&ft_face_location.path, ft_face_location.index)?; - if ft_face.has_color() { - unsafe { - // Select the colored bitmap size to use from the array of available sizes. - freetype_sys::FT_Select_Size(ft_face.raw_mut(), 0); - } - } - - let ft_face = Rc::new(ft_face); - self.ft_faces.insert(ft_face_location, Rc::clone(&ft_face)); - - Ok(ft_face) - } - - fn face_from_pattern( - &mut self, - pattern: &PatternRef, - font_key: FontKey, - ) -> Result, Error> { - if let Some(ft_face_location) = pattern.ft_face_location(0) { - if self.faces.get(&font_key).is_some() { - return Ok(Some(font_key)); - } - - trace!("Got font path={:?}, index={:?}", ft_face_location.path, ft_face_location.index); - - let ft_face = match self.ft_faces.get(&ft_face_location) { - Some(ft_face) => Rc::clone(ft_face), - None => self.load_ft_face(ft_face_location)?, - }; - - let non_scalable = if pattern.scalable().next().unwrap_or(true) { - None - } else { - Some(pattern.pixelsize().next().expect("has 1+ pixelsize") as f32) - }; - - let embolden = pattern.embolden().next().unwrap_or(false); - - let matrix = pattern.get_matrix().map(|matrix| { - // Convert Fontconfig matrix to FreeType matrix. - let xx = to_fixedpoint_16_6(matrix.xx); - let xy = to_fixedpoint_16_6(matrix.xy); - let yx = to_fixedpoint_16_6(matrix.yx); - let yy = to_fixedpoint_16_6(matrix.yy); - - Matrix { xx, xy, yx, yy } - }); - - let pixelsize_fixup_factor = pattern.pixelsizefixupfactor().next(); - - let face = FaceLoadingProperties { - load_flags: Self::ft_load_flags(pattern), - render_mode: Self::ft_render_mode(pattern), - lcd_filter: Self::ft_lcd_filter(pattern), - non_scalable, - colored: ft_face.has_color(), - embolden, - matrix, - pixelsize_fixup_factor, - ft_face, - }; - - debug!("Loaded Face {:?}", face); - - self.faces.insert(font_key, face); - - Ok(Some(font_key)) - } else { - Ok(None) - } - } - - fn face_for_glyph(&mut self, glyph_key: GlyphKey) -> Result { - if let Some(face) = self.faces.get(&glyph_key.font_key) { - let index = face.ft_face.get_char_index(glyph_key.c as usize); - - if index != 0 { - return Ok(glyph_key.font_key); - } - } - - Ok(self.load_face_with_glyph(glyph_key).unwrap_or(glyph_key.font_key)) - } - - fn load_face_with_glyph(&mut self, glyph: GlyphKey) -> Result { - let fallback_list = self.fallback_lists.get(&glyph.font_key).unwrap(); - - // Check whether glyph is presented in any fallback font. - if !fallback_list.coverage.has_char(glyph.c) { - return Ok(glyph.font_key); - } - - for fallback_font in &fallback_list.list { - let font_key = fallback_font.key; - let font_pattern = &fallback_font.pattern; - match self.faces.get(&font_key) { - Some(face) => { - let index = face.ft_face.get_char_index(glyph.c as usize); - - // We found something in a current face, so let's use it. - if index != 0 { - return Ok(font_key); - } - }, - None => { - if font_pattern.get_charset().map(|cs| cs.has_char(glyph.c)) != Some(true) { - continue; - } - - let pattern = font_pattern.clone(); - let key = self.face_from_pattern(&pattern, font_key)?.unwrap(); - - return Ok(key); - }, - } - } - - // You can hit this return, if you're failing to get charset from a pattern. - Ok(glyph.font_key) - } - - fn get_rendered_glyph(&mut self, glyph_key: GlyphKey) -> Result { - // Render a normal character if it's not a cursor. - let font_key = self.face_for_glyph(glyph_key)?; - let face = &self.faces[&font_key]; - let index = face.ft_face.get_char_index(glyph_key.c as usize); - let pixelsize = face - .non_scalable - .unwrap_or_else(|| glyph_key.size.as_f32_pts() * self.device_pixel_ratio * 96. / 72.); - - if !face.colored { - face.ft_face.set_char_size(to_freetype_26_6(pixelsize), 0, 0, 0)?; - } - - unsafe { - let ft_lib = self.library.raw(); - freetype::ffi::FT_Library_SetLcdFilter(ft_lib, face.lcd_filter); - } - - face.ft_face.load_glyph(index as u32, face.load_flags)?; - - let glyph = face.ft_face.glyph(); - - // Generate synthetic bold. - if face.embolden { - unsafe { - freetype_sys::FT_GlyphSlot_Embolden(glyph.raw() - as *const freetype_sys::FT_GlyphSlotRec - as *mut freetype_sys::FT_GlyphSlotRec); - } - } - - // Transform glyphs with the matrix from Fontconfig. Primarily used to generate italics. - if let Some(matrix) = face.matrix.as_ref() { - let glyph = face.ft_face.raw().glyph; - - unsafe { - // Check that the glyph is a vectorial outline, not a bitmap. - if (*glyph).format == freetype_sys::FT_GLYPH_FORMAT_OUTLINE { - let outline = &(*glyph).outline; - - freetype_sys::FT_Outline_Transform(outline, matrix); - } - } - } - - glyph.render_glyph(face.render_mode)?; - - let (pixel_height, pixel_width, buf) = Self::normalize_buffer(&glyph.bitmap())?; - - let rasterized_glyph = RasterizedGlyph { - c: glyph_key.c, - top: glyph.bitmap_top(), - left: glyph.bitmap_left(), - width: pixel_width, - height: pixel_height, - buf, - }; - - if face.colored { - let fixup_factor = if let Some(pixelsize_fixup_factor) = face.pixelsize_fixup_factor { - pixelsize_fixup_factor - } else { - // Fallback if user has bitmap scaling disabled. - let metrics = face.ft_face.size_metrics().ok_or(Error::MissingSizeMetrics)?; - f64::from(pixelsize) / f64::from(metrics.y_ppem) - }; - Ok(downsample_bitmap(rasterized_glyph, fixup_factor)) - } else { - Ok(rasterized_glyph) - } - } - - fn ft_load_flags(pattern: &PatternRef) -> LoadFlag { - let antialias = pattern.antialias().next().unwrap_or(true); - let autohint = pattern.autohint().next().unwrap_or(false); - let hinting = pattern.hinting().next().unwrap_or(true); - let rgba = pattern.rgba().next().unwrap_or(fc::Rgba::Unknown); - let embedded_bitmaps = pattern.embeddedbitmap().next().unwrap_or(true); - let scalable = pattern.scalable().next().unwrap_or(true); - let color = pattern.color().next().unwrap_or(false); - - // Disable hinting if so was requested. - let hintstyle = if hinting { - pattern.hintstyle().next().unwrap_or(fc::HintStyle::Full) - } else { - fc::HintStyle::None - }; - - let mut flags = match (antialias, hintstyle, rgba) { - (false, fc::HintStyle::None, _) => LoadFlag::NO_HINTING | LoadFlag::MONOCHROME, - (false, ..) => LoadFlag::TARGET_MONO | LoadFlag::MONOCHROME, - (true, fc::HintStyle::None, _) => LoadFlag::NO_HINTING, - // `hintslight` does *not* use LCD hinting even when a subpixel mode - // is selected. - // - // According to the FreeType docs, - // - // > You can use a hinting algorithm that doesn't correspond to the - // > same rendering mode. As an example, it is possible to use the - // > ‘light’ hinting algorithm and have the results rendered in - // > horizontal LCD pixel mode. - // - // In practice, this means we can have `FT_LOAD_TARGET_LIGHT` with - // subpixel render modes like `FT_RENDER_MODE_LCD`. Libraries like - // cairo take the same approach and consider `hintslight` to always - // prefer `FT_LOAD_TARGET_LIGHT`. - (true, fc::HintStyle::Slight, _) => LoadFlag::TARGET_LIGHT, - (true, fc::HintStyle::Medium, _) => LoadFlag::TARGET_NORMAL, - // If LCD hinting is to be used, must select hintmedium or hintfull, - // have AA enabled, and select a subpixel mode. - (true, fc::HintStyle::Full, fc::Rgba::Rgb) - | (true, fc::HintStyle::Full, fc::Rgba::Bgr) => LoadFlag::TARGET_LCD, - (true, fc::HintStyle::Full, fc::Rgba::Vrgb) - | (true, fc::HintStyle::Full, fc::Rgba::Vbgr) => LoadFlag::TARGET_LCD_V, - // For non-rgba modes with Full hinting, just use the default hinting algorithm. - (true, fc::HintStyle::Full, fc::Rgba::Unknown) - | (true, fc::HintStyle::Full, fc::Rgba::None) => LoadFlag::TARGET_NORMAL, - }; - - // Non scalable fonts only have bitmaps, so disabling them entirely is likely not a - // desirable thing. Colored fonts aren't scalable, but also only have bitmaps. - if !embedded_bitmaps && scalable && !color { - flags |= LoadFlag::NO_BITMAP; - } - - // Use color for colored fonts. - if color { - flags |= LoadFlag::COLOR; - } - - // Force autohint if it was requested. - if autohint { - flags |= LoadFlag::FORCE_AUTOHINT; - } - - flags - } - - fn ft_render_mode(pat: &PatternRef) -> freetype::RenderMode { - let antialias = pat.antialias().next().unwrap_or(true); - let rgba = pat.rgba().next().unwrap_or(fc::Rgba::Unknown); - - match (antialias, rgba) { - (false, _) => freetype::RenderMode::Mono, - (_, fc::Rgba::Rgb) | (_, fc::Rgba::Bgr) => freetype::RenderMode::Lcd, - (_, fc::Rgba::Vrgb) | (_, fc::Rgba::Vbgr) => freetype::RenderMode::LcdV, - (true, _) => freetype::RenderMode::Normal, - } - } - - fn ft_lcd_filter(pat: &PatternRef) -> c_uint { - match pat.lcdfilter().next().unwrap_or(fc::LcdFilter::Default) { - fc::LcdFilter::None => freetype::ffi::FT_LCD_FILTER_NONE, - fc::LcdFilter::Default => freetype::ffi::FT_LCD_FILTER_DEFAULT, - fc::LcdFilter::Light => freetype::ffi::FT_LCD_FILTER_LIGHT, - fc::LcdFilter::Legacy => freetype::ffi::FT_LCD_FILTER_LEGACY, - } - } - - /// Given a FreeType `Bitmap`, returns packed buffer with 1 byte per LCD channel. - /// - /// The i32 value in the return type is the number of pixels per row. - fn normalize_buffer( - bitmap: &freetype::bitmap::Bitmap, - ) -> freetype::FtResult<(i32, i32, BitmapBuffer)> { - use freetype::bitmap::PixelMode; - - let buf = bitmap.buffer(); - let mut packed = Vec::with_capacity((bitmap.rows() * bitmap.width()) as usize); - let pitch = bitmap.pitch().abs() as usize; - match bitmap.pixel_mode()? { - PixelMode::Lcd => { - for i in 0..bitmap.rows() { - let start = (i as usize) * pitch; - let stop = start + bitmap.width() as usize; - packed.extend_from_slice(&buf[start..stop]); - } - Ok((bitmap.rows(), bitmap.width() / 3, BitmapBuffer::RGB(packed))) - }, - PixelMode::LcdV => { - for i in 0..bitmap.rows() / 3 { - for j in 0..bitmap.width() { - for k in 0..3 { - let offset = ((i as usize) * 3 + k) * pitch + (j as usize); - packed.push(buf[offset]); - } - } - } - Ok((bitmap.rows() / 3, bitmap.width(), BitmapBuffer::RGB(packed))) - }, - // Mono data is stored in a packed format using 1 bit per pixel. - PixelMode::Mono => { - fn unpack_byte(res: &mut Vec, byte: u8, mut count: u8) { - // Mono stores MSBit at top of byte - let mut bit = 7; - while count != 0 { - let value = ((byte >> bit) & 1) * 255; - // Push value 3x since result buffer should be 1 byte - // per channel. - res.push(value); - res.push(value); - res.push(value); - count -= 1; - bit -= 1; - } - }; - - for i in 0..(bitmap.rows() as usize) { - let mut columns = bitmap.width(); - let mut byte = 0; - let offset = i * bitmap.pitch().abs() as usize; - while columns != 0 { - let bits = min(8, columns); - unpack_byte(&mut packed, buf[offset + byte], bits as u8); - - columns -= bits; - byte += 1; - } - } - Ok((bitmap.rows(), bitmap.width(), BitmapBuffer::RGB(packed))) - }, - // Gray data is stored as a value between 0 and 255 using 1 byte per pixel. - PixelMode::Gray => { - for i in 0..bitmap.rows() { - let start = (i as usize) * pitch; - let stop = start + bitmap.width() as usize; - for byte in &buf[start..stop] { - packed.push(*byte); - packed.push(*byte); - packed.push(*byte); - } - } - Ok((bitmap.rows(), bitmap.width(), BitmapBuffer::RGB(packed))) - }, - PixelMode::Bgra => { - let buf_size = (bitmap.rows() * bitmap.width() * 4) as usize; - let mut i = 0; - while i < buf_size { - packed.push(buf[i + 2]); - packed.push(buf[i + 1]); - packed.push(buf[i]); - packed.push(buf[i + 3]); - i += 4; - } - Ok((bitmap.rows(), bitmap.width(), BitmapBuffer::RGBA(packed))) - }, - mode => panic!("unhandled pixel mode: {:?}", mode), - } - } -} - -/// Downscale a bitmap by a fixed factor. -/// -/// This will take the `bitmap_glyph` as input and return the glyph's content downscaled by -/// `fixup_factor`. -fn downsample_bitmap(mut bitmap_glyph: RasterizedGlyph, fixup_factor: f64) -> RasterizedGlyph { - // Only scale colored buffers which are bigger than required. - let bitmap_buffer = match (&bitmap_glyph.buf, fixup_factor.partial_cmp(&1.0)) { - (BitmapBuffer::RGBA(buffer), Some(Ordering::Less)) => buffer, - _ => return bitmap_glyph, - }; - - let bitmap_width = bitmap_glyph.width as usize; - let bitmap_height = bitmap_glyph.height as usize; - - let target_width = (bitmap_width as f64 * fixup_factor) as usize; - let target_height = (bitmap_height as f64 * fixup_factor) as usize; - - // Number of pixels in the input buffer, per pixel in the output buffer. - let downsampling_step = 1.0 / fixup_factor; - - let mut downsampled_buffer = Vec::::with_capacity(target_width * target_height * 4); - - for line_index in 0..target_height { - // Get the first and last line which will be consolidated in the current output pixel. - let line_index = line_index as f64; - let source_line_start = (line_index * downsampling_step).round() as usize; - let source_line_end = ((line_index + 1.) * downsampling_step).round() as usize; - - for column_index in 0..target_width { - // Get the first and last column which will be consolidated in the current output - // pixel. - let column_index = column_index as f64; - let source_column_start = (column_index * downsampling_step).round() as usize; - let source_column_end = ((column_index + 1.) * downsampling_step).round() as usize; - - let (mut r, mut g, mut b, mut a) = (0u32, 0u32, 0u32, 0u32); - let mut pixels_picked: u32 = 0; - - // Consolidate all pixels within the source rectangle into a single averaged pixel. - for source_line in source_line_start..source_line_end { - let source_pixel_index = source_line * bitmap_width; - - for source_column in source_column_start..source_column_end { - let offset = (source_pixel_index + source_column) * 4; - r += u32::from(bitmap_buffer[offset]); - g += u32::from(bitmap_buffer[offset + 1]); - b += u32::from(bitmap_buffer[offset + 2]); - a += u32::from(bitmap_buffer[offset + 3]); - pixels_picked += 1; - } - } - - // Add a single pixel to the output buffer for the downscaled source rectangle. - downsampled_buffer.push((r / pixels_picked) as u8); - downsampled_buffer.push((g / pixels_picked) as u8); - downsampled_buffer.push((b / pixels_picked) as u8); - downsampled_buffer.push((a / pixels_picked) as u8); - } - } - - bitmap_glyph.buf = BitmapBuffer::RGBA(downsampled_buffer); - - // Downscale the metrics. - bitmap_glyph.top = (f64::from(bitmap_glyph.top) * fixup_factor) as i32; - bitmap_glyph.left = (f64::from(bitmap_glyph.left) * fixup_factor) as i32; - bitmap_glyph.width = target_width as i32; - bitmap_glyph.height = target_height as i32; - - bitmap_glyph -} - -/// Errors occurring when using the freetype rasterizer. -#[derive(Debug)] -pub enum Error { - /// Error occurred within the FreeType library. - FreeType(freetype::Error), - - /// Couldn't find font matching description. - MissingFont(FontDesc), - - /// Tried to get size metrics from a Face that didn't have a size. - MissingSizeMetrics, - - /// Requested an operation with a FontKey that isn't known to the rasterizer. - FontNotLoaded, -} - -impl std::error::Error for Error { - fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { - match self { - Error::FreeType(err) => err.source(), - _ => None, - } - } -} - -impl Display for Error { - fn fmt(&self, f: &mut Formatter) -> fmt::Result { - match self { - Error::FreeType(err) => err.fmt(f), - Error::MissingFont(err) => write!(f, "Unable to find the font {}", err), - Error::FontNotLoaded => f.write_str("Tried to use a font that hasn't been loaded"), - Error::MissingSizeMetrics => { - f.write_str("Tried to get size metrics from a face without a size") - }, - } - } -} - -impl From for Error { - fn from(val: freetype::Error) -> Error { - Error::FreeType(val) - } -} - -unsafe impl Send for FreeTypeRasterizer {} diff --git a/font/src/lib.rs b/font/src/lib.rs deleted file mode 100644 index 15cd46ff..00000000 --- a/font/src/lib.rs +++ /dev/null @@ -1,226 +0,0 @@ -//! Compatibility layer for different font engines. -//! -//! CoreText is used on Mac OS. -//! FreeType is used on everything that's not Mac OS. -//! Eventually, ClearType support will be available for windows. - -#![deny(clippy::all, clippy::if_not_else, clippy::enum_glob_use, clippy::wrong_pub_self_convention)] - -use std::fmt; -use std::ops::{Add, Mul}; -use std::sync::atomic::{AtomicUsize, Ordering}; - -// If target isn't macos or windows, reexport everything from ft. -#[cfg(not(any(target_os = "macos", windows)))] -pub mod ft; -#[cfg(not(any(target_os = "macos", windows)))] -pub use ft::{Error, FreeTypeRasterizer as Rasterizer}; - -#[cfg(windows)] -pub mod directwrite; -#[cfg(windows)] -pub use directwrite::{DirectWriteRasterizer as Rasterizer, Error}; - -// If target is macos, reexport everything from darwin. -#[cfg(target_os = "macos")] -mod darwin; -#[cfg(target_os = "macos")] -pub use darwin::*; - -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct FontDesc { - name: String, - style: Style, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Slant { - Normal, - Italic, - Oblique, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum Weight { - Normal, - Bold, -} - -/// Style of font. -#[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub enum Style { - Specific(String), - Description { slant: Slant, weight: Weight }, -} - -impl fmt::Display for Style { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Style::Specific(ref s) => f.write_str(&s), - Style::Description { slant, weight } => { - write!(f, "slant={:?}, weight={:?}", slant, weight) - }, - } - } -} - -impl FontDesc { - pub fn new(name: S, style: Style) -> FontDesc - where - S: Into, - { - FontDesc { name: name.into(), style } - } -} - -impl fmt::Display for FontDesc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{} - {}", self.name, self.style) - } -} - -/// Identifier for a Font for use in maps/etc. -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] -pub struct FontKey { - token: u32, -} - -impl FontKey { - /// Get next font key for given size. - /// - /// The generated key will be globally unique. - pub fn next() -> FontKey { - static TOKEN: AtomicUsize = AtomicUsize::new(0); - - FontKey { token: TOKEN.fetch_add(1, Ordering::SeqCst) as _ } - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct GlyphKey { - pub c: char, - pub font_key: FontKey, - pub size: Size, -} - -/// Font size stored as integer. -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] -pub struct Size(i16); - -impl Size { - /// Create a new `Size` from a f32 size in points. - pub fn new(size: f32) -> Size { - Size((size * Size::factor()) as i16) - } - - /// Scale factor between font "Size" type and point size. - #[inline] - pub fn factor() -> f32 { - 2.0 - } - - /// Get the f32 size in points. - pub fn as_f32_pts(self) -> f32 { - f32::from(self.0) / Size::factor() - } -} - -impl> Add for Size { - type Output = Size; - - fn add(self, other: T) -> Size { - Size(self.0.saturating_add(other.into().0)) - } -} - -impl> Mul for Size { - type Output = Size; - - fn mul(self, other: T) -> Size { - Size(self.0 * other.into().0) - } -} - -impl From for Size { - fn from(float: f32) -> Size { - Size::new(float) - } -} - -#[derive(Clone)] -pub struct RasterizedGlyph { - pub c: char, - pub width: i32, - pub height: i32, - pub top: i32, - pub left: i32, - pub buf: BitmapBuffer, -} - -#[derive(Clone, Debug)] -pub enum BitmapBuffer { - /// RGB alphamask. - RGB(Vec), - - /// RGBA pixels with premultiplied alpha. - RGBA(Vec), -} - -impl Default for RasterizedGlyph { - fn default() -> RasterizedGlyph { - RasterizedGlyph { - c: ' ', - width: 0, - height: 0, - top: 0, - left: 0, - buf: BitmapBuffer::RGB(Vec::new()), - } - } -} - -impl fmt::Debug for RasterizedGlyph { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("RasterizedGlyph") - .field("c", &self.c) - .field("width", &self.width) - .field("height", &self.height) - .field("top", &self.top) - .field("left", &self.left) - .field("buf", &self.buf) - .finish() - } -} - -#[derive(Copy, Clone)] -pub struct Metrics { - pub average_advance: f64, - pub line_height: f64, - pub descent: f32, - pub underline_position: f32, - pub underline_thickness: f32, - pub strikeout_position: f32, - pub strikeout_thickness: f32, -} - -pub trait Rasterize { - /// Errors occurring in Rasterize methods. - type Err: ::std::error::Error + Send + Sync + 'static; - - /// Create a new Rasterizer. - fn new(device_pixel_ratio: f32, use_thin_strokes: bool) -> Result - where - Self: Sized; - - /// Get `Metrics` for the given `FontKey`. - fn metrics(&self, _: FontKey, _: Size) -> Result; - - /// Load the font described by `FontDesc` and `Size`. - fn load_font(&mut self, _: &FontDesc, _: Size) -> Result; - - /// Rasterize the glyph described by `GlyphKey`.. - fn get_glyph(&mut self, _: GlyphKey) -> Result; - - /// Update the Rasterizer's DPI factor. - fn update_dpr(&mut self, device_pixel_ratio: f32); -}