use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::path::{Path, PathBuf};
use std::sync::atomic::{AtomicU64, Ordering};
pub(crate) fn register_font_from_memory(bytes: &[u8]) -> Option<Vec<String>> {
let path = create_temp_font_file(bytes)?;
let names = register_with_fontconfig(&path);
#[cfg(target_os = "macos")]
let names = {
let registration = macos_core_text::register(&path);
match registration.family {
Some(name) => fold_in_core_text_name(names, name),
None => {
if !registration.registered {
let _ = std::fs::remove_file(&path);
}
return None;
}
}
};
if names.is_empty() {
let _ = std::fs::remove_file(&path);
return None;
}
notify_fontmap_config_changed();
Some(names)
}
pub(crate) fn ensure_codicon_registered() -> bool {
static REGISTERED: std::sync::OnceLock<bool> = std::sync::OnceLock::new();
*REGISTERED.get_or_init(|| register_font_from_memory(crate::codicon::FONT_BYTES).is_some())
}
#[cfg(target_os = "macos")]
fn fold_in_core_text_name(mut names: Vec<String>, name: String) -> Vec<String> {
if let Some(pos) = names
.iter()
.position(|existing| existing.eq_ignore_ascii_case(&name))
{
names.remove(pos);
}
names.insert(0, name);
names
}
fn register_with_fontconfig(path: &Path) -> Vec<String> {
let Some(c_path) = path.to_str().and_then(|s| CString::new(s).ok()) else {
return Vec::new();
};
let config = unsafe { fontconfig_sys::FcConfigGetCurrent() };
if config.is_null() {
return Vec::new();
}
let before = app_font_count(config);
let added = unsafe { fontconfig_sys::FcConfigAppFontAddFile(config, c_path.as_ptr().cast()) };
if added == 0 {
return Vec::new();
}
new_app_font_family_names(config, before)
}
fn unique_temp_font_path() -> PathBuf {
static COUNTER: AtomicU64 = AtomicU64::new(0);
let n = COUNTER.fetch_add(1, Ordering::Relaxed);
std::env::temp_dir().join(format!(
"quadraui-1013-appfont-{}-{n}.font",
std::process::id()
))
}
fn create_temp_font_file(bytes: &[u8]) -> Option<PathBuf> {
use std::fs::OpenOptions;
use std::io::Write;
let path = unique_temp_font_path();
let mut file = OpenOptions::new()
.write(true)
.create_new(true)
.open(&path)
.ok()?;
file.write_all(bytes).ok()?;
Some(path)
}
fn app_font_count(config: *mut fontconfig_sys::FcConfig) -> usize {
let set = unsafe { fontconfig_sys::FcConfigGetFonts(config, fontconfig_sys::FcSetApplication) };
if set.is_null() {
return 0;
}
unsafe { (*set).nfont.max(0) as usize }
}
fn new_app_font_family_names(config: *mut fontconfig_sys::FcConfig, before: usize) -> Vec<String> {
let set = unsafe { fontconfig_sys::FcConfigGetFonts(config, fontconfig_sys::FcSetApplication) };
if set.is_null() {
return Vec::new();
}
let (nfont, fonts) = unsafe { ((*set).nfont.max(0) as usize, (*set).fonts) };
if fonts.is_null() || nfont <= before {
return Vec::new();
}
let mut names = Vec::with_capacity(nfont - before);
for i in before..nfont {
let pattern = unsafe { *fonts.add(i) };
if !pattern.is_null() {
if let Some(name) = pattern_family_name(pattern) {
names.push(name);
}
}
}
names
}
fn pattern_family_name(pattern: *mut fontconfig_sys::FcPattern) -> Option<String> {
let mut out: *mut fontconfig_sys::FcChar8 = std::ptr::null_mut();
let result = unsafe {
fontconfig_sys::FcPatternGetString(
pattern,
fontconfig_sys::constants::FC_FAMILY.as_ptr(),
0,
&mut out,
)
};
if result != fontconfig_sys::FcResultMatch || out.is_null() {
return None;
}
let c = unsafe { CStr::from_ptr(out as *const c_char) };
Some(c.to_string_lossy().into_owned())
}
pub(crate) fn has_font_family(family: &str) -> bool {
let requested = family.trim();
if requested.is_empty() {
return false;
}
if fontconfig_has_font_family(requested) {
return true;
}
#[cfg(target_os = "macos")]
if macos_core_text::has_font_family(requested) {
return true;
}
false
}
fn fontconfig_has_font_family(requested: &str) -> bool {
let Ok(c_family) = CString::new(requested) else {
return false;
};
let config = unsafe { fontconfig_sys::FcConfigGetCurrent() };
if config.is_null() {
return false;
}
let pattern = unsafe { fontconfig_sys::FcPatternCreate() };
if pattern.is_null() {
return false;
}
unsafe {
fontconfig_sys::FcPatternAddString(
pattern,
fontconfig_sys::constants::FC_FAMILY.as_ptr(),
c_family.as_ptr().cast(),
);
fontconfig_sys::FcConfigSubstitute(config, pattern, fontconfig_sys::FcMatchPattern);
fontconfig_sys::FcDefaultSubstitute(pattern);
}
let mut result: fontconfig_sys::FcResult = fontconfig_sys::FcResultNoMatch;
let matched = unsafe { fontconfig_sys::FcFontMatch(config, pattern, &mut result) };
unsafe { fontconfig_sys::FcPatternDestroy(pattern) };
if matched.is_null() {
return false;
}
let resolved = pattern_family_name(matched);
unsafe { fontconfig_sys::FcPatternDestroy(matched) };
resolved.is_some_and(|name| name.eq_ignore_ascii_case(requested))
}
fn notify_fontmap_config_changed() {
let fontmap = unsafe { pangocairo::ffi::pango_cairo_font_map_get_default() };
if fontmap.is_null() {
return;
}
unsafe { pangocairo::pango::ffi::pango_font_map_changed(fontmap) };
let font_type: pangocairo::cairo::FontType =
unsafe { pangocairo::ffi::pango_cairo_font_map_get_font_type(fontmap.cast()) }.into();
if font_type != pangocairo::cairo::FontType::FontTypeFt {
return;
}
unsafe { pango_fc_font_map_config_changed(fontmap) };
}
extern "C" {
fn pango_fc_font_map_config_changed(fcfontmap: *mut pangocairo::pango::ffi::PangoFontMap);
}
#[cfg(target_os = "macos")]
mod macos_core_text {
use core_foundation::array::CFArray;
use core_foundation::base::TCFType;
use core_foundation::url::{CFURLRef, CFURL};
use core_text::font;
use core_text::font_descriptor::CTFontDescriptor;
use core_text::font_manager::CTFontManagerCreateFontDescriptorsFromURL;
use std::path::Path;
const K_CT_FONT_MANAGER_SCOPE_PROCESS: u32 = 1;
pub(super) struct Registration {
pub(super) registered: bool,
pub(super) family: Option<String>,
}
pub(super) fn register(path: &Path) -> Registration {
let Some(url) = CFURL::from_path(path, false) else {
return Registration {
registered: false,
family: None,
};
};
let registered = unsafe {
CTFontManagerRegisterFontsForURL(
url.as_concrete_TypeRef(),
K_CT_FONT_MANAGER_SCOPE_PROCESS,
std::ptr::null_mut(),
)
};
if !registered {
return Registration {
registered: false,
family: None,
};
}
let descriptors_ref =
unsafe { CTFontManagerCreateFontDescriptorsFromURL(url.as_concrete_TypeRef()) };
if descriptors_ref.is_null() {
return Registration {
registered: true,
family: None,
};
}
let descriptors: CFArray<CTFontDescriptor> =
unsafe { TCFType::wrap_under_create_rule(descriptors_ref) };
let family = descriptors
.get(0)
.map(|descriptor| descriptor.family_name());
Registration {
registered: true,
family,
}
}
pub(super) fn has_font_family(family: &str) -> bool {
let Ok(ctfont) = font::new_from_name(family, 12.0) else {
return false;
};
ctfont.family_name().eq_ignore_ascii_case(family)
|| ctfont.postscript_name().eq_ignore_ascii_case(family)
}
#[link(name = "CoreText", kind = "framework")]
extern "C" {
fn CTFontManagerRegisterFontsForURL(
font_url: CFURLRef,
scope: u32,
error: *mut core_foundation::error::CFErrorRef,
) -> bool;
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn register_font_from_memory_rejects_bytes_that_are_not_a_font() {
let garbage = [0u8; 64];
assert!(
register_font_from_memory(&garbage).is_none(),
"64 zero bytes are not a parseable font — must report None, not a fabricated family"
);
}
#[test]
fn register_font_from_memory_rejects_empty_bytes() {
assert!(
register_font_from_memory(&[]).is_none(),
"empty bytes are not a parseable font — must report None"
);
}
#[test]
fn unique_temp_font_path_does_not_collide_within_a_process() {
let a = unique_temp_font_path();
let b = unique_temp_font_path();
assert_ne!(a, b, "consecutive calls must not reuse the same temp path");
}
#[test]
fn has_font_family_is_false_for_a_name_nothing_is_installed_under() {
assert!(!has_font_family(
"Definitely Not A Real Font Family Quadraui 1024"
));
}
#[test]
fn has_font_family_is_false_for_empty_or_blank_input() {
assert!(!has_font_family(""));
assert!(!has_font_family(" "));
}
#[test]
#[ignore]
fn manual_smoke_has_font_family_sees_a_just_registered_app_font() {
let path = std::env::var("QUADRAUI_SMOKE_FONT").expect("set QUADRAUI_SMOKE_FONT");
let bytes = std::fs::read(&path).expect("read font");
let names = register_font_from_memory(&bytes).expect("register_font_from_memory");
let family = names.first().expect("at least one family name");
assert!(
has_font_family(family),
"just-registered family {family:?} should be reported as installed"
);
}
#[test]
#[ignore]
fn manual_smoke_real_font() {
let path = std::env::var("QUADRAUI_SMOKE_FONT").expect("set QUADRAUI_SMOKE_FONT");
let bytes = std::fs::read(&path).expect("read font");
let names = register_font_from_memory(&bytes).expect("register_font_from_memory");
eprintln!("registered families: {names:?}");
assert!(!names.is_empty());
}
const ICON_FONT_BYTES: &[u8] = include_bytes!("../../tests/fixtures/vimcode-icons.ttf");
fn registered_icon_font_names() -> &'static [String] {
static NAMES: std::sync::OnceLock<Vec<String>> = std::sync::OnceLock::new();
NAMES.get_or_init(|| {
register_font_from_memory(ICON_FONT_BYTES).expect(
"vimcode-icons.ttf is a well-formed font every registration backend here \
should parse",
)
})
}
#[test]
fn register_font_from_memory_registers_the_real_icon_font_fixture() {
let names = registered_icon_font_names();
assert!(
!names.is_empty(),
"a real font must resolve at least one family name"
);
}
#[test]
fn has_font_family_sees_the_real_icon_font_fixture() {
let names = registered_icon_font_names();
let family = names.first().expect("at least one family name");
assert!(
has_font_family(family),
"just-registered family {family:?} should be reported as installed"
);
}
#[cfg(target_os = "macos")]
#[test]
fn gtk_register_font_from_memory_registers_with_core_text_so_pango_resolves_pua_glyph() {
use pangocairo::pango::prelude::*;
let names = registered_icon_font_names();
let family = names.first().expect("at least one family name").to_string();
assert!(
macos_core_text::has_font_family(&family),
"the family this function returns first on macOS, {family:?}, must be the one \
Core Text itself resolves — otherwise the assertions below would just be \
re-testing Fontconfig's own name"
);
let font_map = pangocairo::FontMap::default();
let context = pangocairo::pango::Context::new();
context.set_font_map(Some(&font_map));
let mut desc = pangocairo::pango::FontDescription::new();
desc.set_family(&family);
desc.set_size(12 * pangocairo::pango::SCALE);
let font = font_map.load_font(&context, &desc).unwrap_or_else(|| {
panic!(
"Pango's default font map returned no font at all for the just-registered \
family {family:?}"
)
});
let resolved_family = font.describe().family().map(|f| f.to_string());
assert_eq!(
resolved_family.as_deref(),
Some(family.as_str()),
"Pango resolved family {resolved_family:?} instead of the just-registered \
{family:?} — on macOS this means the font only reached Fontconfig, which \
PangoCoreTextFontMap never consults"
);
let ctfont = core_text::font::new_from_name(&family, 12.0)
.expect("Core Text must resolve the family it just registered");
let characters: [u16; 1] = [0xEAF0];
let mut glyphs: [u16; 1] = [0];
let all_mapped = unsafe {
ctfont.get_glyphs_for_characters(characters.as_ptr(), glyphs.as_mut_ptr(), 1)
};
assert!(
all_mapped && glyphs[0] != 0,
"Core Text resolved family {family:?} but reports glyph 0 (.notdef) for U+EAF0 \
(the Explorer icon) — the font registration didn't actually reach Core Text"
);
}
}