feat(font): port the Linux font engine

Ports the rest of ZIG/src/font for the freetype/fontconfig/harfbuzz
backend: face loading and rasterization (synthetic bold/italic, color
emoji bitmaps, glyph constraints for nerd font icons), fontconfig
discovery, DeferredFace, Collection, CodepointResolver, CodepointMap,
SharedGrid, opentype table parsers, the nerd font attribute table, and
the harfbuzz shaper with feature parsing and shaping cache.

Embedded fonts (JetBrains Mono 2.304 variable, Symbols Nerd Font 3.4.0,
Noto emoji) and the test fonts from Ghostty live in res/ with their OFL/
MIT licenses.

The crate opts into unsafe_code = "deny"; FFI is confined to ft.rs,
fontconfig.rs and hb.rs with SAFETY comments. Glyph resampling uses an
stb-style resampler that is not bit-identical to stb.

Deferred to the next font pass (needs ghostty-terminal/config):
shaper/run.zig, SharedGridSet.zig and the 23 terminal-driven shaper
tests. Config-facing types are mirrored locally in config_types.rs.

Test Plan:
- cargo test -p ghostty-font (139 tests)
This commit is contained in:
ddidderr committed 2026-10-11 22:13:06 +02:00
1 parent 6cf16ed2e6
commit 25801765eb
64 files changed
+35316 -23

No files matched your search

+67
View File
@@ -0,0 +1,67 @@
//! A library represents the shared state that the underlying font
//! library implementation(s) require per-process. Port of `font/library.zig`
//! (the `FreetypeLibrary` variant).
use std::sync::{Arc, Mutex};
use crate::face::freetype::ft;
#[derive(Debug, thiserror::Error)]
pub enum LibraryError {
#[error("freetype error {0}")]
Freetype(i32),
}
/// Shared FreeType library state. Cheap to clone.
#[derive(Clone)]
pub struct Library {
pub(crate) inner: Arc<LibraryInner>,
}
pub(crate) struct LibraryInner {
pub(crate) lib: ft::Lib,
/// Mutex to be held any time the library is
/// being used to create or destroy a face.
pub(crate) mutex: Mutex<()>,
}
impl Library {
pub fn init() -> Result<Library, LibraryError> {
let lib = ft::Lib::new().map_err(LibraryError::Freetype)?;
Ok(Library {
inner: Arc::new(LibraryInner {
lib,
mutex: Mutex::new(()),
}),
})
}
/// Lock the library mutex (poisoning is ignored: the guarded state is
/// only FreeType's own).
pub(crate) fn lock(&self) -> std::sync::MutexGuard<'_, ()> {
self.inner
.mutex
.lock()
.unwrap_or_else(std::sync::PoisonError::into_inner)
}
}
impl std::fmt::Debug for Library {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str("Library")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn init_and_clone() {
let lib = Library::init().unwrap();
let lib2 = lib.clone();
drop(lib);
drop(lib2.lock());
}
}