1use crate::buffer::Buffer;
2use crate::plan::ShapePlan;
3use crate::Face;
4
5pub fn apply(plan: &ShapePlan, face: &Face, buffer: &mut Buffer) -> Option<()> {
6 let trak_mask = plan.trak_mask;
7
8 let ptem = face.points_per_em?;
9 if ptem <= 0.0 {
10 return None;
11 }
12
13 let trak = face.tables().trak?;
14
15 if !buffer.have_positions {
16 buffer.clear_positions();
17 }
18
19 if buffer.direction.is_horizontal() {
20 let tracking = trak.hor_tracking(ptem)?;
21 let advance_to_add = tracking;
22 let offset_to_add = tracking / 2;
23 foreach_grapheme!(buffer, start, end, {
24 if buffer.info[start].mask & trak_mask != 0 {
25 buffer.pos[start].x_advance += advance_to_add;
26 buffer.pos[start].x_offset += offset_to_add;
27 }
28 });
29 } else {
30 let tracking = trak.ver_tracking(ptem)?;
31 let advance_to_add = tracking;
32 let offset_to_add = tracking / 2;
33 foreach_grapheme!(buffer, start, end, {
34 if buffer.info[start].mask & trak_mask != 0 {
35 buffer.pos[start].y_advance += advance_to_add;
36 buffer.pos[start].y_offset += offset_to_add;
37 }
38 });
39 }
40
41 Some(())
42}
43
44trait TrackTableExt {
45 fn hor_tracking(&self, ptem: f32) -> Option<i32>;
46 fn ver_tracking(&self, ptem: f32) -> Option<i32>;
47}
48
49impl TrackTableExt for ttf_parser::trak::Table<'_> {
50 fn hor_tracking(&self, ptem: f32) -> Option<i32> {
51 self.horizontal.tracking(ptem)
52 }
53
54 fn ver_tracking(&self, ptem: f32) -> Option<i32> {
55 self.vertical.tracking(ptem)
56 }
57}
58
59trait TrackTableDataExt {
60 fn tracking(&self, ptem: f32) -> Option<i32>;
61 fn interpolate_at(
62 &self,
63 idx: u16,
64 target_size: f32,
65 track: &ttf_parser::trak::Track,
66 ) -> Option<f32>;
67}
68
69impl TrackTableDataExt for ttf_parser::trak::TrackData<'_> {
70 fn tracking(&self, ptem: f32) -> Option<i32> {
71 // Choose track.
72 let track = self.tracks.into_iter().find(|t| t.value == 0.0)?;
73
74 // Choose size.
75 if self.sizes.is_empty() {
76 return None;
77 }
78
79 let mut idx = self
80 .sizes
81 .into_iter()
82 .position(|s| s.0 >= ptem)
83 .unwrap_or(self.sizes.len() as usize - 1);
84
85 if idx > 0 {
86 idx -= 1;
87 }
88
89 self.interpolate_at(idx as u16, ptem, &track)
90 .map(|n| crate::round(n) as i32)
91 }
92
93 fn interpolate_at(
94 &self,
95 idx: u16,
96 target_size: f32,
97 track: &ttf_parser::trak::Track,
98 ) -> Option<f32> {
99 debug_assert!(idx < self.sizes.len() - 1);
100
101 let s0 = self.sizes.get(idx)?.0;
102 let s1 = self.sizes.get(idx + 1)?.0;
103
104 let t = if s0 == s1 {
105 0.0
106 } else {
107 (target_size - s0) / (s1 - s0)
108 };
109
110 let n =
111 t * (track.values.get(idx + 1)? as f32) + (1.0 - t) * (track.values.get(idx)? as f32);
112
113 Some(n)
114 }
115}
116