summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock42
-rw-r--r--Cargo.toml1
-rw-r--r--alacritty/Cargo.toml2
-rw-r--r--alacritty/src/config/font.rs2
-rw-r--r--alacritty/src/cursor.rs4
-rw-r--r--alacritty/src/display.rs20
-rw-r--r--alacritty/src/event.rs4
-rw-r--r--alacritty/src/renderer/mod.rs34
-rw-r--r--alacritty/src/renderer/rects.rs2
-rw-r--r--alacritty/src/url.rs3
-rw-r--r--alacritty/src/window.rs6
-rw-r--r--font/Cargo.toml31
-rw-r--r--font/src/darwin/byte_order.rs57
-rw-r--r--font/src/darwin/mod.rs634
-rw-r--r--font/src/directwrite/mod.rs335
-rw-r--r--font/src/ft/fc/char_set.rs70
-rw-r--r--font/src/ft/fc/config.rs29
-rw-r--r--font/src/ft/fc/font_set.rs87
-rw-r--r--font/src/ft/fc/mod.rs337
-rw-r--r--font/src/ft/fc/object_set.rs48
-rw-r--r--font/src/ft/fc/pattern.rs627
-rw-r--r--font/src/ft/mod.rs787
-rw-r--r--font/src/lib.rs226
23 files changed, 58 insertions, 3330 deletions
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)",
@@ -414,6 +414,26 @@ dependencies = [
]
[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -553,25 +573,6 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -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<window::Error> for Error {
}
}
-impl From<font::Error> for Error {
- fn from(val: font::Error) -> Self {
+impl From<crossfont::Error> 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<i8>,
/// 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<GlyphCache, font::Error>
+ ) -> Result<GlyphCache, crossfont::Error>
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<FontKey, font::Error> {
+ ) -> Result<FontKey, crossfont::Error> {
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<font::Metrics, font::Error> {
- let mut rasterizer = font::Rasterizer::new(dpr as f32, font.use_thin_strokes())?;
+ pub fn static_metrics(font: Font, dpr: f64) -> Result<crossfont::Metrics, crossfont::Error> {
+ 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, &regular_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<glutin::ContextError> for Error {
}
}
-impl From<font::Error> for Error {
- fn from(val: font::Error) -> Self {
+impl From<crossfont::Error> 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 <contact@christianduerr.com>", "Joe Wilm <joe@jwilm.com>"]
-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<u8> {
- 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<u8>) -> Vec<u8> {
- bytes
-}
-
-#[cfg(target_endian = "little")]
-pub fn extract_rgb(bytes: &[u8]) -> Vec<u8> {
- 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<u8>) -> Vec<u8> {
- bytes
- .into_iter()
- .enumerate()
- .filter(|&(index, _)| ((index) % 4) != 0)
- .map(|(_, val)| val)
- .collect::<Vec<_>>()
-}
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<FontKey, Font>,
- 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<Rasterizer, Error> {
- 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<Metrics, Error> {
- let font = self.fonts.get(&key).ok_or(Error::FontNotLoaded)?;
-
- Ok(font.metrics())
- }
-
- fn load_font(&mut self, desc: &FontDesc, size: Size) -> Result<FontKey, Error> {
- 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<RasterizedGlyph, Error> {
- // 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<Font, Error> {
- 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<Font, Error> {
- 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<Font, Error> {
- 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<Result<RasterizedGlyph, Error>> {
- 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<Font>,
-}
-
-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<String> {
- // 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<Descriptor> {
- // Convert language type &Vec<String> -> CFArray.
- let langarr: CFArray<CFString> = {
- let tmp: Vec<CFString> =
- 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<Descriptor>.
- list.into_iter().map(|fontdesc| Descriptor::new(fontdesc.clone())).collect()
-}
-
-/// Get descriptors for family name.
-pub fn descriptors_for_family(family: &str) -> Vec<Descriptor> {
- 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::<Vec<_>>();
-
- // 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<f64, ()> {
- 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<RasterizedGlyph, Error> {
- 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<u32> {
- // 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<u32> {
- // 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::<Vec<_>>();
-
- 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<FontKey, Font>,
- keys: HashMap<FontDesc, FontKey>,
- device_pixel_ratio: f32,
- available_fonts: FontCollection,
- fallback_sequence: Option<FontFallback>,
-}
-
-impl DirectWriteRasterizer {
- fn rasterize_glyph(
- &self,
- face: &FontFace,
- size: Size,
- c: char,
- ) -> Result<RasterizedGlyph, Error> {
- 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<u16, Error> {
- 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<dwrote::Font> {
- 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<DirectWriteRasterizer, Error> {
- 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<Metrics, Error> {
- 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<FontKey, Error> {
- // 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<RasterizedGlyph, Error> {
- 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<dwrote::Font> 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<Weight> for FontWeight {
- fn from(weight: Weight) -> FontWeight {
- match weight {
- Weight::Bold => FontWeight::Bold,
- Weight::Normal => FontWeight::Regular,
- }
- }
-}
-
-impl From<Slant> 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<str>, 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<bool, ()> {
- 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<Self::Item> {
- 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<Pattern> {
- 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<FontSet> {
- 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<FontSet> {
- 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<isize> 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<isize> 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<bool> {
- 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<isize> {
- 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<Rgba> {
- 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<HintStyle> {
- 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<LcdFilter> {
- 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<f64> {
- 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<Self::Item> {
- let res = self.get_value(self.index);
- self.index += 1;
- res
- }
-
- #[inline]
- fn nth(&mut self, n: usize) -> Option<Self::Item> {
- 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<Self::Item> {
- 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::Item> {
- 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<Width> {
- 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<FcMatrix> {
- 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<PathBuf> {
- unsafe { self.get_string(b"file\0").nth(index) }.map(From::from)
- }
-
- pub fn ft_face_location(&self, index: usize) -> Option<FTFaceLocation> {
- 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<FallbackFont>,
- coverage: CharSet,
-}
-
-struct FaceLoadingProperties {
- load_flags: LoadFlag,
- render_mode: freetype::RenderMode,
- lcd_filter: c_uint,
- non_scalable: Option<f32>,
- colored: bool,
- embolden: bool,
- matrix: Option<Matrix>,
- pixelsize_fixup_factor: Option<f64>,
- ft_face: Rc<FTFace>,
-}
-
-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<FontKey, FaceLoadingProperties>,
- ft_faces: HashMap<FTFaceLocation, Rc<FTFace>>,
- fallback_lists: HashMap<FontKey, FallbackList>,
- 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<FreeTypeRasterizer, Error> {
- 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<Metrics, Error> {
- 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<FontKey, Error> {
- self.get_face(desc, size)
- }
-
- fn get_glyph(&mut self, glyph_key: GlyphKey) -> Result<RasterizedGlyph, Error> {
- 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<FontKey, Error> {
- // 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<FallbackFont> = 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<FullMetrics, Error> {
- 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<Rc<FTFace>, 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<Option<FontKey>, 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<FontKey, Error> {
- 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<FontKey, Error> {
- 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<RasterizedGlyph, Error> {
- // 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<u8>, 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::<u8>::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<freetype::Error> 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<S>(name: S, style: Style) -> FontDesc
- where
- S: Into<String>,
- {
- 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<T: Into<Size>> Add<T> for Size {
- type Output = Size;
-
- fn add(self, other: T) -> Size {
- Size(self.0.saturating_add(other.into().0))
- }
-}
-
-impl<T: Into<Size>> Mul<T> for Size {
- type Output = Size;
-
- fn mul(self, other: T) -> Size {
- Size(self.0 * other.into().0)
- }
-}
-
-impl From<f32> 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<u8>),
-
- /// RGBA pixels with premultiplied alpha.
- RGBA(Vec<u8>),
-}
-
-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<Self, Self::Err>
- where
- Self: Sized;
-
- /// Get `Metrics` for the given `FontKey`.
- fn metrics(&self, _: FontKey, _: Size) -> Result<Metrics, Self::Err>;
-
- /// Load the font described by `FontDesc` and `Size`.
- fn load_font(&mut self, _: &FontDesc, _: Size) -> Result<FontKey, Self::Err>;
-
- /// Rasterize the glyph described by `GlyphKey`..
- fn get_glyph(&mut self, _: GlyphKey) -> Result<RasterizedGlyph, Self::Err>;
-
- /// Update the Rasterizer's DPI factor.
- fn update_dpr(&mut self, device_pixel_ratio: f32);
-}