mirror of
https://github.com/alacritty/alacritty.git
synced 2025-02-10 15:46:10 -05:00
Add Windows font fallback
This commit is contained in:
parent
33abfe34a8
commit
840cb1b93a
4 changed files with 253 additions and 101 deletions
|
@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
||||||
- Vi mode for copying text and opening links
|
- Vi mode for copying text and opening links
|
||||||
- `CopySelection` action which copies into selection buffer on Linux/BSD
|
- `CopySelection` action which copies into selection buffer on Linux/BSD
|
||||||
- Option `cursor.thickness` to set terminal cursor thickness
|
- Option `cursor.thickness` to set terminal cursor thickness
|
||||||
|
- Font fallback on Windows
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
|
|
17
Cargo.lock
generated
17
Cargo.lock
generated
|
@ -465,7 +465,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dwrote"
|
name = "dwrote"
|
||||||
version = "0.9.0"
|
version = "0.11.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -473,6 +473,7 @@ dependencies = [
|
||||||
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
"serde_derive 1.0.104 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"wio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -548,13 +549,14 @@ dependencies = [
|
||||||
"core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
"core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"core-text 13.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"core-text 13.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"dwrote 0.9.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.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
"euclid 0.20.7 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"foreign-types 0.5.0 (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.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"freetype-rs 0.24.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
"libc 0.2.67 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"log 0.4.8 (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.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"servo-fontconfig 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2123,6 +2125,14 @@ dependencies = [
|
||||||
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "wio"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"winapi 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ws2_32-sys"
|
name = "ws2_32-sys"
|
||||||
version = "0.2.1"
|
version = "0.2.1"
|
||||||
|
@ -2239,7 +2249,7 @@ dependencies = [
|
||||||
"checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a"
|
"checksum dlib 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "77e51249a9d823a4cb79e3eca6dcd756153e8ed0157b6c04775d04bf1b13b76a"
|
||||||
"checksum downcast-rs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52ba6eb47c2131e784a38b726eb54c1e1484904f013e576a25354d0124161af6"
|
"checksum downcast-rs 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "52ba6eb47c2131e784a38b726eb54c1e1484904f013e576a25354d0124161af6"
|
||||||
"checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
|
"checksum dtoa 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4358a9e11b9a09cf52383b451b49a169e8d797b68aa02301ff586d70d9661ea3"
|
||||||
"checksum dwrote 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "0bd1369e02db5e9b842a9b67bce8a2fcc043beafb2ae8a799dd482d46ea1ff0d"
|
"checksum dwrote 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "439a1c2ba5611ad3ed731280541d36d2e9c4ac5e7fb818a27b604bdc5a6aa65b"
|
||||||
"checksum embed-resource 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bbaba4684ab0af1cbb3ef0b1f540ddc4b57b31940c920ea594efe09ab86e2a6c"
|
"checksum embed-resource 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bbaba4684ab0af1cbb3ef0b1f540ddc4b57b31940c920ea594efe09ab86e2a6c"
|
||||||
"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
"checksum env_logger 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "44533bbbb3bb3c1fa17d9f2e4e38bbbaf8396ba82193c4cb1b6445d711445d36"
|
||||||
"checksum euclid 0.20.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3f852d320142e1cceb15dccef32ed72a9970b83109d8a4e24b1ab04d579f485d"
|
"checksum euclid 0.20.7 (registry+https://github.com/rust-lang/crates.io-index)" = "3f852d320142e1cceb15dccef32ed72a9970b83109d8a4e24b1ab04d579f485d"
|
||||||
|
@ -2431,6 +2441,7 @@ dependencies = [
|
||||||
"checksum winpty 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92c5a39bb2408a307dd5ab774039ec3f2c68d052970ae4dacc346101abe202b2"
|
"checksum winpty 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "92c5a39bb2408a307dd5ab774039ec3f2c68d052970ae4dacc346101abe202b2"
|
||||||
"checksum winpty-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8a179a59760dc51d30b5e6eaf1bd6da88461f72f2616e962ddebef7e413210"
|
"checksum winpty-sys 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ed8a179a59760dc51d30b5e6eaf1bd6da88461f72f2616e962ddebef7e413210"
|
||||||
"checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
|
"checksum winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "b2986deb581c4fe11b621998a5e53361efe6b48a151178d0cd9eeffa4dc6acc9"
|
||||||
|
"checksum wio 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "5d129932f4644ac2396cb456385cbf9e63b5b30c6e8dc4820bdca4eb082037a5"
|
||||||
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
"checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e"
|
||||||
"checksum x11-clipboard 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e5e937afd03b64b7be4f959cc044e09260a47241b71e56933f37db097bf7859d"
|
"checksum x11-clipboard 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "e5e937afd03b64b7be4f959cc044e09260a47241b71e56933f37db097bf7859d"
|
||||||
"checksum x11-dl 2.18.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8"
|
"checksum x11-dl 2.18.5 (registry+https://github.com/rust-lang/crates.io-index)" = "2bf981e3a5b3301209754218f962052d4d9ee97e478f4d26d4a6eced34c1fef8"
|
||||||
|
|
|
@ -23,4 +23,5 @@ core-graphics = "0.17"
|
||||||
core-foundation-sys = "0.6"
|
core-foundation-sys = "0.6"
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
dwrote = { version = "0.9.0" }
|
dwrote = { version = "0.11" }
|
||||||
|
winapi = { version = "0.3", features = ["impl-default"] }
|
||||||
|
|
|
@ -13,30 +13,164 @@
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
//
|
//
|
||||||
//! Rasterization powered by DirectWrite
|
//! Rasterization powered by DirectWrite
|
||||||
use dwrote::{FontCollection, FontStretch, FontStyle, FontWeight, GlyphOffset, GlyphRunAnalysis};
|
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::{
|
use super::{
|
||||||
BitmapBuffer, FontDesc, FontKey, GlyphKey, Metrics, RasterizedGlyph, Size, Slant, Style, Weight,
|
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 {
|
pub struct DirectWriteRasterizer {
|
||||||
fonts: Vec<dwrote::FontFace>,
|
fonts: HashMap<FontKey, Font>,
|
||||||
|
keys: HashMap<FontDesc, FontKey>,
|
||||||
device_pixel_ratio: f32,
|
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 {
|
impl crate::Rasterize for DirectWriteRasterizer {
|
||||||
type Err = Error;
|
type Err = Error;
|
||||||
|
|
||||||
fn new(device_pixel_ratio: f32, _: bool) -> Result<DirectWriteRasterizer, Error> {
|
fn new(device_pixel_ratio: f32, _: bool) -> Result<DirectWriteRasterizer, Error> {
|
||||||
Ok(DirectWriteRasterizer { fonts: Vec::new(), device_pixel_ratio })
|
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> {
|
fn metrics(&self, key: FontKey, size: Size) -> Result<Metrics, Error> {
|
||||||
let font = self.fonts.get(key.token as usize).ok_or(Error::FontNotLoaded)?;
|
let face = &self.get_loaded_font(key)?.face;
|
||||||
|
let vmetrics = face.metrics().metrics0();
|
||||||
|
|
||||||
let vmetrics = font.metrics();
|
let scale = em_size(size) * self.device_pixel_ratio / f32::from(vmetrics.designUnitsPerEm);
|
||||||
let scale = (size.as_f32_pts() * self.device_pixel_ratio * (96.0 / 72.0))
|
|
||||||
/ f32::from(vmetrics.designUnitsPerEm);
|
|
||||||
|
|
||||||
let underline_position = f32::from(vmetrics.underlinePosition) * scale;
|
let underline_position = f32::from(vmetrics.underlinePosition) * scale;
|
||||||
let underline_thickness = f32::from(vmetrics.underlineThickness) * scale;
|
let underline_thickness = f32::from(vmetrics.underlineThickness) * scale;
|
||||||
|
@ -50,10 +184,12 @@ impl crate::Rasterize for DirectWriteRasterizer {
|
||||||
|
|
||||||
let line_height = f64::from(ascent - descent + line_gap);
|
let line_height = f64::from(ascent - descent + line_gap);
|
||||||
|
|
||||||
// We assume that all monospace characters have the same width
|
// Since all monospace characters have the same width, we use `!` for horizontal metrics
|
||||||
// Because of this we take '!', the first drawable character, for measurements
|
let c = '!';
|
||||||
let glyph_metrics = font.get_design_glyph_metrics(&[33], false);
|
let glyph_index = self.get_glyph_index(face, c)?;
|
||||||
let hmetrics = glyph_metrics.first().ok_or(Error::MissingGlyph('!'))?;
|
|
||||||
|
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);
|
let average_advance = f64::from(hmetrics.advanceWidth) * f64::from(scale);
|
||||||
|
|
||||||
|
@ -69,27 +205,22 @@ impl crate::Rasterize for DirectWriteRasterizer {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> {
|
fn load_font(&mut self, desc: &FontDesc, _size: Size) -> Result<FontKey, Error> {
|
||||||
let system_fc = FontCollection::system();
|
// Fast path if face is already loaded
|
||||||
|
if let Some(key) = self.keys.get(desc) {
|
||||||
|
return Ok(*key);
|
||||||
|
}
|
||||||
|
|
||||||
let family = system_fc
|
let family = self
|
||||||
|
.available_fonts
|
||||||
.get_font_family_by_name(&desc.name)
|
.get_font_family_by_name(&desc.name)
|
||||||
.ok_or_else(|| Error::MissingFont(desc.clone()))?;
|
.ok_or_else(|| Error::MissingFont(desc.clone()))?;
|
||||||
|
|
||||||
let font = match desc.style {
|
let font = match desc.style {
|
||||||
Style::Description { weight, slant } => {
|
Style::Description { weight, slant } => {
|
||||||
let weight =
|
|
||||||
if weight == Weight::Bold { FontWeight::Bold } else { FontWeight::Regular };
|
|
||||||
|
|
||||||
let style = match slant {
|
|
||||||
Slant::Normal => FontStyle::Normal,
|
|
||||||
Slant::Oblique => FontStyle::Oblique,
|
|
||||||
Slant::Italic => FontStyle::Italic,
|
|
||||||
};
|
|
||||||
|
|
||||||
// This searches for the "best" font - should mean we don't have to worry about
|
// 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
|
// fallbacks if our exact desired weight/style isn't available
|
||||||
Ok(family.get_first_matching_font(weight, FontStretch::Normal, style))
|
Ok(family.get_first_matching_font(weight.into(), FontStretch::Normal, slant.into()))
|
||||||
},
|
}
|
||||||
Style::Specific(ref style) => {
|
Style::Specific(ref style) => {
|
||||||
let mut idx = 0;
|
let mut idx = 0;
|
||||||
let count = family.get_font_count();
|
let count = family.get_font_count();
|
||||||
|
@ -107,73 +238,26 @@ impl crate::Rasterize for DirectWriteRasterizer {
|
||||||
|
|
||||||
idx += 1;
|
idx += 1;
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
let face = font.create_font_face();
|
let key = FontKey::next();
|
||||||
self.fonts.push(face);
|
self.keys.insert(desc.clone(), key);
|
||||||
|
self.fonts.insert(key, font.into());
|
||||||
|
|
||||||
Ok(FontKey { token: (self.fonts.len() - 1) as u32 })
|
Ok(key)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_glyph(&mut self, glyph: GlyphKey) -> Result<RasterizedGlyph, Error> {
|
fn get_glyph(&mut self, glyph: GlyphKey) -> Result<RasterizedGlyph, Error> {
|
||||||
let font = self.fonts.get(glyph.font_key.token as usize).ok_or(Error::FontNotLoaded)?;
|
let loaded_font = self.get_loaded_font(glyph.font_key)?;
|
||||||
|
|
||||||
let offset = GlyphOffset { advanceOffset: 0.0, ascenderOffset: 0.0 };
|
match self.rasterize_glyph(&loaded_font.face, glyph.size, glyph.c) {
|
||||||
|
Err(err @ Error::MissingGlyph(_)) => {
|
||||||
let glyph_index = *font
|
let fallback_font = self.get_fallback_font(&loaded_font, glyph.c).ok_or(err)?;
|
||||||
.get_glyph_indices(&[glyph.c as u32])
|
self.rasterize_glyph(&fallback_font.create_font_face(), glyph.size, glyph.c)
|
||||||
.first()
|
},
|
||||||
.ok_or_else(|| Error::MissingGlyph(glyph.c))?;
|
result => result,
|
||||||
if glyph_index == 0 {
|
|
||||||
// The DirectWrite documentation states that we should get 0 returned if the glyph
|
|
||||||
// does not exist in the font
|
|
||||||
return Err(Error::MissingGlyph(glyph.c));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let glyph_run = dwrote::DWRITE_GLYPH_RUN {
|
|
||||||
fontFace: unsafe { font.as_ptr() },
|
|
||||||
fontEmSize: glyph.size.as_f32_pts(),
|
|
||||||
glyphCount: 1,
|
|
||||||
glyphIndices: &(glyph_index),
|
|
||||||
glyphAdvances: &(0.0),
|
|
||||||
glyphOffsets: &(offset),
|
|
||||||
isSideways: 0,
|
|
||||||
bidiLevel: 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
let rendering_mode = font.get_recommended_rendering_mode_default_params(
|
|
||||||
glyph.size.as_f32_pts(),
|
|
||||||
self.device_pixel_ratio * (96.0 / 72.0),
|
|
||||||
dwrote::DWRITE_MEASURING_MODE_NATURAL,
|
|
||||||
);
|
|
||||||
|
|
||||||
let glyph_analysis = GlyphRunAnalysis::create(
|
|
||||||
&glyph_run,
|
|
||||||
self.device_pixel_ratio * (96.0 / 72.0),
|
|
||||||
None,
|
|
||||||
rendering_mode,
|
|
||||||
dwrote::DWRITE_MEASURING_MODE_NATURAL,
|
|
||||||
0.0,
|
|
||||||
0.0,
|
|
||||||
)
|
|
||||||
.or_else(|_| Err(Error::MissingGlyph(glyph.c)))?;
|
|
||||||
|
|
||||||
let bounds = glyph_analysis
|
|
||||||
.get_alpha_texture_bounds(dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1)
|
|
||||||
.or_else(|_| Err(Error::MissingGlyph(glyph.c)))?;
|
|
||||||
let buf = glyph_analysis
|
|
||||||
.create_alpha_texture(dwrote::DWRITE_TEXTURE_CLEARTYPE_3x1, bounds)
|
|
||||||
.or_else(|_| Err(Error::MissingGlyph(glyph.c)))?;
|
|
||||||
|
|
||||||
Ok(RasterizedGlyph {
|
|
||||||
c: glyph.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 update_dpr(&mut self, device_pixel_ratio: f32) {
|
fn update_dpr(&mut self, device_pixel_ratio: f32) {
|
||||||
|
@ -186,29 +270,84 @@ pub enum Error {
|
||||||
MissingFont(FontDesc),
|
MissingFont(FontDesc),
|
||||||
MissingGlyph(char),
|
MissingGlyph(char),
|
||||||
FontNotLoaded,
|
FontNotLoaded,
|
||||||
|
DirectWriteError(HRESULT),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ::std::error::Error for Error {
|
impl std::error::Error for Error {}
|
||||||
fn description(&self) -> &str {
|
|
||||||
match *self {
|
|
||||||
Error::MissingFont(ref _desc) => "Couldn't find the requested font",
|
|
||||||
Error::MissingGlyph(ref _c) => "Couldn't find the requested glyph",
|
|
||||||
Error::FontNotLoaded => "Tried to operate on font that hasn't been loaded",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ::std::fmt::Display for Error {
|
impl Display for Error {
|
||||||
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
|
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
||||||
match *self {
|
match self {
|
||||||
Error::MissingGlyph(ref c) => write!(f, "Glyph not found for char {:?}", c),
|
Error::MissingGlyph(c) => write!(f, "Glyph not found for char {:?}", c),
|
||||||
Error::MissingFont(ref desc) => write!(
|
Error::MissingFont(desc) => write!(
|
||||||
f,
|
f,
|
||||||
"Couldn't find a font with {}\n\tPlease check the font config in your \
|
"Couldn't find a font with {}\n\tPlease check the font config in your \
|
||||||
alacritty.yml.",
|
alacritty.yml.",
|
||||||
desc
|
desc
|
||||||
),
|
),
|
||||||
Error::FontNotLoaded => f.write_str("Tried to use a font that hasn't been loaded"),
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue