1
1
mirror of https://github.com/wez/wezterm.git synced 2024-11-26 16:34:23 +03:00

macos: improve core text font matching

Change the loader so that it has better matching weight and stretch
characteristics, and ask core text to return all possible candidates
so that we can then apply our CSS-style font matching rules.

Previously, the font descriptor we created would only match the
family name and return the normal/regular variant only.

refs: https://github.com/wez/wezterm/issues/873
This commit is contained in:
Wez Furlong 2021-06-17 23:26:38 -07:00
parent 7afe539b0c
commit 618f77f2c6

View File

@ -18,11 +18,23 @@ lazy_static::lazy_static! {
static ref FALLBACK: Vec<ParsedFont> = build_fallback_list(); static ref FALLBACK: Vec<ParsedFont> = build_fallback_list();
} }
extern "C" {
static NSFontWeightUltraLight: f64;
static NSFontWeightThin: f64;
static NSFontWeightLight: f64;
static NSFontWeightRegular: f64;
static NSFontWeightMedium: f64;
static NSFontWeightSemibold: f64;
static NSFontWeightBold: f64;
static NSFontWeightHeavy: f64;
static NSFontWeightBlack: f64;
}
/// A FontLocator implemented using the system font loading /// A FontLocator implemented using the system font loading
/// functions provided by core text. /// functions provided by core text.
pub struct CoreTextFontLocator {} pub struct CoreTextFontLocator {}
fn descriptor_from_attr(attr: &FontAttributes) -> anyhow::Result<CTFontDescriptor> { fn descriptor_from_attr(attr: &FontAttributes) -> anyhow::Result<CFArray<CTFontDescriptor>> {
let family_name = attr let family_name = attr
.family .family
.parse::<CFString>() .parse::<CFString>()
@ -34,8 +46,16 @@ fn descriptor_from_attr(attr: &FontAttributes) -> anyhow::Result<CTFontDescripto
} else { } else {
0 0
} }
| if attr.stretch < FontStretch::Normal {
kCTFontCondensedTrait
} else if attr.stretch > FontStretch::Normal {
kCTFontExpandedTrait
} else {
0
}
| if attr.italic { kCTFontItalicTrait } else { 0 }; | if attr.italic { kCTFontItalicTrait } else { 0 };
let weight_attr: CFString = unsafe { TCFType::wrap_under_get_rule(kCTFontWeightTrait) };
let family_attr: CFString = unsafe { TCFType::wrap_under_get_rule(kCTFontFamilyNameAttribute) }; let family_attr: CFString = unsafe { TCFType::wrap_under_get_rule(kCTFontFamilyNameAttribute) };
let traits_attr: CFString = unsafe { TCFType::wrap_under_get_rule(kCTFontTraitsAttribute) }; let traits_attr: CFString = unsafe { TCFType::wrap_under_get_rule(kCTFontTraitsAttribute) };
let symbolic_traits_attr: CFString = let symbolic_traits_attr: CFString =
@ -46,11 +66,41 @@ fn descriptor_from_attr(attr: &FontAttributes) -> anyhow::Result<CTFontDescripto
CFNumber::from(symbolic_traits as i32).as_CFType(), CFNumber::from(symbolic_traits as i32).as_CFType(),
)]); )]);
let weight = unsafe {
match attr.weight {
FontWeight::Thin => NSFontWeightUltraLight,
FontWeight::ExtraLight => NSFontWeightThin,
FontWeight::Light => NSFontWeightLight,
FontWeight::DemiLight => NSFontWeightLight,
FontWeight::Book => NSFontWeightLight,
FontWeight::Regular => NSFontWeightRegular,
FontWeight::Medium => NSFontWeightMedium,
FontWeight::DemiBold => NSFontWeightSemibold,
FontWeight::Bold => NSFontWeightBold,
FontWeight::ExtraBold => NSFontWeightHeavy,
FontWeight::Black => NSFontWeightBlack,
FontWeight::ExtraBlack => NSFontWeightBlack,
}
};
let attributes = CFDictionary::from_CFType_pairs(&[ let attributes = CFDictionary::from_CFType_pairs(&[
(traits_attr, traits.as_CFType()), (traits_attr, traits.as_CFType()),
(family_attr, family_name.as_CFType()), (family_attr, family_name.as_CFType()),
(weight_attr, CFNumber::from(weight).as_CFType()),
]); ]);
Ok(core_text::font_descriptor::new_from_attributes(&attributes)) let desc = core_text::font_descriptor::new_from_attributes(&attributes);
let array = unsafe {
core_text::font_descriptor::CTFontDescriptorCreateMatchingFontDescriptors(
desc.as_concrete_TypeRef(),
std::ptr::null(),
)
};
if array.is_null() {
anyhow::bail!("no font matches {:?}", attr);
} else {
Ok(unsafe { CFArray::wrap_under_get_rule(array) })
}
} }
/// Given a descriptor, return a handle that can be used to open it. /// Given a descriptor, return a handle that can be used to open it.
@ -62,21 +112,9 @@ fn descriptor_from_attr(attr: &FontAttributes) -> anyhow::Result<CTFontDescripto
fn handles_from_descriptor(descriptor: &CTFontDescriptor) -> Vec<ParsedFont> { fn handles_from_descriptor(descriptor: &CTFontDescriptor) -> Vec<ParsedFont> {
let mut result = vec![]; let mut result = vec![];
if let Some(path) = descriptor.font_path() { if let Some(path) = descriptor.font_path() {
let family_name = descriptor.family_name();
let mut font_info = vec![];
let source = FontDataSource::OnDisk(path); let source = FontDataSource::OnDisk(path);
let _ = crate::parser::parse_and_collect_font_info( let _ =
&source, crate::parser::parse_and_collect_font_info(&source, &mut result, FontOrigin::CoreText);
&mut font_info,
FontOrigin::CoreText,
);
for parsed in font_info {
if parsed.names().full_name == family_name || parsed.names().family == family_name {
result.push(parsed);
}
}
} }
result result
@ -91,8 +129,11 @@ impl FontLocator for CoreTextFontLocator {
let mut fonts = vec![]; let mut fonts = vec![];
for attr in fonts_selection { for attr in fonts_selection {
if let Ok(descriptor) = descriptor_from_attr(attr) { if let Ok(descriptors) = descriptor_from_attr(attr) {
let handles = handles_from_descriptor(&descriptor); let mut handles = vec![];
for descriptor in descriptors.iter() {
handles.append(&mut handles_from_descriptor(&descriptor));
}
log::trace!("core text matched {:?} to {:#?}", attr, handles); log::trace!("core text matched {:?} to {:#?}", attr, handles);
if let Some(parsed) = ParsedFont::best_match(attr, handles) { if let Some(parsed) = ParsedFont::best_match(attr, handles) {
log::trace!("best match from core text is {:?}", parsed); log::trace!("best match from core text is {:?}", parsed);
@ -185,8 +226,10 @@ fn build_fallback_list_impl() -> anyhow::Result<Vec<ParsedFont>> {
is_fallback: true, is_fallback: true,
is_synthetic: true, is_synthetic: true,
}; };
if let Ok(descriptor) = descriptor_from_attr(&symbols) { if let Ok(descriptors) = descriptor_from_attr(&symbols) {
fonts.append(&mut handles_from_descriptor(&descriptor)); for descriptor in descriptors.iter() {
fonts.append(&mut handles_from_descriptor(&descriptor));
}
} }
// Constrain to default weight/stretch/style // Constrain to default weight/stretch/style