1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-10 15:04:32 +03:00

wezterm-font: remove font-loader dep on windows

There are a number of cases where font-loader might panic on windows,
and the optional font-loader dep causes problems with `cargo vendor`
in #337, so this is a step to removing that dep.

This commit makes direct GDI calls to enumerate monospace truetype
fonts from the system and then applies our normal matching on the
result.
This commit is contained in:
Wez Furlong 2020-11-22 10:27:48 -08:00
parent 9bebc811d0
commit 9ca428c4e1
4 changed files with 160 additions and 12 deletions

View File

@ -225,6 +225,8 @@ impl_lua_conversion!(StyleRule);
pub enum FontLocatorSelection {
/// Use fontconfig APIs to resolve fonts (!macos, posix systems)
FontConfig,
/// Use the EnumFontFamilies call on win32 systems
EnumFontFamilies,
/// Use the fontloader crate to use a system specific method of
/// resolving fonts
FontLoader,
@ -238,10 +240,12 @@ lazy_static::lazy_static! {
impl Default for FontLocatorSelection {
fn default() -> Self {
if cfg!(all(unix, not(target_os = "macos"))) {
FontLocatorSelection::FontConfig
} else {
if cfg!(windows) {
FontLocatorSelection::EnumFontFamilies
} else if cfg!(target_os = "macos") {
FontLocatorSelection::FontLoader
} else {
FontLocatorSelection::FontConfig
}
}
}
@ -258,7 +262,12 @@ impl FontLocatorSelection {
}
pub fn variants() -> Vec<&'static str> {
vec!["FontConfig", "FontLoader", "ConfigDirsOnly"]
vec![
"FontConfig",
"FontLoader",
"ConfigDirsOnly",
"EnumFontFamilies",
]
}
}
@ -269,6 +278,7 @@ impl std::str::FromStr for FontLocatorSelection {
"fontconfig" => Ok(Self::FontConfig),
"fontloader" => Ok(Self::FontLoader),
"configdirsonly" => Ok(Self::ConfigDirsOnly),
"enumfontfamilies" => Ok(Self::EnumFontFamilies),
_ => Err(anyhow!(
"{} is not a valid FontLocatorSelection variant, possible values are {:?}",
s,

View File

@ -29,11 +29,6 @@ window = { path = "../window" }
[target.'cfg(any(target_os = "android", all(unix, not(target_os = "macos"))))'.dependencies]
fontconfig = { path = "../deps/fontconfig" }
# on linux, font-loader pulls in servo-font* crates which conflict with
# our newer font related deps, so we avoid it on linux
[target.'cfg(any(windows, target_os = "macos"))'.dependencies]
font-loader = { version = "0.8" }
[target."cfg(windows)".dependencies]
dwrote = "0.9"
winapi = "0.3"
@ -41,3 +36,6 @@ winapi = "0.3"
[target.'cfg(target_os = "macos")'.dependencies]
core-foundation = "0.7"
core-text = "15.0"
# on linux, font-loader pulls in servo-font* crates which conflict with
# our newer font related deps, so we avoid it on linux
font-loader = { version = "0.8" }

View File

@ -0,0 +1,133 @@
#![cfg(windows)]
use crate::locator::{FontDataHandle, FontLocator};
use config::FontAttributes;
use std::collections::HashSet;
use std::ffi::OsString;
use std::os::windows::ffi::OsStringExt;
use winapi::ctypes::c_int;
use winapi::shared::minwindef::{DWORD, LPARAM};
use winapi::um::wingdi::{
CreateCompatibleDC, CreateFontIndirectW, DeleteDC, EnumFontFamiliesExW, GetFontData,
SelectObject, ENUMLOGFONTEXW, FIXED_PITCH, GDI_ERROR, LF_FULLFACESIZE, LOGFONTW,
OUT_TT_ONLY_PRECIS, TEXTMETRICW, TRUETYPE_FONTTYPE,
};
/// A FontLocator implemented using the system font loading
/// functions provided by the font-loader crate.
pub struct EnumFontFamiliesFontLocator {}
struct Entry {
log_font: ENUMLOGFONTEXW,
name: String,
}
impl Entry {
fn locator(&self) -> anyhow::Result<FontDataHandle> {
unsafe {
let hdc = CreateCompatibleDC(std::ptr::null_mut());
let font = CreateFontIndirectW(&self.log_font.elfLogFont);
SelectObject(hdc, font as *mut _);
let size = GetFontData(hdc, 0, 0, std::ptr::null_mut(), 0);
let result = match size {
_ if size > 0 && size != GDI_ERROR => {
let mut data = vec![0u8; size as usize];
GetFontData(hdc, 0, 0, data.as_mut_ptr() as *mut _, size);
Ok(FontDataHandle::Memory {
data,
index: 0,
name: self.name.clone(),
})
}
_ => Err(anyhow::anyhow!("Failed to get font data")),
};
DeleteDC(hdc);
result
}
}
}
#[allow(non_snake_case)]
unsafe extern "system" fn callback(
lpelfe: *const LOGFONTW,
_: *const TEXTMETRICW,
fonttype: DWORD,
lparam: LPARAM,
) -> c_int {
let log_font: &ENUMLOGFONTEXW = &*(lpelfe as *const ENUMLOGFONTEXW);
if fonttype == TRUETYPE_FONTTYPE && log_font.elfFullName[0] != b'@' as u16 {
let fonts = lparam as *mut Vec<Entry>;
let len = log_font
.elfFullName
.iter()
.position(|&c| c == 0)
.unwrap_or(LF_FULLFACESIZE);
if let Ok(name) = OsString::from_wide(&log_font.elfFullName[0..len]).into_string() {
(*fonts).push(Entry {
log_font: *log_font,
name,
});
}
}
1 // continue enumeration
}
impl FontLocator for EnumFontFamiliesFontLocator {
fn load_fonts(
&self,
fonts_selection: &[FontAttributes],
loaded: &mut HashSet<FontAttributes>,
) -> anyhow::Result<Vec<FontDataHandle>> {
let mut log_font = LOGFONTW {
lfHeight: 0,
lfWidth: 0,
lfEscapement: 0,
lfOrientation: 0,
lfWeight: 0,
lfItalic: 0,
lfUnderline: 0,
lfStrikeOut: 0,
lfCharSet: 0,
lfOutPrecision: OUT_TT_ONLY_PRECIS as u8,
lfClipPrecision: 0,
lfQuality: 0,
lfPitchAndFamily: FIXED_PITCH as u8,
lfFaceName: [0u16; 32],
};
let mut sys_fonts: Vec<Entry> = vec![];
unsafe {
let hdc = CreateCompatibleDC(std::ptr::null_mut());
EnumFontFamiliesExW(
hdc,
&mut log_font,
Some(callback),
&mut sys_fonts as *mut _ as LPARAM,
0,
);
DeleteDC(hdc);
}
let mut handles = vec![];
for font in sys_fonts {
if let Ok(handle) = font.locator() {
if let Ok(parsed) = crate::parser::ParsedFont::from_locator(&handle) {
handles.push((handle, parsed));
}
}
}
let mut fonts = Vec::new();
for font_attr in fonts_selection {
for (handle, parsed) in &handles {
if crate::parser::font_info_matches(font_attr, parsed.names()) {
fonts.push(handle.clone());
loaded.insert(font_attr.clone());
}
}
}
Ok(fonts)
}
}

View File

@ -2,9 +2,10 @@ use config::FontAttributes;
use std::collections::HashSet;
use std::path::PathBuf;
pub mod enum_font_families;
#[cfg(all(unix, not(target_os = "macos")))]
pub mod font_config;
#[cfg(any(target_os = "macos", windows))]
#[cfg(target_os = "macos")]
pub mod font_loader;
/// Represents the data behind a font.
@ -64,11 +65,17 @@ pub fn new_locator(locator: FontLocatorSelection) -> Box<dyn FontLocator> {
panic!("fontconfig not compiled in");
}
FontLocatorSelection::FontLoader => {
#[cfg(any(target_os = "macos", windows))]
#[cfg(target_os = "macos")]
return Box::new(font_loader::FontLoaderFontLocator {});
#[cfg(not(any(target_os = "macos", windows)))]
#[cfg(not(target_os = "macos"))]
panic!("fontloader not compiled in");
}
FontLocatorSelection::EnumFontFamilies => {
#[cfg(windows)]
return Box::new(enum_font_families::EnumFontFamiliesFontLocator {});
#[cfg(not(windows))]
panic!("EnumFontFamilies not compiled in");
}
FontLocatorSelection::ConfigDirsOnly => Box::new(NopSystemSource {}),
}
}