feat(font): add run iterator, shared grid set and config integration

Completes the font engine port:
- shaper/run.rs ports run.zig: the RunIterator over terminal rows that
  splits text runs at style/face boundaries, selection and cursor,
  handles graphemes and ligatures; Shaper::shape_run shapes a run.
- shared_grid_set.rs ports SharedGridSet.zig: grids keyed by a config
  derived key, handed out as Arc<SharedGrid>.
- Font-related types now have one canonical definition in ghostty-config
  (Variation, Descriptor, CodepointMap, metric modifiers, freetype load
  flags, synthetic style, shaping break) which the font crate re-exports.
  MetricModifier::apply_i32 now saturates to -i32::MAX like Zig.
- The 23 terminal-driven harfbuzz shaper tests are ported, printing via
  Screen until Terminal lands.

Test Plan:
- cargo test -p ghostty-font (157 passed, 3 ignored - also skipped in
  Zig); Devanagari/Chakma/Bengali tests return early without system fonts
- cargo test -p ghostty-config (255 passed)
This commit is contained in:
ddidderr committed 2026-10-11 22:38:55 +02:00
1 parent ab9c3fc9db
commit c5d2cf4a64
19 files changed
+3204 -554

No files matched your search

+3 -66
View File
@@ -11,8 +11,8 @@ use crate::metrics::Metrics;
pub mod freetype;
pub use crate::config_types::FreetypeLoadFlags;
pub use freetype::Face;
pub use ghostty_config::types_b::FreetypeLoadFlags;
/// If a DPI can't be calculated, this DPI is used. This is probably
/// wrong on modern devices so it is highly recommended you get the DPI
@@ -118,51 +118,8 @@ impl DesiredSize {
}
}
/// A font variation setting. The best documentation for this I know of
/// is actually the CSS font-variation-settings property on MDN:
/// https://developer.mozilla.org/en-US/docs/Web/CSS/font-variation-settings
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Variation {
pub id: VariationId,
pub value: f64,
}
/// A four byte OpenType variation axis tag (`wght`, `slnt`, ...). The
/// numeric value is the big-endian interpretation of the 4 characters
/// (Zig: `packed struct(u32) { d, c, b, a }`, i.e. `a` is the high byte).
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct VariationId {
pub a: u8,
pub b: u8,
pub c: u8,
pub d: u8,
}
impl VariationId {
pub fn new(v: &[u8; 4]) -> VariationId {
VariationId {
a: v[0],
b: v[1],
c: v[2],
d: v[3],
}
}
/// The raw 32-bit tag value.
pub fn as_u32(self) -> u32 {
u32::from_be_bytes([self.a, self.b, self.c, self.d])
}
pub fn from_u32(v: u32) -> VariationId {
let [a, b, c, d] = v.to_be_bytes();
VariationId { a, b, c, d }
}
/// Converts the ID to a string.
pub fn str(self) -> [u8; 4] {
[self.a, self.b, self.c, self.d]
}
}
/// Font variation types: canonical definitions live in `ghostty-config`.
pub use ghostty_config::font_types::{Variation, VariationId};
/// The size and position of a glyph.
#[derive(Debug, Clone, Copy, PartialEq, Default)]
@@ -622,26 +579,6 @@ impl Constraint {
mod tests {
use super::*;
#[test]
fn variation_id_wght_should_be_2003265652() {
let id = VariationId::new(b"wght");
assert_eq!(2003265652, id.as_u32());
assert_eq!(b"wght", &id.str());
}
#[test]
fn variation_id_slnt_should_be_1936486004() {
let id = VariationId {
a: b's',
b: b'l',
c: b'n',
d: b't',
};
assert_eq!(1936486004, id.as_u32());
assert_eq!(b"slnt", &id.str());
assert_eq!(VariationId::from_u32(1936486004), id);
}
#[test]
fn desired_size_pixels() {
let s = DesiredSize {