diff --git a/Cargo.lock b/Cargo.lock index 460d8a465d..996ac6b999 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3534,9 +3534,9 @@ dependencies = [ [[package]] name = "cosmic-text" -version = "0.13.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e418dd4f5128c3e93eab12246391c54a20c496811131f85754dc8152ee207892" +checksum = "3e1ecbb5db9a4c2ee642df67bcfa8f044dd867dbbaa21bfab139cbc204ffbf67" dependencies = [ "bitflags 2.9.0", "fontdb 0.16.2", diff --git a/crates/gpui/Cargo.toml b/crates/gpui/Cargo.toml index 790cd00220..81ae893143 100644 --- a/crates/gpui/Cargo.toml +++ b/crates/gpui/Cargo.toml @@ -161,7 +161,7 @@ blade-graphics = { workspace = true, optional = true } blade-macros = { workspace = true, optional = true } blade-util = { workspace = true, optional = true } bytemuck = { version = "1", optional = true } -cosmic-text = { version = "0.13.2", optional = true } +cosmic-text = { version = "0.14.0", optional = true } font-kit = { git = "https://github.com/zed-industries/font-kit", rev = "5474cfad4b719a72ec8ed2cb7327b2b01fd10568", features = [ "source-fontconfig-dlopen", ], optional = true } diff --git a/crates/gpui/src/platform/linux/text_system.rs b/crates/gpui/src/platform/linux/text_system.rs index 1987954db5..65c303de06 100644 --- a/crates/gpui/src/platform/linux/text_system.rs +++ b/crates/gpui/src/platform/linux/text_system.rs @@ -6,8 +6,8 @@ use crate::{ use anyhow::{Context as _, Ok, Result, anyhow}; use collections::HashMap; use cosmic_text::{ - Attrs, AttrsList, CacheKey, Family, Font as CosmicTextFont, FontSystem, ShapeBuffer, ShapeLine, - SwashCache, + Attrs, AttrsList, CacheKey, Family, Font as CosmicTextFont, FontFeatures as CosmicFontFeatures, + FontSystem, ShapeBuffer, ShapeLine, SwashCache, }; use itertools::Itertools; @@ -21,15 +21,29 @@ use std::{borrow::Cow, sync::Arc}; pub(crate) struct CosmicTextSystem(RwLock); +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +struct FontKey { + family: SharedString, + features: FontFeatures, +} + +impl FontKey { + fn new(family: SharedString, features: FontFeatures) -> Self { + Self { family, features } + } +} + struct CosmicTextSystemState { swash_cache: SwashCache, font_system: FontSystem, scratch: ShapeBuffer, /// Contains all already loaded fonts, including all faces. Indexed by `FontId`. loaded_fonts_store: Vec>, + /// Contains enabled font features for each loaded font. + features_store: Vec, /// Caches the `FontId`s associated with a specific family to avoid iterating the font database /// for every font face in a family. - font_ids_by_family_cache: HashMap>, + font_ids_by_family_cache: HashMap>, /// The name of each font associated with the given font id postscript_names: HashMap, } @@ -44,6 +58,7 @@ impl CosmicTextSystem { swash_cache: SwashCache::new(), scratch: ShapeBuffer::default(), loaded_fonts_store: Vec::new(), + features_store: Vec::new(), font_ids_by_family_cache: HashMap::default(), postscript_names: HashMap::default(), })) @@ -78,15 +93,13 @@ impl PlatformTextSystem for CosmicTextSystem { fn font_id(&self, font: &Font) -> Result { // todo(linux): Do we need to use CosmicText's Font APIs? Can we consolidate this to use font_kit? let mut state = self.0.write(); - - let candidates = if let Some(font_ids) = state.font_ids_by_family_cache.get(&font.family) { + let key = FontKey::new(font.family.clone(), font.features.clone()); + let candidates = if let Some(font_ids) = state.font_ids_by_family_cache.get(&key) { font_ids.as_slice() } else { let font_ids = state.load_family(&font.family, &font.features)?; - state - .font_ids_by_family_cache - .insert(font.family.clone(), font_ids); - state.font_ids_by_family_cache[&font.family].as_ref() + state.font_ids_by_family_cache.insert(key.clone(), font_ids); + state.font_ids_by_family_cache[&key].as_ref() }; // todo(linux) ideally we would make fontdb's `find_best_match` pub instead of using font-kit here @@ -229,9 +242,24 @@ impl CosmicTextSystemState { continue; }; + // Convert features into cosmic_text struct. + let mut font_features = CosmicFontFeatures::new(); + for feature in _features.0.iter() { + let name_bytes: [u8; 4] = feature + .0 + .as_bytes() + .try_into() + .map_err(|_| anyhow!("Incorrect feature flag format"))?; + + let tag = cosmic_text::FeatureTag::new(&name_bytes); + + font_features.set(tag, feature.1); + } + let font_id = FontId(self.loaded_fonts_store.len()); font_ids.push(font_id); self.loaded_fonts_store.push(font); + self.features_store.push(font_features); self.postscript_names.insert(font_id, postscript_name); } @@ -361,18 +389,22 @@ impl CosmicTextSystemState { #[profiling::function] fn layout_line(&mut self, text: &str, font_size: Pixels, font_runs: &[FontRun]) -> LineLayout { - let mut attrs_list = AttrsList::new(Attrs::new()); + let mut attrs_list = AttrsList::new(&Attrs::new()); let mut offs = 0; for run in font_runs { let font = &self.loaded_fonts_store[run.font_id.0]; let font = self.font_system.db().face(font.id()).unwrap(); + + let features = self.features_store[run.font_id.0].clone(); + attrs_list.add_span( offs..(offs + run.len), - Attrs::new() + &Attrs::new() .family(Family::Name(&font.families.first().unwrap().0)) .stretch(font.stretch) .style(font.style) - .weight(font.weight), + .weight(font.weight) + .font_features(features), ); offs += run.len; }