diff --git a/src/geom/em.rs b/src/geom/em.rs index 6d2f3acaf..caddbcddf 100644 --- a/src/geom/em.rs +++ b/src/geom/em.rs @@ -3,7 +3,7 @@ use super::*; /// A length that is relative to the font size. /// /// `1em` is the same as the font size. -#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)] +#[derive(Default, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] #[derive(Serialize, Deserialize)] pub struct Em(N64); diff --git a/src/library/text.rs b/src/library/text.rs index 914122a19..f2c19516b 100644 --- a/src/library/text.rs +++ b/src/library/text.rs @@ -163,6 +163,7 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult { let fallback = args.named("fallback")?; let style = args.named("style")?; let weight = args.named("weight")?; + let tracking = args.named::("tracking")?; let stretch = args.named("stretch")?; let size = args.named::("size")?.or_else(|| args.find()); let top_edge = args.named("top-edge")?; @@ -203,6 +204,10 @@ pub fn font(ctx: &mut EvalContext, args: &mut Args) -> TypResult { text.fill = Paint::Color(fill); } + if let Some(tracking) = tracking { + text.tracking = Em::new(tracking); + } + set!(text.variant.style => style); set!(text.variant.weight => weight); set!(text.variant.stretch => stretch); @@ -259,6 +264,8 @@ pub fn shape<'a>( ); } + track_segment(&mut glyphs, style.tracking); + let (size, baseline) = measure(ctx, &glyphs, style); ShapedText { text, @@ -555,6 +562,23 @@ fn shape_segment<'a>( } } +/// Apply tracking to a slice of shaped glyphs. +fn track_segment(glyphs: &mut [ShapedGlyph], tracking: Em) { + if tracking.is_zero() { + return; + } + + let mut glyphs = glyphs.iter_mut().peekable(); + while let Some(glyph) = glyphs.next() { + if glyphs + .peek() + .map_or(false, |next| glyph.text_index != next.text_index) + { + glyph.x_advance += tracking; + } + } +} + /// Measure the size and baseline of a run of shaped glyphs with the given /// properties. fn measure( diff --git a/src/style/mod.rs b/src/style/mod.rs index 607347166..bb662032b 100644 --- a/src/style/mod.rs +++ b/src/style/mod.rs @@ -140,6 +140,8 @@ pub struct TextStyle { pub families: Rc, /// OpenType features. pub features: Rc, + /// The amount of space that should be added between character. + pub tracking: Em, /// Whether 300 extra font weight should be added to what is defined by the /// `variant`. pub strong: bool, @@ -222,6 +224,7 @@ impl Default for TextStyle { fill: Paint::Color(Color::Rgba(RgbaColor::BLACK)), families: Rc::new(FamilyStyle::default()), features: Rc::new(FontFeatures::default()), + tracking: Em::zero(), strong: false, emph: false, monospace: false, diff --git a/tests/ref/text/tracking.png b/tests/ref/text/tracking.png new file mode 100644 index 000000000..446fd981a Binary files /dev/null and b/tests/ref/text/tracking.png differ diff --git a/tests/typ/text/tracking.typ b/tests/typ/text/tracking.typ new file mode 100644 index 000000000..de9bd63da --- /dev/null +++ b/tests/typ/text/tracking.typ @@ -0,0 +1,8 @@ +// Test tracking characters apart or together. + +--- +#font(tracking: -0.01) +I saw Zoe yӛsterday, on the tram. + +--- +I'm in#font(tracking: 0.3)[ spaace]!