From c020707ebc02be12290224d00679b6b323665c1f Mon Sep 17 00:00:00 2001 From: Martin Haug Date: Sat, 20 Nov 2021 12:36:03 +0100 Subject: [PATCH] Add tracking to font function --- src/geom/em.rs | 2 +- src/library/text.rs | 24 ++++++++++++++++++++++++ src/style/mod.rs | 3 +++ tests/ref/text/tracking.png | Bin 0 -> 2163 bytes tests/typ/text/tracking.typ | 8 ++++++++ 5 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 tests/ref/text/tracking.png create mode 100644 tests/typ/text/tracking.typ 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 0000000000000000000000000000000000000000..446fd981a25ddbef478f0cd11b9c4a777e89c015 GIT binary patch literal 2163 zcmaKtX*iUP8pmfCJk7B@ma&t04H6-w8cQ=~WXUc>$dn>X!bl{`n6V_8h@r?9Dj7tU z$-WeMMPfwurVOHDBwowuoDb(**SW6q-XH$geSf$={(rw@ds}lMxGWq3fe2Yz;2j|l zDD3zC2>2b4(}2ir2n0Y|;<2Z%k1yvfMU*#*oh;+!K+tUpJqk0zR|Oh)hoLL6Bn%o< zcnr%d5MymZ*NhCNTTo+TQ?_rLTIGxHz8j)etIDQiTdQXD`sn6Z2TAPt>H_@jO46`+ zZ%~ko)%l-e_AI7>|M|eTYED`jWd@I*5D3^mW!hDyLdGkEznop^$)ZO)*ULkKM4& z)w!D=gf?m+3s0q1``o|HeQxSH`=H4ui!W)aH4rS?Fj;tw+g+E~LIDE7wr_xH3D;gE zZiL5T-{91>9VKy$X30TzwnOpzI*5S)WE`$4>e?$~QPOZyIHDOF9w6>xlGKKBL&2+C$!>PRWDLyj!ghhfBMC2E#* z!aYTFGFT2^`BwtT@>?Jl2Z0jORi;@ByRM#reURP0iW28c1_Vp*LBeBHJZ&F zL#r{eE(fez3H9ju&G&Qo!Uy3lfWOnC9BIkWma9ts^JA4&kZ01~RaEY$U8^k7#p+7J zuxzYTbyncTfT2g)0%jwujl83$yuth{$j7O$4*z-?PDcYmUZwrOW)Kd-F=PMYVJ}rr zZ6}s0Tjr{~t8>5OVA!orA*>(6A3;$R0o!VF52Z4$-2h;JQ$}8*5-mdhcFCm7cB5o$@hv|y!{=pX6~+}7nCF8W@&?scnhoX42UlTf85#Qw~U#-ak<$A9A9aMjh70Q=4{czq# z92P9YKiM|4F!N@gqccx4tjFfb1wx*Be63Rw*A^FUf$&a`q?{QZ5QdJ2Z8SZpkCh6P zi-$u;tN6xADW?vrQ;2JBxbT9P>73H-4BVOe5gF8ii|?oCTAjDDC}#H~a2PW^E=c`Y zHBn}yKI{4{U=nTPySmw~PMr3@sVA0C`^jkm9`SwZayn|?6o z{f5LJ+8I&e7^nATO=AIj1;`v>kA%69aF%mLB#zWSAc#Er`C=o>Y)cdtmL*|I>+G9b z-(S}@GNnN|M4I<+O#};~=l5KVq&s9~Lhl;Gyq~%FS|Txi_($)(RP@VhV3#xy0zeW;`Dfw+3Hy)!=@W{ykG`l>P(Y){Msfzrk<2(zXgP>pTC#n=v0GjJ3n> z33d>*hRij3zDom#^=*<`Dc>u(2Y`wFn|c70Y`3Gj@{|7UgsV{BHazy6z`8uB#pJogy$G2I^x=yNw-;{t6<4Ls1EOP=g zab`6-mBQ$uaSKp_LzlA6Vv5Kf*0JxS?(4Yjb1dBSsvqdB_~LVBz~M`sbqw0<1e{>3 zXH+Q=eMJ4G>6r|T2{jt4mc4-(3@AQ=*d+3hR-6#@L*eR{eZn?w4|fr;yjzhEy&hDZ zFe&HXIPPQstZ60ow47SEuXw1}s=*X)JHppakEI!k9)EiSU=~4VPv0sv5SWQy-eSZs-B%Wf#Ob7`arXHNGH~r8$9Ch>fJj z;pvLbOsZHbjIso?_cyE?Hfm&PO(-3Zurca%2;%q1iCZYpC@$9n(=LbFD?(=tQa5iP zX>yH$``)}De_34=*&s)H|F-p> qY)5GC|Fd@ty!~I2|LuD2xg8N1YRH<=^ECwI_gI?Q;u)si