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 2ad8f91..43f1789 100644 --- a/src/attrs.rs +++ b/src/attrs.rs @@ -125,6 +125,72 @@ impl From for Metrics { } } } +/// A 4-byte `OpenType` feature tag identifier +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct FeatureTag([u8; 4]); + +impl FeatureTag { + pub const fn new(tag: &[u8; 4]) -> Self { + Self(*tag) + } + + /// 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"); + + pub fn as_bytes(&self) -> &[u8; 4] { + &self.0 + } +} + +#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] +pub struct Feature { + pub tag: FeatureTag, + pub value: u32, +} + +#[derive(Clone, Debug, Default, Eq, Hash, PartialEq)] +pub struct FontFeatures { + pub features: Vec, +} + +impl FontFeatures { + pub fn new() -> Self { + Self { + features: Vec::new(), + } + } + + 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) + } +} /// A wrapper for letter spacing to get around that f32 doesn't implement Eq and Hash #[derive(Clone, Copy, Debug)] @@ -158,7 +224,7 @@ impl Hash for LetterSpacing { } /// 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, @@ -171,6 +237,7 @@ pub struct Attrs<'a> { pub metrics_opt: Option, /// Letter spacing (tracking) in EM pub letter_spacing_opt: Option, + pub font_features: FontFeatures, } impl<'a> Attrs<'a> { @@ -188,6 +255,7 @@ impl<'a> Attrs<'a> { cache_key_flags: CacheKeyFlags::empty(), metrics_opt: None, letter_spacing_opt: None, + font_features: FontFeatures::new(), } } @@ -245,6 +313,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 @@ -270,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, @@ -295,10 +369,11 @@ pub struct AttrsOwned { pub metrics_opt: Option, /// Letter spacing (tracking) in EM pub letter_spacing_opt: Option, + pub font_features: FontFeatures, } 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), @@ -309,6 +384,7 @@ impl AttrsOwned { cache_key_flags: attrs.cache_key_flags, metrics_opt: attrs.metrics_opt, letter_spacing_opt: attrs.letter_spacing_opt, + font_features: attrs.font_features.clone(), } } @@ -323,6 +399,7 @@ impl AttrsOwned { cache_key_flags: self.cache_key_flags, metrics_opt: self.metrics_opt, letter_spacing_opt: self.letter_spacing_opt, + font_features: self.font_features.clone(), } } } @@ -337,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(), @@ -365,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; @@ -387,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. @@ -421,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 2b499a8..98eb730 100644 --- a/src/shape.rs +++ b/src/shape.rs @@ -140,12 +140,24 @@ 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 rb_font_features = Vec::new(); + + // 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()), + feature.value, + 0..usize::MAX, + )); + } + let shape_plan = rustybuzz::ShapePlan::new( font.rustybuzz(), buffer.direction(), Some(buffer.script()), buffer.language().as_ref(), - &[], + &rb_font_features, ); let glyph_buffer = rustybuzz::shape_with_plan(font.rustybuzz(), &shape_plan, buffer); let glyph_infos = glyph_buffer.glyph_infos(); @@ -249,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( @@ -377,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) { @@ -432,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);