From d071d1a1c3c8e5a6538de4de299a5b270043a67c Mon Sep 17 00:00:00 2001 From: Stewart Connor Date: Wed, 19 Mar 2025 17:45:30 +1100 Subject: [PATCH 01/12] add kerning ligatures --- src/attrs.rs | 36 ++++++++++++++++++++++++++++++++++++ src/shape.rs | 38 +++++++++++++++++++++++++++++++++++++- 2 files changed, 73 insertions(+), 1 deletion(-) diff --git a/src/attrs.rs b/src/attrs.rs index d7acc1c..3debcc4 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -125,6 +125,31 @@ impl From for Metrics { } } +/// Font features for controlling typographic behavior +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct FontFeatures { + /// Kerning adjusts spacing between specific character pairs + pub kerning: bool, + /// Standard ligatures (fi, fl, etc.) + pub standard_ligatures: bool, + /// Contextual ligatures (context-dependent ligatures) + pub contextual_ligatures: bool, + /// Discretionary ligatures (optional stylistic ligatures) + pub discretionary_ligatures: bool, +} + +impl FontFeatures { + /// Create new font features with default settings + pub fn new() -> Self { + Self { + kerning: true, + standard_ligatures: true, + contextual_ligatures: false, + discretionary_ligatures: false, + } + } +} + /// Text attributes #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Attrs<'a> { @@ -137,6 +162,7 @@ pub struct Attrs<'a> { pub metadata: usize, pub cache_key_flags: CacheKeyFlags, pub metrics_opt: Option, + pub font_features: FontFeatures, } impl<'a> Attrs<'a> { @@ -153,6 +179,7 @@ impl<'a> Attrs<'a> { metadata: 0, cache_key_flags: CacheKeyFlags::empty(), metrics_opt: None, + font_features: FontFeatures::new(), } } @@ -204,6 +231,12 @@ impl<'a> Attrs<'a> { self } + /// Set [FontFeatures] + pub fn font_features(mut self, font_features: FontFeatures) -> Self { + self.font_features = font_features; + self + } + /// Check if font matches pub fn matches(&self, face: &fontdb::FaceInfo) -> bool { //TODO: smarter way of including emoji @@ -252,6 +285,7 @@ pub struct AttrsOwned { pub metadata: usize, pub cache_key_flags: CacheKeyFlags, pub metrics_opt: Option, + pub font_features: FontFeatures, } impl AttrsOwned { @@ -265,6 +299,7 @@ impl AttrsOwned { metadata: attrs.metadata, cache_key_flags: attrs.cache_key_flags, metrics_opt: attrs.metrics_opt, + font_features: attrs.font_features, } } @@ -278,6 +313,7 @@ impl AttrsOwned { metadata: self.metadata, cache_key_flags: self.cache_key_flags, metrics_opt: self.metrics_opt, + font_features: self.font_features, } } } diff --git a/src/shape.rs b/src/shape.rs index 04a4d40..31a510c 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -140,12 +140,48 @@ fn shape_fallback( let rtl = matches!(buffer.direction(), rustybuzz::Direction::RightToLeft); assert_eq!(rtl, span_rtl); + let attrs = attrs_list.get_span(start_run); + + let mut features = Vec::new(); + + if attrs.font_features.kerning { + features.push(rustybuzz::Feature::new( + rustybuzz::ttf_parser::Tag::from_bytes(b"kern"), + 1, + 0..usize::MAX, + )); + } + + if attrs.font_features.standard_ligatures { + features.push(rustybuzz::Feature::new( + rustybuzz::ttf_parser::Tag::from_bytes(b"liga"), + 1, + 0..usize::MAX, + )); + } + + if attrs.font_features.contextual_ligatures { + features.push(rustybuzz::Feature::new( + rustybuzz::ttf_parser::Tag::from_bytes(b"clig"), + 1, + 0..usize::MAX, + )); + } + + if attrs.font_features.discretionary_ligatures { + features.push(rustybuzz::Feature::new( + rustybuzz::ttf_parser::Tag::from_bytes(b"dlig"), + 1, + 0..usize::MAX, + )); + } + let shape_plan = rustybuzz::ShapePlan::new( font.rustybuzz(), buffer.direction(), Some(buffer.script()), buffer.language().as_ref(), - &[], + &features, ); let glyph_buffer = rustybuzz::shape_with_plan(font.rustybuzz(), &shape_plan, buffer); let glyph_infos = glyph_buffer.glyph_infos(); From 81ca98d5db1ac7ff61ad4e4f3fa9249b4287120b Mon Sep 17 00:00:00 2001 From: Stewart Connor Date: Fri, 21 Mar 2025 17:12:38 +1100 Subject: [PATCH 02/12] more explicit about setting features --- src/shape.rs | 48 ++++++++++++++++++++---------------------------- 1 file changed, 20 insertions(+), 28 deletions(-) diff --git a/src/shape.rs b/src/shape.rs index 31a510c..b7ec113 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -144,37 +144,29 @@ fn shape_fallback( let mut features = Vec::new(); - if attrs.font_features.kerning { - features.push(rustybuzz::Feature::new( - rustybuzz::ttf_parser::Tag::from_bytes(b"kern"), - 1, - 0..usize::MAX, - )); - } + features.push(rustybuzz::Feature::new( + rustybuzz::ttf_parser::Tag::from_bytes(b"kern"), + if attrs.font_features.kerning { 1 } else { 0 }, + 0..usize::MAX, + )); - if attrs.font_features.standard_ligatures { - features.push(rustybuzz::Feature::new( - rustybuzz::ttf_parser::Tag::from_bytes(b"liga"), - 1, - 0..usize::MAX, - )); - } + features.push(rustybuzz::Feature::new( + rustybuzz::ttf_parser::Tag::from_bytes(b"liga"), + if attrs.font_features.standard_ligatures { 1 } else { 0 }, + 0..usize::MAX, + )); - if attrs.font_features.contextual_ligatures { - features.push(rustybuzz::Feature::new( - rustybuzz::ttf_parser::Tag::from_bytes(b"clig"), - 1, - 0..usize::MAX, - )); - } + features.push(rustybuzz::Feature::new( + rustybuzz::ttf_parser::Tag::from_bytes(b"clig"), + if attrs.font_features.contextual_ligatures { 1 } else { 0 }, + 0..usize::MAX, + )); - if attrs.font_features.discretionary_ligatures { - features.push(rustybuzz::Feature::new( - rustybuzz::ttf_parser::Tag::from_bytes(b"dlig"), - 1, - 0..usize::MAX, - )); - } + features.push(rustybuzz::Feature::new( + rustybuzz::ttf_parser::Tag::from_bytes(b"dlig"), + if attrs.font_features.discretionary_ligatures { 1 } else { 0 }, + 0..usize::MAX, + )); let shape_plan = rustybuzz::ShapePlan::new( font.rustybuzz(), From 4b3a6e84aca307d5a9f9b04821a39bb6363c1bab Mon Sep 17 00:00:00 2001 From: Stewart Connor Date: Fri, 21 Mar 2025 17:13:24 +1100 Subject: [PATCH 03/12] better defaults --- src/attrs.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/attrs.rs b/src/attrs.rs index 3debcc4..c9ca70e 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -144,8 +144,8 @@ impl FontFeatures { Self { kerning: true, standard_ligatures: true, - contextual_ligatures: false, - discretionary_ligatures: false, + contextual_ligatures: true, + discretionary_ligatures: true, } } } From 2d67e31bf1da8bb7aa182c76cd25dcba5cf4426f Mon Sep 17 00:00:00 2001 From: Stewart Connor Date: Fri, 21 Mar 2025 17:56:14 +1100 Subject: [PATCH 04/12] format --- src/shape.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/shape.rs b/src/shape.rs index b7ec113..c98d78e 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -143,25 +143,21 @@ fn shape_fallback( let attrs = attrs_list.get_span(start_run); let mut features = Vec::new(); - features.push(rustybuzz::Feature::new( rustybuzz::ttf_parser::Tag::from_bytes(b"kern"), if attrs.font_features.kerning { 1 } else { 0 }, 0..usize::MAX, )); - features.push(rustybuzz::Feature::new( rustybuzz::ttf_parser::Tag::from_bytes(b"liga"), if attrs.font_features.standard_ligatures { 1 } else { 0 }, 0..usize::MAX, )); - features.push(rustybuzz::Feature::new( rustybuzz::ttf_parser::Tag::from_bytes(b"clig"), if attrs.font_features.contextual_ligatures { 1 } else { 0 }, 0..usize::MAX, )); - features.push(rustybuzz::Feature::new( rustybuzz::ttf_parser::Tag::from_bytes(b"dlig"), if attrs.font_features.discretionary_ligatures { 1 } else { 0 }, From 50373cd6d737f3c95090e14572d0fa7e435027f9 Mon Sep 17 00:00:00 2001 From: Stewart Connor Date: Fri, 21 Mar 2025 18:03:15 +1100 Subject: [PATCH 05/12] formatting --- src/shape.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/shape.rs b/src/shape.rs index c98d78e..21752cb 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -145,22 +145,22 @@ fn shape_fallback( let mut features = Vec::new(); features.push(rustybuzz::Feature::new( rustybuzz::ttf_parser::Tag::from_bytes(b"kern"), - if attrs.font_features.kerning { 1 } else { 0 }, + attrs.font_features.kerning as u32, 0..usize::MAX, )); features.push(rustybuzz::Feature::new( rustybuzz::ttf_parser::Tag::from_bytes(b"liga"), - if attrs.font_features.standard_ligatures { 1 } else { 0 }, + attrs.font_features.standard_ligatures as u32, 0..usize::MAX, )); features.push(rustybuzz::Feature::new( rustybuzz::ttf_parser::Tag::from_bytes(b"clig"), - if attrs.font_features.contextual_ligatures { 1 } else { 0 }, + attrs.font_features.contextual_ligatures as u32, 0..usize::MAX, )); features.push(rustybuzz::Feature::new( rustybuzz::ttf_parser::Tag::from_bytes(b"dlig"), - if attrs.font_features.discretionary_ligatures { 1 } else { 0 }, + attrs.font_features.discretionary_ligatures as u32, 0..usize::MAX, )); From f5879b3425f39d6d6a442642bcd66986b6554487 Mon Sep 17 00:00:00 2001 From: Stewart Connor Date: Fri, 21 Mar 2025 18:24:32 +1100 Subject: [PATCH 06/12] add contextual alternatives --- src/attrs.rs | 3 +++ src/shape.rs | 5 +++++ 2 files changed, 8 insertions(+) diff --git a/src/attrs.rs b/src/attrs.rs index c9ca70e..c13642e 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -134,6 +134,8 @@ pub struct FontFeatures { pub standard_ligatures: bool, /// Contextual ligatures (context-dependent ligatures) pub contextual_ligatures: bool, + /// Contextual alternates (glyph substitutions based on context) + pub contextual_alternates: bool, /// Discretionary ligatures (optional stylistic ligatures) pub discretionary_ligatures: bool, } @@ -145,6 +147,7 @@ impl FontFeatures { kerning: true, standard_ligatures: true, contextual_ligatures: true, + contextual_alternates: true, discretionary_ligatures: true, } } diff --git a/src/shape.rs b/src/shape.rs index 21752cb..a6c400d 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -158,6 +158,11 @@ fn shape_fallback( attrs.font_features.contextual_ligatures as u32, 0..usize::MAX, )); + features.push(rustybuzz::Feature::new( + rustybuzz::ttf_parser::Tag::from_bytes(b"calt"), + attrs.font_features.contextual_alternates as u32, + 0..usize::MAX, + )); features.push(rustybuzz::Feature::new( rustybuzz::ttf_parser::Tag::from_bytes(b"dlig"), attrs.font_features.discretionary_ligatures as u32, From 1d7850099f2e0d8fc456d2644e36c7c8a714b297 Mon Sep 17 00:00:00 2001 From: Stewart Connor Date: Wed, 26 Mar 2025 14:31:37 +1100 Subject: [PATCH 07/12] pre-removing copy --- src/attrs.rs | 88 ++++++++++++++++++++++++++++++++++++++-------------- src/shape.rs | 38 +++++++---------------- 2 files changed, 75 insertions(+), 51 deletions(-) diff --git a/src/attrs.rs b/src/attrs.rs index c13642e..33faf91 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -5,6 +5,8 @@ use alloc::vec::Vec; use core::ops::Range; use rangemap::RangeMap; use smol_str::SmolStr; +use std::collections::HashMap; +use std::rc::Rc; use crate::{CacheKeyFlags, Metrics}; @@ -125,31 +127,69 @@ impl From for Metrics { } } -/// Font features for controlling typographic behavior +/// OpenType feature tag #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct FeatureTag([u8; 4]); + +impl FeatureTag { + /// Create a new feature tag from a 4-character string + pub const fn new(tag: &[u8; 4]) -> Self { + Self(*tag) + } + + // Common OpenType features as constants + pub const KERNING: Self = Self::new(b"kern"); + pub const STANDARD_LIGATURES: Self = Self::new(b"liga"); + pub const CONTEXTUAL_LIGATURES: Self = Self::new(b"clig"); + pub const CONTEXTUAL_ALTERNATES: Self = Self::new(b"calt"); + pub const DISCRETIONARY_LIGATURES: Self = Self::new(b"dlig"); + pub const SMALL_CAPS: Self = Self::new(b"smcp"); + pub const ALL_SMALL_CAPS: Self = Self::new(b"c2sc"); + pub const STYLISTIC_SET_1: Self = Self::new(b"ss01"); + pub const STYLISTIC_SET_2: Self = Self::new(b"ss02"); + // Add more common features as needed +} + +/// Font feature with tag and value +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct Feature { + pub tag: FeatureTag, + pub value: u32, +} + +/// Font features for text shaping +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] pub struct FontFeatures { - /// Kerning adjusts spacing between specific character pairs - pub kerning: bool, - /// Standard ligatures (fi, fl, etc.) - pub standard_ligatures: bool, - /// Contextual ligatures (context-dependent ligatures) - pub contextual_ligatures: bool, - /// Contextual alternates (glyph substitutions based on context) - pub contextual_alternates: bool, - /// Discretionary ligatures (optional stylistic ligatures) - pub discretionary_ligatures: bool, + /// List of OpenType features with their values (0 = disabled, 1 = enabled) + pub features: Vec, } impl FontFeatures { /// Create new font features with default settings pub fn new() -> Self { - Self { - kerning: true, - standard_ligatures: true, - contextual_ligatures: true, - contextual_alternates: true, - discretionary_ligatures: true, - } + let mut features = Vec::new(); + // Enable kerning by default + features.push(Feature { tag: FeatureTag::KERNING, value: 1 }); + // Enable standard ligatures by default + features.push(Feature { tag: FeatureTag::STANDARD_LIGATURES, value: 1 }); + + Self { features } + } + + /// Set a feature value + pub fn set(&mut self, tag: FeatureTag, value: u32) -> &mut Self { + self.features.push(Feature { tag, value }); + self + } + + /// Enable a feature (set to 1) + pub fn enable(&mut self, tag: FeatureTag) -> &mut Self { + self.set(tag, 1) + } + + /// Disable a feature (set to 0) + pub fn disable(&mut self, tag: FeatureTag) -> &mut Self { + self.set(tag, 0) } } @@ -165,7 +205,7 @@ pub struct Attrs<'a> { pub metadata: usize, pub cache_key_flags: CacheKeyFlags, pub metrics_opt: Option, - pub font_features: FontFeatures, + pub font_features: Option>, } impl<'a> Attrs<'a> { @@ -182,7 +222,7 @@ impl<'a> Attrs<'a> { metadata: 0, cache_key_flags: CacheKeyFlags::empty(), metrics_opt: None, - font_features: FontFeatures::new(), + font_features: None, // Use default features } } @@ -236,7 +276,7 @@ impl<'a> Attrs<'a> { /// Set [FontFeatures] pub fn font_features(mut self, font_features: FontFeatures) -> Self { - self.font_features = font_features; + self.font_features = Some(Rc::new(font_features)); self } @@ -288,7 +328,7 @@ pub struct AttrsOwned { pub metadata: usize, pub cache_key_flags: CacheKeyFlags, pub metrics_opt: Option, - pub font_features: FontFeatures, + pub font_features: Option>, } impl AttrsOwned { @@ -302,7 +342,7 @@ impl AttrsOwned { metadata: attrs.metadata, cache_key_flags: attrs.cache_key_flags, metrics_opt: attrs.metrics_opt, - font_features: attrs.font_features, + font_features: attrs.font_features.clone(), } } @@ -316,7 +356,7 @@ impl AttrsOwned { metadata: self.metadata, cache_key_flags: self.cache_key_flags, metrics_opt: self.metrics_opt, - font_features: self.font_features, + font_features: self.font_features.clone(), } } } diff --git a/src/shape.rs b/src/shape.rs index a6c400d..2718aa0 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -142,39 +142,23 @@ fn shape_fallback( let attrs = attrs_list.get_span(start_run); - let mut features = Vec::new(); - features.push(rustybuzz::Feature::new( - rustybuzz::ttf_parser::Tag::from_bytes(b"kern"), - attrs.font_features.kerning as u32, - 0..usize::MAX, - )); - features.push(rustybuzz::Feature::new( - rustybuzz::ttf_parser::Tag::from_bytes(b"liga"), - attrs.font_features.standard_ligatures as u32, - 0..usize::MAX, - )); - features.push(rustybuzz::Feature::new( - rustybuzz::ttf_parser::Tag::from_bytes(b"clig"), - attrs.font_features.contextual_ligatures as u32, - 0..usize::MAX, - )); - features.push(rustybuzz::Feature::new( - rustybuzz::ttf_parser::Tag::from_bytes(b"calt"), - attrs.font_features.contextual_alternates as u32, - 0..usize::MAX, - )); - features.push(rustybuzz::Feature::new( - rustybuzz::ttf_parser::Tag::from_bytes(b"dlig"), - attrs.font_features.discretionary_ligatures as u32, - 0..usize::MAX, - )); + // Convert attrs::Feature to rustybuzz::Feature + let font_features = attrs.get_font_features(); + let mut rb_features = Vec::with_capacity(font_features.features.len()); + for feature in &font_features.features { + rb_features.push(rustybuzz::Feature::new( + rustybuzz::ttf_parser::Tag::from_bytes(&feature.tag.as_bytes()), + feature.value, + 0..usize::MAX, + )); + } let shape_plan = rustybuzz::ShapePlan::new( font.rustybuzz(), buffer.direction(), Some(buffer.script()), buffer.language().as_ref(), - &features, + &rb_features, ); let glyph_buffer = rustybuzz::shape_with_plan(font.rustybuzz(), &shape_plan, buffer); let glyph_infos = glyph_buffer.glyph_infos(); From fb852d3ab5ef25dca26d87a98f7a0dd3b871f328 Mon Sep 17 00:00:00 2001 From: Stewart Connor Date: Wed, 26 Mar 2025 14:48:54 +1100 Subject: [PATCH 08/12] changes before ref --- src/attrs.rs | 26 ++++++++++++++------------ src/shape.rs | 23 ++++++++++++----------- 2 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/attrs.rs b/src/attrs.rs index 33faf91..4a08b3e 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -5,8 +5,6 @@ use alloc::vec::Vec; use core::ops::Range; use rangemap::RangeMap; use smol_str::SmolStr; -use std::collections::HashMap; -use std::rc::Rc; use crate::{CacheKeyFlags, Metrics}; @@ -148,6 +146,10 @@ impl FeatureTag { pub const STYLISTIC_SET_1: Self = Self::new(b"ss01"); pub const STYLISTIC_SET_2: Self = Self::new(b"ss02"); // Add more common features as needed + + pub fn as_bytes(&self) -> &[u8; 4] { + &self.0 + } } /// Font feature with tag and value @@ -194,7 +196,7 @@ impl FontFeatures { } /// Text attributes -#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +#[derive(Clone, Debug, Eq, Hash, PartialEq)] pub struct Attrs<'a> { //TODO: should this be an option? pub color_opt: Option, @@ -205,7 +207,7 @@ pub struct Attrs<'a> { pub metadata: usize, pub cache_key_flags: CacheKeyFlags, pub metrics_opt: Option, - pub font_features: Option>, + pub font_features: Option, } impl<'a> Attrs<'a> { @@ -276,7 +278,7 @@ impl<'a> Attrs<'a> { /// Set [FontFeatures] pub fn font_features(mut self, font_features: FontFeatures) -> Self { - self.font_features = Some(Rc::new(font_features)); + self.font_features = Some(font_features); self } @@ -328,7 +330,7 @@ pub struct AttrsOwned { pub metadata: usize, pub cache_key_flags: CacheKeyFlags, pub metrics_opt: Option, - pub font_features: Option>, + pub font_features: Option, } impl AttrsOwned { @@ -371,9 +373,9 @@ pub struct AttrsList { impl AttrsList { /// Create a new attributes list with a set of default [Attrs] - pub fn new(defaults: Attrs) -> Self { + pub fn new(defaults: &Attrs) -> Self { Self { - defaults: AttrsOwned::new(defaults), + defaults: AttrsOwned::new(defaults.clone()), spans: RangeMap::new(), } } @@ -399,13 +401,13 @@ impl AttrsList { } /// Add an attribute span, removes any previous matching parts of spans - pub fn add_span(&mut self, range: Range, attrs: Attrs) { + pub fn add_span(&mut self, range: Range, attrs: &Attrs) { //do not support 1..1 or 2..1 even if by accident. if range.is_empty() { return; } - self.spans.insert(range, AttrsOwned::new(attrs)); + self.spans.insert(range, AttrsOwned::new(attrs.clone())); } /// Get the attribute span for an index @@ -455,8 +457,8 @@ impl AttrsList { } /// Resets the attributes with new defaults. - pub(crate) fn reset(mut self, default: Attrs) -> Self { - self.defaults = AttrsOwned::new(default); + pub(crate) fn reset(mut self, default: &Attrs) -> Self { + self.defaults = AttrsOwned::new(default.clone()); self.spans.clear(); self } diff --git a/src/shape.rs b/src/shape.rs index 2718aa0..1edcc5e 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -141,17 +141,18 @@ fn shape_fallback( assert_eq!(rtl, span_rtl); let attrs = attrs_list.get_span(start_run); + let mut rb_features = Vec::new(); // Convert attrs::Feature to rustybuzz::Feature - let font_features = attrs.get_font_features(); - let mut rb_features = Vec::with_capacity(font_features.features.len()); - for feature in &font_features.features { - rb_features.push(rustybuzz::Feature::new( - rustybuzz::ttf_parser::Tag::from_bytes(&feature.tag.as_bytes()), - feature.value, - 0..usize::MAX, - )); - } + if let Some(font_features) = attrs.font_features { + for feature in font_features.features { + rb_features.push(rustybuzz::Feature::new( + rustybuzz::ttf_parser::Tag::from_bytes(&feature.tag.as_bytes()), + feature.value, + 0..usize::MAX, + )); + } + }; let shape_plan = rustybuzz::ShapePlan::new( font.rustybuzz(), @@ -261,7 +262,7 @@ fn shape_run( let attrs = attrs_list.get_span(start_run); - let fonts = font_system.get_font_matches(attrs); + let fonts = font_system.get_font_matches(&attrs); let default_families = [&attrs.family]; let mut font_iter = FontFallbackIter::new( @@ -444,7 +445,7 @@ fn shape_skip( end_run: usize, ) { let attrs = attrs_list.get_span(start_run); - let fonts = font_system.get_font_matches(attrs); + let fonts = font_system.get_font_matches(&attrs); let default_families = [&attrs.family]; let mut font_iter = FontFallbackIter::new(font_system, &fonts, &default_families, &[], ""); From 71b0680e6f032658af87303366ba7ca54cf78e12 Mon Sep 17 00:00:00 2001 From: Stewart Connor Date: Wed, 26 Mar 2025 17:06:24 +1100 Subject: [PATCH 09/12] revert the references --- src/attrs.rs | 38 ++++++++++++++++---------------------- src/shape.rs | 24 +++++++++++------------- 2 files changed, 27 insertions(+), 35 deletions(-) diff --git a/src/attrs.rs b/src/attrs.rs index 4a08b3e..24d515b 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -134,7 +134,7 @@ impl FeatureTag { pub const fn new(tag: &[u8; 4]) -> Self { Self(*tag) } - + // Common OpenType features as constants pub const KERNING: Self = Self::new(b"kern"); pub const STANDARD_LIGATURES: Self = Self::new(b"liga"); @@ -146,7 +146,7 @@ impl FeatureTag { pub const STYLISTIC_SET_1: Self = Self::new(b"ss01"); pub const STYLISTIC_SET_2: Self = Self::new(b"ss02"); // Add more common features as needed - + pub fn as_bytes(&self) -> &[u8; 4] { &self.0 } @@ -169,26 +169,20 @@ pub struct FontFeatures { impl FontFeatures { /// Create new font features with default settings pub fn new() -> Self { - let mut features = Vec::new(); - // Enable kerning by default - features.push(Feature { tag: FeatureTag::KERNING, value: 1 }); - // Enable standard ligatures by default - features.push(Feature { tag: FeatureTag::STANDARD_LIGATURES, value: 1 }); - - Self { features } + Self { features: Vec::new() } } - + /// Set a feature value pub fn set(&mut self, tag: FeatureTag, value: u32) -> &mut Self { self.features.push(Feature { tag, value }); self } - + /// Enable a feature (set to 1) pub fn enable(&mut self, tag: FeatureTag) -> &mut Self { self.set(tag, 1) } - + /// Disable a feature (set to 0) pub fn disable(&mut self, tag: FeatureTag) -> &mut Self { self.set(tag, 0) @@ -207,7 +201,7 @@ pub struct Attrs<'a> { pub metadata: usize, pub cache_key_flags: CacheKeyFlags, pub metrics_opt: Option, - pub font_features: Option, + pub font_features: FontFeatures, } impl<'a> Attrs<'a> { @@ -224,7 +218,7 @@ impl<'a> Attrs<'a> { metadata: 0, cache_key_flags: CacheKeyFlags::empty(), metrics_opt: None, - font_features: None, // Use default features + font_features: FontFeatures::new(), } } @@ -278,7 +272,7 @@ impl<'a> Attrs<'a> { /// Set [FontFeatures] pub fn font_features(mut self, font_features: FontFeatures) -> Self { - self.font_features = Some(font_features); + self.font_features = font_features; self } @@ -330,7 +324,7 @@ pub struct AttrsOwned { pub metadata: usize, pub cache_key_flags: CacheKeyFlags, pub metrics_opt: Option, - pub font_features: Option, + pub font_features: FontFeatures, } impl AttrsOwned { @@ -373,9 +367,9 @@ pub struct AttrsList { impl AttrsList { /// Create a new attributes list with a set of default [Attrs] - pub fn new(defaults: &Attrs) -> Self { + pub fn new(defaults: Attrs) -> Self { Self { - defaults: AttrsOwned::new(defaults.clone()), + defaults: AttrsOwned::new(defaults), spans: RangeMap::new(), } } @@ -401,13 +395,13 @@ impl AttrsList { } /// Add an attribute span, removes any previous matching parts of spans - pub fn add_span(&mut self, range: Range, attrs: &Attrs) { + pub fn add_span(&mut self, range: Range, attrs: Attrs) { //do not support 1..1 or 2..1 even if by accident. if range.is_empty() { return; } - self.spans.insert(range, AttrsOwned::new(attrs.clone())); + self.spans.insert(range, AttrsOwned::new(attrs)); } /// Get the attribute span for an index @@ -457,8 +451,8 @@ impl AttrsList { } /// Resets the attributes with new defaults. - pub(crate) fn reset(mut self, default: &Attrs) -> Self { - self.defaults = AttrsOwned::new(default.clone()); + pub(crate) fn reset(mut self, default: Attrs) -> Self { + self.defaults = AttrsOwned::new(default); self.spans.clear(); self } diff --git a/src/shape.rs b/src/shape.rs index 1edcc5e..8db1334 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -141,25 +141,23 @@ fn shape_fallback( assert_eq!(rtl, span_rtl); let attrs = attrs_list.get_span(start_run); - let mut rb_features = Vec::new(); + let mut rb_font_features = Vec::new(); // Convert attrs::Feature to rustybuzz::Feature - if let Some(font_features) = attrs.font_features { - for feature in font_features.features { - rb_features.push(rustybuzz::Feature::new( - rustybuzz::ttf_parser::Tag::from_bytes(&feature.tag.as_bytes()), - feature.value, - 0..usize::MAX, - )); - } - }; + for feature in attrs.font_features.features { + rb_font_features.push(rustybuzz::Feature::new( + rustybuzz::ttf_parser::Tag::from_bytes(&feature.tag.as_bytes()), + feature.value, + 0..usize::MAX, + )); + } let shape_plan = rustybuzz::ShapePlan::new( font.rustybuzz(), buffer.direction(), Some(buffer.script()), buffer.language().as_ref(), - &rb_features, + &rb_font_features, ); let glyph_buffer = rustybuzz::shape_with_plan(font.rustybuzz(), &shape_plan, buffer); let glyph_infos = glyph_buffer.glyph_infos(); @@ -262,7 +260,7 @@ fn shape_run( let attrs = attrs_list.get_span(start_run); - let fonts = font_system.get_font_matches(&attrs); + let fonts = font_system.get_font_matches(attrs); let default_families = [&attrs.family]; let mut font_iter = FontFallbackIter::new( @@ -445,7 +443,7 @@ fn shape_skip( end_run: usize, ) { let attrs = attrs_list.get_span(start_run); - let fonts = font_system.get_font_matches(&attrs); + let fonts = font_system.get_font_matches(attrs); let default_families = [&attrs.family]; let mut font_iter = FontFallbackIter::new(font_system, &fonts, &default_families, &[], ""); From dc03df8986c0bce710720d774de55e6d84fd4d22 Mon Sep 17 00:00:00 2001 From: Stewart Connor Date: Wed, 26 Mar 2025 17:43:13 +1100 Subject: [PATCH 10/12] Add comments --- src/attrs.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/attrs.rs b/src/attrs.rs index 24d515b..2a2d4c4 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -124,28 +124,33 @@ impl From for Metrics { } } } - -/// OpenType feature tag +/// A 4-byte OpenType feature tag identifier #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct FeatureTag([u8; 4]); impl FeatureTag { - /// Create a new feature tag from a 4-character string pub const fn new(tag: &[u8; 4]) -> Self { Self(*tag) } - // Common OpenType features as constants + /// Kerning adjusts spacing between specific character pairs pub const KERNING: Self = Self::new(b"kern"); + /// Standard ligatures (fi, fl, etc.) pub const STANDARD_LIGATURES: Self = Self::new(b"liga"); + /// Contextual ligatures (context-dependent ligatures) pub const CONTEXTUAL_LIGATURES: Self = Self::new(b"clig"); + /// Contextual alternates (glyph substitutions based on context) pub const CONTEXTUAL_ALTERNATES: Self = Self::new(b"calt"); + /// Discretionary ligatures (optional stylistic ligatures) pub const DISCRETIONARY_LIGATURES: Self = Self::new(b"dlig"); + /// Small caps (lowercase to small capitals) pub const SMALL_CAPS: Self = Self::new(b"smcp"); + /// All small caps (uppercase and lowercase to small capitals) pub const ALL_SMALL_CAPS: Self = Self::new(b"c2sc"); + /// Stylistic Set 1 (font-specific alternate glyphs) pub const STYLISTIC_SET_1: Self = Self::new(b"ss01"); + /// Stylistic Set 2 (font-specific alternate glyphs) pub const STYLISTIC_SET_2: Self = Self::new(b"ss02"); - // Add more common features as needed pub fn as_bytes(&self) -> &[u8; 4] { &self.0 From 03180dd9ee9c04dd5f107aa9389e35245d373009 Mon Sep 17 00:00:00 2001 From: Stewart Connor Date: Wed, 26 Mar 2025 17:45:21 +1100 Subject: [PATCH 11/12] nits --- src/attrs.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/attrs.rs b/src/attrs.rs index 2a2d4c4..ceb08c6 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -157,27 +157,22 @@ impl FeatureTag { } } -/// Font feature with tag and value #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct Feature { pub tag: FeatureTag, pub value: u32, } -/// Font features for text shaping #[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] pub struct FontFeatures { - /// List of OpenType features with their values (0 = disabled, 1 = enabled) pub features: Vec, } impl FontFeatures { - /// Create new font features with default settings pub fn new() -> Self { Self { features: Vec::new() } } - /// Set a feature value pub fn set(&mut self, tag: FeatureTag, value: u32) -> &mut Self { self.features.push(Feature { tag, value }); self From e828131c924f9e62ec5636f993bdad35feafacef Mon Sep 17 00:00:00 2001 From: Stewart Connor Date: Mon, 31 Mar 2025 17:03:51 +1100 Subject: [PATCH 12/12] fix for test.sh --- examples/multiview/src/main.rs | 2 +- examples/rich-text/src/main.rs | 139 +++++++++++++++++++-------------- examples/terminal/src/main.rs | 2 +- src/attrs.rs | 22 +++--- src/buffer.rs | 30 +++---- src/buffer_line.rs | 8 +- src/edit/editor.rs | 4 +- src/edit/syntect.rs | 12 +-- src/font/fallback/macos.rs | 2 +- src/font/system.rs | 2 +- src/lib.rs | 2 +- src/shape.rs | 8 +- tests/common/mod.rs | 6 +- tests/wrap_stability.rs | 4 +- tests/wrap_word_fallback.rs | 2 +- 15 files changed, 136 insertions(+), 109 deletions(-) diff --git a/examples/multiview/src/main.rs b/examples/multiview/src/main.rs index 5e14298..12713cc 100644 --- a/examples/multiview/src/main.rs +++ b/examples/multiview/src/main.rs @@ -31,7 +31,7 @@ fn main() { let attrs = Attrs::new().family(Family::Monospace); match fs::read_to_string(&path) { - Ok(text) => buffer.set_text(&text, attrs, Shaping::Advanced), + Ok(text) => buffer.set_text(&text, &attrs, Shaping::Advanced), Err(err) => { log::error!("failed to load {:?}: {}", path, err); } diff --git a/examples/rich-text/src/main.rs b/examples/rich-text/src/main.rs index 285cea9..167486d 100644 --- a/examples/rich-text/src/main.rs +++ b/examples/rich-text/src/main.rs @@ -21,89 +21,112 @@ use winit::{ fn set_buffer_text(buffer: &mut BorrowedWithFontSystem<'_, Buffer>) { let attrs = Attrs::new(); - let serif_attrs = attrs.family(Family::Serif); - let mono_attrs = attrs.family(Family::Monospace); - let comic_attrs = attrs.family(Family::Name("Comic Neue")); + let serif_attrs = attrs.clone().family(Family::Serif); + let mono_attrs = attrs.clone().family(Family::Monospace); + let comic_attrs = attrs.clone().family(Family::Name("Comic Neue")); let spans: &[(&str, Attrs)] = &[ - ("Font size 64 ", attrs.metrics(Metrics::relative(64.0, 1.2))), - ("Font size 8 ", attrs.metrics(Metrics::relative(8.0, 1.2))), - ("Font size 20 ", attrs.metrics(Metrics::relative(20.0, 1.2))), - ("Font size 14 ", attrs.metrics(Metrics::relative(14.0, 1.2))), + ( + "Font size 64 ", + attrs.clone().metrics(Metrics::relative(64.0, 1.2)), + ), + ( + "Font size 8 ", + attrs.clone().metrics(Metrics::relative(8.0, 1.2)), + ), + ( + "Font size 20 ", + attrs.clone().metrics(Metrics::relative(20.0, 1.2)), + ), + ( + "Font size 14 ", + attrs.clone().metrics(Metrics::relative(14.0, 1.2)), + ), ( "Font size 48\n", - attrs.metrics(Metrics::relative(48.0, 1.2)), + attrs.clone().metrics(Metrics::relative(48.0, 1.2)), ), - ("B", attrs.weight(Weight::BOLD)), - ("old ", attrs), - ("I", attrs.style(Style::Italic)), - ("talic ", attrs), - ("f", attrs), - ("i ", attrs), - ("f", attrs.weight(Weight::BOLD)), - ("i ", attrs), - ("f", attrs.style(Style::Italic)), - ("i \n", attrs), - ("Sans-Serif Normal ", attrs), - ("Sans-Serif Bold ", attrs.weight(Weight::BOLD)), - ("Sans-Serif Italic ", attrs.style(Style::Italic)), + ("B", attrs.clone().weight(Weight::BOLD)), + ("old ", attrs.clone()), + ("I", attrs.clone().style(Style::Italic)), + ("talic ", attrs.clone()), + ("f", attrs.clone()), + ("i ", attrs.clone()), + ("f", attrs.clone().weight(Weight::BOLD)), + ("i ", attrs.clone()), + ("f", attrs.clone().style(Style::Italic)), + ("i \n", attrs.clone()), + ("Sans-Serif Normal ", attrs.clone()), + ("Sans-Serif Bold ", attrs.clone().weight(Weight::BOLD)), + ("Sans-Serif Italic ", attrs.clone().style(Style::Italic)), ( "Sans-Serif Fake Italic ", - attrs.cache_key_flags(CacheKeyFlags::FAKE_ITALIC), + attrs.clone().cache_key_flags(CacheKeyFlags::FAKE_ITALIC), ), ( "Sans-Serif Bold Italic\n", - attrs.weight(Weight::BOLD).style(Style::Italic), + attrs.clone().weight(Weight::BOLD).style(Style::Italic), ), - ("Serif Normal ", serif_attrs), - ("Serif Bold ", serif_attrs.weight(Weight::BOLD)), - ("Serif Italic ", serif_attrs.style(Style::Italic)), + ("Serif Normal ", serif_attrs.clone()), + ("Serif Bold ", serif_attrs.clone().weight(Weight::BOLD)), + ("Serif Italic ", serif_attrs.clone().style(Style::Italic)), ( "Serif Bold Italic\n", - serif_attrs.weight(Weight::BOLD).style(Style::Italic), + serif_attrs + .clone() + .weight(Weight::BOLD) + .style(Style::Italic), ), - ("Mono Normal ", mono_attrs), - ("Mono Bold ", mono_attrs.weight(Weight::BOLD)), - ("Mono Italic ", mono_attrs.style(Style::Italic)), + ("Mono Normal ", mono_attrs.clone()), + ("Mono Bold ", mono_attrs.clone().weight(Weight::BOLD)), + ("Mono Italic ", mono_attrs.clone().style(Style::Italic)), ( "Mono Bold Italic\n", - mono_attrs.weight(Weight::BOLD).style(Style::Italic), + mono_attrs.clone().weight(Weight::BOLD).style(Style::Italic), ), - ("Comic Normal ", comic_attrs), - ("Comic Bold ", comic_attrs.weight(Weight::BOLD)), - ("Comic Italic ", comic_attrs.style(Style::Italic)), + ("Comic Normal ", comic_attrs.clone()), + ("Comic Bold ", comic_attrs.clone().weight(Weight::BOLD)), + ("Comic Italic ", comic_attrs.clone().style(Style::Italic)), ( "Comic Bold Italic\n", - comic_attrs.weight(Weight::BOLD).style(Style::Italic), + comic_attrs + .clone() + .weight(Weight::BOLD) + .style(Style::Italic), ), - ("R", attrs.color(Color::rgb(0xFF, 0x00, 0x00))), - ("A", attrs.color(Color::rgb(0xFF, 0x7F, 0x00))), - ("I", attrs.color(Color::rgb(0xFF, 0xFF, 0x00))), - ("N", attrs.color(Color::rgb(0x00, 0xFF, 0x00))), - ("B", attrs.color(Color::rgb(0x00, 0x00, 0xFF))), - ("O", attrs.color(Color::rgb(0x4B, 0x00, 0x82))), - ("W ", attrs.color(Color::rgb(0x94, 0x00, 0xD3))), - ("Red ", attrs.color(Color::rgb(0xFF, 0x00, 0x00))), - ("Orange ", attrs.color(Color::rgb(0xFF, 0x7F, 0x00))), - ("Yellow ", attrs.color(Color::rgb(0xFF, 0xFF, 0x00))), - ("Green ", attrs.color(Color::rgb(0x00, 0xFF, 0x00))), - ("Blue ", attrs.color(Color::rgb(0x00, 0x00, 0xFF))), - ("Indigo ", attrs.color(Color::rgb(0x4B, 0x00, 0x82))), - ("Violet ", attrs.color(Color::rgb(0x94, 0x00, 0xD3))), - ("U", attrs.color(Color::rgb(0x94, 0x00, 0xD3))), - ("N", attrs.color(Color::rgb(0x4B, 0x00, 0x82))), - ("I", attrs.color(Color::rgb(0x00, 0x00, 0xFF))), - ("C", attrs.color(Color::rgb(0x00, 0xFF, 0x00))), - ("O", attrs.color(Color::rgb(0xFF, 0xFF, 0x00))), - ("R", attrs.color(Color::rgb(0xFF, 0x7F, 0x00))), - ("N\n", attrs.color(Color::rgb(0xFF, 0x00, 0x00))), + ("R", attrs.clone().color(Color::rgb(0xFF, 0x00, 0x00))), + ("A", attrs.clone().color(Color::rgb(0xFF, 0x7F, 0x00))), + ("I", attrs.clone().color(Color::rgb(0xFF, 0xFF, 0x00))), + ("N", attrs.clone().color(Color::rgb(0x00, 0xFF, 0x00))), + ("B", attrs.clone().color(Color::rgb(0x00, 0x00, 0xFF))), + ("O", attrs.clone().color(Color::rgb(0x4B, 0x00, 0x82))), + ("W ", attrs.clone().color(Color::rgb(0x94, 0x00, 0xD3))), + ("Red ", attrs.clone().color(Color::rgb(0xFF, 0x00, 0x00))), + ("Orange ", attrs.clone().color(Color::rgb(0xFF, 0x7F, 0x00))), + ("Yellow ", attrs.clone().color(Color::rgb(0xFF, 0xFF, 0x00))), + ("Green ", attrs.clone().color(Color::rgb(0x00, 0xFF, 0x00))), + ("Blue ", attrs.clone().color(Color::rgb(0x00, 0x00, 0xFF))), + ("Indigo ", attrs.clone().color(Color::rgb(0x4B, 0x00, 0x82))), + ("Violet ", attrs.clone().color(Color::rgb(0x94, 0x00, 0xD3))), + ("U", attrs.clone().color(Color::rgb(0x94, 0x00, 0xD3))), + ("N", attrs.clone().color(Color::rgb(0x4B, 0x00, 0x82))), + ("I", attrs.clone().color(Color::rgb(0x00, 0x00, 0xFF))), + ("C", attrs.clone().color(Color::rgb(0x00, 0xFF, 0x00))), + ("O", attrs.clone().color(Color::rgb(0xFF, 0xFF, 0x00))), + ("R", attrs.clone().color(Color::rgb(0xFF, 0x7F, 0x00))), + ("N\n", attrs.clone().color(Color::rgb(0xFF, 0x00, 0x00))), ( "生活,삶,जिंदगी 😀 FPS\n", - attrs.color(Color::rgb(0xFF, 0x00, 0x00)), + attrs.clone().color(Color::rgb(0xFF, 0x00, 0x00)), ), ]; - buffer.set_rich_text(spans.iter().copied(), attrs, Shaping::Advanced, None); + buffer.set_rich_text( + spans.iter().map(|(text, attrs)| (*text, attrs.clone())), + &attrs, + Shaping::Advanced, + None, + ); } fn main() { diff --git a/examples/terminal/src/main.rs b/examples/terminal/src/main.rs index 7e4bfbb..286dc33 100644 --- a/examples/terminal/src/main.rs +++ b/examples/terminal/src/main.rs @@ -36,7 +36,7 @@ fn main() { let text = std::env::args() .nth(1) .unwrap_or(" Hi, Rust! 🦀 ".to_string()); - buffer.set_text(&text, attrs, Shaping::Advanced); + buffer.set_text(&text, &attrs, Shaping::Advanced); // Perform shaping as desired buffer.shape_until_scroll(true); diff --git a/src/attrs.rs b/src/attrs.rs index 970e5b9..43f1789 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -125,7 +125,7 @@ impl From for Metrics { } } } -/// A 4-byte OpenType feature tag identifier +/// A 4-byte `OpenType` feature tag identifier #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] pub struct FeatureTag([u8; 4]); @@ -171,7 +171,9 @@ pub struct FontFeatures { impl FontFeatures { pub fn new() -> Self { - Self { features: Vec::new() } + Self { + features: Vec::new(), + } } pub fn set(&mut self, tag: FeatureTag, value: u32) -> &mut Self { @@ -311,7 +313,7 @@ impl<'a> Attrs<'a> { self } - /// Set [FontFeatures] + /// Set [`FontFeatures`] pub fn font_features(mut self, font_features: FontFeatures) -> Self { self.font_features = font_features; self @@ -342,8 +344,8 @@ pub struct FontMatchAttrs { weight: Weight, } -impl<'a> From> for FontMatchAttrs { - fn from(attrs: Attrs<'a>) -> Self { +impl<'a> From<&Attrs<'a>> for FontMatchAttrs { + fn from(attrs: &Attrs<'a>) -> Self { Self { family: FamilyOwned::new(attrs.family), stretch: attrs.stretch, @@ -371,7 +373,7 @@ pub struct AttrsOwned { } impl AttrsOwned { - pub fn new(attrs: Attrs) -> Self { + pub fn new(attrs: &Attrs) -> Self { Self { color_opt: attrs.color_opt, family_owned: FamilyOwned::new(attrs.family), @@ -412,7 +414,7 @@ pub struct AttrsList { impl AttrsList { /// Create a new attributes list with a set of default [Attrs] - pub fn new(defaults: Attrs) -> Self { + pub fn new(defaults: &Attrs) -> Self { Self { defaults: AttrsOwned::new(defaults), spans: RangeMap::new(), @@ -440,7 +442,7 @@ impl AttrsList { } /// Add an attribute span, removes any previous matching parts of spans - pub fn add_span(&mut self, range: Range, attrs: Attrs) { + pub fn add_span(&mut self, range: Range, attrs: &Attrs) { //do not support 1..1 or 2..1 even if by accident. if range.is_empty() { return; @@ -462,7 +464,7 @@ impl AttrsList { /// Split attributes list at an offset #[allow(clippy::missing_panics_doc)] pub fn split_off(&mut self, index: usize) -> Self { - let mut new = Self::new(self.defaults.as_attrs()); + let mut new = Self::new(&self.defaults.as_attrs()); let mut removes = Vec::new(); //get the keys we need to remove or fix. @@ -496,7 +498,7 @@ impl AttrsList { } /// Resets the attributes with new defaults. - pub(crate) fn reset(mut self, default: Attrs) -> Self { + pub(crate) fn reset(mut self, default: &Attrs) -> Self { self.defaults = AttrsOwned::new(default); self.spans.clear(); self diff --git a/src/buffer.rs b/src/buffer.rs index f9c9792..0292e6b 100644 --- a/src/buffer.rs +++ b/src/buffer.rs @@ -266,7 +266,7 @@ impl Buffer { /// Will panic if `metrics.line_height` is zero. pub fn new(font_system: &mut FontSystem, metrics: Metrics) -> Self { let mut buffer = Self::new_empty(metrics); - buffer.set_text(font_system, "", Attrs::new(), Shaping::Advanced); + buffer.set_text(font_system, "", &Attrs::new(), Shaping::Advanced); buffer } @@ -676,7 +676,7 @@ impl Buffer { &mut self, font_system: &mut FontSystem, text: &str, - attrs: Attrs, + attrs: &Attrs, shaping: Shaping, ) { self.lines.clear(); @@ -710,10 +710,10 @@ impl Buffer { /// buffer.set_rich_text( /// &mut font_system, /// [ - /// ("hello, ", attrs), - /// ("cosmic\ntext", attrs.family(Family::Monospace)), + /// ("hello, ", attrs.clone()), + /// ("cosmic\ntext", attrs.clone().family(Family::Monospace)), /// ], - /// attrs, + /// &attrs, /// Shaping::Advanced, /// None, /// ); @@ -722,7 +722,7 @@ impl Buffer { &mut self, font_system: &mut FontSystem, spans: I, - default_attrs: Attrs, + default_attrs: &Attrs, shaping: Shaping, alignment: Option, ) where @@ -758,7 +758,7 @@ impl Buffer { .lines .get_mut(line_count) .map(BufferLine::reclaim_attrs) - .unwrap_or_else(|| AttrsList::new(Attrs::new())) + .unwrap_or_else(|| AttrsList::new(&Attrs::new())) .reset(default_attrs); let mut line_string = self .lines @@ -792,7 +792,7 @@ impl Buffer { let text_end = line_string.len(); // Only add attrs if they don't match the defaults if *attrs != attrs_list.defaults() { - attrs_list.add_span(text_start..text_end, *attrs); + attrs_list.add_span(text_start..text_end, attrs); } } @@ -811,7 +811,7 @@ impl Buffer { .lines .get_mut(line_count + 1) .map(BufferLine::reclaim_attrs) - .unwrap_or_else(|| AttrsList::new(Attrs::new())) + .unwrap_or_else(|| AttrsList::new(&Attrs::new())) .reset(default_attrs); let next_line_string = self .lines @@ -1428,7 +1428,7 @@ impl BorrowedWithFontSystem<'_, Buffer> { } /// Set text of buffer, using provided attributes for each line by default - pub fn set_text(&mut self, text: &str, attrs: Attrs, shaping: Shaping) { + pub fn set_text(&mut self, text: &str, attrs: &Attrs, shaping: Shaping) { self.inner.set_text(self.font_system, text, attrs, shaping); } @@ -1438,14 +1438,14 @@ impl BorrowedWithFontSystem<'_, Buffer> { /// # use cosmic_text::{Attrs, Buffer, Family, FontSystem, Metrics, Shaping}; /// # let mut font_system = FontSystem::new(); /// let mut buffer = Buffer::new_empty(Metrics::new(32.0, 44.0)); - /// let mut buffer = buffer.borrow_with(&mut font_system); /// let attrs = Attrs::new().family(Family::Serif); /// buffer.set_rich_text( + /// &mut font_system, /// [ - /// ("hello, ", attrs), - /// ("cosmic\ntext", attrs.family(Family::Monospace)), + /// ("hello, ", attrs.clone()), + /// ("cosmic\ntext", attrs.clone().family(Family::Monospace)), /// ], - /// attrs, + /// &attrs, /// Shaping::Advanced, /// None, /// ); @@ -1453,7 +1453,7 @@ impl BorrowedWithFontSystem<'_, Buffer> { pub fn set_rich_text<'r, 's, I>( &mut self, spans: I, - default_attrs: Attrs, + default_attrs: &Attrs, shaping: Shaping, alignment: Option, ) where diff --git a/src/buffer_line.rs b/src/buffer_line.rs index 36e7195..6929e03 100644 --- a/src/buffer_line.rs +++ b/src/buffer_line.rs @@ -162,13 +162,13 @@ impl BufferLine { if other.attrs_list.defaults() != self.attrs_list.defaults() { // If default formatting does not match, make a new span for it self.attrs_list - .add_span(len..len + other.text().len(), other.attrs_list.defaults()); + .add_span(len..len + other.text().len(), &other.attrs_list.defaults()); } for (other_range, attrs) in other.attrs_list.spans_iter() { // Add previous attrs spans let range = other_range.start + len..other_range.end + len; - self.attrs_list.add_span(range, attrs.as_attrs()); + self.attrs_list.add_span(range, &attrs.as_attrs()); } self.reset(); @@ -283,7 +283,7 @@ impl BufferLine { Self { text: String::default(), ending: LineEnding::default(), - attrs_list: AttrsList::new(Attrs::new()), + attrs_list: AttrsList::new(&Attrs::new()), align: None, shape_opt: Cached::Empty, layout_opt: Cached::Empty, @@ -296,7 +296,7 @@ impl BufferLine { /// /// The buffer line is in an invalid state after this is called. See [`Self::reset_new`]. pub(crate) fn reclaim_attrs(&mut self) -> AttrsList { - mem::replace(&mut self.attrs_list, AttrsList::new(Attrs::new())) + mem::replace(&mut self.attrs_list, AttrsList::new(&Attrs::new())) } /// Reclaim text memory that isn't needed any longer. diff --git a/src/edit/editor.rs b/src/edit/editor.rs index a3e85da..c1b63d8 100644 --- a/src/edit/editor.rs +++ b/src/edit/editor.rs @@ -380,7 +380,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> { let line = BufferLine::new( String::new(), ending, - AttrsList::new(attrs_list.as_ref().map_or_else( + AttrsList::new(&attrs_list.as_ref().map_or_else( || { buffer .lines @@ -404,7 +404,7 @@ impl<'buffer> Edit<'buffer> for Editor<'buffer> { // Collect attributes let mut final_attrs = attrs_list.unwrap_or_else(|| { - AttrsList::new(line.attrs_list().get_span(cursor.index.saturating_sub(1))) + AttrsList::new(&line.attrs_list().get_span(cursor.index.saturating_sub(1))) }); // Append the inserted text, line by line diff --git a/src/edit/syntect.rs b/src/edit/syntect.rs index b532c96..57eb9c7 100644 --- a/src/edit/syntect.rs +++ b/src/edit/syntect.rs @@ -88,7 +88,7 @@ impl<'syntax_system, 'buffer> SyntaxEditor<'syntax_system, 'buffer> { foreground.a, )); } - line.set_attrs_list(AttrsList::new(attrs)); + line.set_attrs_list(AttrsList::new(&attrs)); } }); } @@ -125,7 +125,7 @@ impl<'syntax_system, 'buffer> SyntaxEditor<'syntax_system, 'buffer> { let text = fs::read_to_string(path)?; self.editor.with_buffer_mut(|buffer| { - buffer.set_text(font_system, &text, attrs, Shaping::Advanced); + buffer.set_text(font_system, &text, &attrs, Shaping::Advanced); }); //TODO: re-use text @@ -332,9 +332,11 @@ impl<'buffer> Edit<'buffer> for SyntaxEditor<'_, 'buffer> { ); let attrs = line.attrs_list().defaults(); - let mut attrs_list = AttrsList::new(attrs); + let mut attrs_list = AttrsList::new(&attrs); + let original_attrs = attrs.clone(); // Store a clone for comparison for (style, _, range) in ranges { let span_attrs = attrs + .clone() // Clone attrs for modification .color(Color::rgba( style.foreground.r, style.foreground.g, @@ -352,8 +354,8 @@ impl<'buffer> Edit<'buffer> for SyntaxEditor<'_, 'buffer> { } else { Weight::NORMAL }); //TODO: underline - if span_attrs != attrs { - attrs_list.add_span(range, span_attrs); + if span_attrs != original_attrs { + attrs_list.add_span(range, &span_attrs); } } diff --git a/src/font/fallback/macos.rs b/src/font/fallback/macos.rs index acfb0ac..7eddd48 100644 --- a/src/font/fallback/macos.rs +++ b/src/font/fallback/macos.rs @@ -4,7 +4,7 @@ use unicode_script::Script; use super::Fallback; -/// A platform-specific font fallback list, for MacOS. +/// A platform-specific font fallback list, for `MacOS`. #[derive(Debug)] pub struct PlatformFallback; diff --git a/src/font/system.rs b/src/font/system.rs index d52c1ad..d3a91d9 100644 --- a/src/font/system.rs +++ b/src/font/system.rs @@ -306,7 +306,7 @@ impl FontSystem { }) } - pub fn get_font_matches(&mut self, attrs: Attrs<'_>) -> Arc> { + pub fn get_font_matches(&mut self, attrs: &Attrs<'_>) -> Arc> { // Clear the cache first if it reached the size limit if self.font_matches_cache.len() >= Self::FONT_MATCHES_CACHE_SIZE_LIMIT { log::trace!("clear font mache cache"); diff --git a/src/lib.rs b/src/lib.rs index 5482e05..53c5ea5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,7 +36,7 @@ //! let attrs = Attrs::new(); //! //! // Add some text! -//! buffer.set_text("Hello, Rust! 🦀\n", attrs, Shaping::Advanced); +//! buffer.set_text("Hello, Rust! 🦀\n", &attrs, Shaping::Advanced); //! //! // Perform shaping as desired //! buffer.shape_until_scroll(true); diff --git a/src/shape.rs b/src/shape.rs index ec887ac..98eb730 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -146,7 +146,7 @@ fn shape_fallback( // Convert attrs::Feature to rustybuzz::Feature for feature in attrs.font_features.features { rb_font_features.push(rustybuzz::Feature::new( - rustybuzz::ttf_parser::Tag::from_bytes(&feature.tag.as_bytes()), + rustybuzz::ttf_parser::Tag::from_bytes(feature.tag.as_bytes()), feature.value, 0..usize::MAX, )); @@ -261,7 +261,7 @@ fn shape_run( let attrs = attrs_list.get_span(start_run); - let fonts = font_system.get_font_matches(attrs); + let fonts = font_system.get_font_matches(&attrs); let default_families = [&attrs.family]; let mut font_iter = FontFallbackIter::new( @@ -389,7 +389,7 @@ fn shape_run_cached( let run_range = start_run..end_run; let mut key = ShapeRunKey { text: line[run_range.clone()].to_string(), - default_attrs: AttrsOwned::new(attrs_list.defaults()), + default_attrs: AttrsOwned::new(&attrs_list.defaults()), attrs_spans: Vec::new(), }; for (attrs_range, attrs) in attrs_list.spans.overlapping(&run_range) { @@ -444,7 +444,7 @@ fn shape_skip( end_run: usize, ) { let attrs = attrs_list.get_span(start_run); - let fonts = font_system.get_font_matches(attrs); + let fonts = font_system.get_font_matches(&attrs); let default_families = [&attrs.family]; let mut font_iter = FontFallbackIter::new(font_system, &fonts, &default_families, &[], ""); diff --git a/tests/common/mod.rs b/tests/common/mod.rs index c290f2b..8440070 100644 --- a/tests/common/mod.rs +++ b/tests/common/mod.rs @@ -36,7 +36,7 @@ impl Default for DrawTestCfg { let font = Attrs::new().family(Family::Serif); Self { name: "default".into(), - font: AttrsOwned::new(font), + font: AttrsOwned::new(&font), text: "".into(), font_size: 16.0, line_height: 20.0, @@ -60,7 +60,7 @@ impl DrawTestCfg { } pub fn font_attrs(mut self, attrs: Attrs) -> Self { - self.font = AttrsOwned::new(attrs); + self.font = AttrsOwned::new(&attrs); self } @@ -92,7 +92,7 @@ impl DrawTestCfg { Some((self.canvas_width - margins * 2) as f32), Some((self.canvas_height - margins * 2) as f32), ); - buffer.set_text(&self.text, self.font.as_attrs(), Shaping::Advanced); + buffer.set_text(&self.text, &self.font.as_attrs(), Shaping::Advanced); buffer.shape_until_scroll(true); // Black diff --git a/tests/wrap_stability.rs b/tests/wrap_stability.rs index 11188fc..3bcdf29 100644 --- a/tests/wrap_stability.rs +++ b/tests/wrap_stability.rs @@ -11,7 +11,7 @@ use cosmic_text::{ fn stable_wrap() { let font_size = 18.0; let attrs = AttrsList::new( - Attrs::new() + &Attrs::new() .family(Family::Name("FiraMono")) .weight(Weight::MEDIUM), ); @@ -105,7 +105,7 @@ fn wrap_extra_line() { // Add some text! buffer.set_wrap(Wrap::Word); - buffer.set_text("Lorem ipsum dolor sit amet, qui minim labore adipisicing\n\nweeewoooo minim sint cillum sint consectetur cupidatat.", Attrs::new().family(cosmic_text::Family::Name("Inter")), Shaping::Advanced); + buffer.set_text("Lorem ipsum dolor sit amet, qui minim labore adipisicing\n\nweeewoooo minim sint cillum sint consectetur cupidatat.", &Attrs::new().family(cosmic_text::Family::Name("Inter")), Shaping::Advanced); // Set a size for the text buffer, in pixels buffer.set_size(Some(50.0), Some(1000.0)); diff --git a/tests/wrap_word_fallback.rs b/tests/wrap_word_fallback.rs index 3a2b474..0b3c664 100644 --- a/tests/wrap_word_fallback.rs +++ b/tests/wrap_word_fallback.rs @@ -15,7 +15,7 @@ fn wrap_word_fallback() { let mut buffer = buffer.borrow_with(&mut font_system); buffer.set_wrap(Wrap::WordOrGlyph); - buffer.set_text("Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint cillum sint consectetur cupidatat.", Attrs::new().family(cosmic_text::Family::Name("Inter")), Shaping::Advanced); + buffer.set_text("Lorem ipsum dolor sit amet, qui minim labore adipisicing minim sint cillum sint consectetur cupidatat.", &Attrs::new().family(cosmic_text::Family::Name("Inter")), Shaping::Advanced); buffer.set_size(Some(50.0), Some(1000.0)); buffer.shape_until_scroll(false);