harfrust/hb/aat/
layout_trak_table.rs1#[cfg(not(feature = "std"))]
3#[allow(unused_imports)]
4use core_maths::CoreFloat as _;
5
6use read_fonts::tables::trak::TrackTableEntry;
7use read_fonts::types::{BigEndian, Fixed};
8use read_fonts::FontData;
9
10use crate::hb::{buffer::hb_buffer_t, face::Scale, hb_font_t, ot_shape_plan::hb_ot_shape_plan_t};
11
12pub fn apply(
13 _plan: &hb_ot_shape_plan_t,
14 face: &hb_font_t,
15 scale: Scale,
16 point_size: Option<f32>,
17 buffer: &mut hb_buffer_t,
18) -> Option<()> {
19 let trak = face.aat_tables.trak.as_ref()?;
20 let mut ptem = point_size.unwrap_or(0.0);
21 if ptem <= 0.0 {
22 ptem = 12.0; }
24
25 if !buffer.have_positions {
26 buffer.clear_positions();
27 }
28
29 let advance_to_add = if buffer.direction.is_horizontal() {
30 scale.scale_x_f(trak.get_h_tracking(ptem, 0.0))
31 } else {
32 scale.scale_y_f(trak.get_v_tracking(ptem, 0.0))
33 };
34
35 foreach_grapheme!(buffer, start, end, {
36 if buffer.direction.is_horizontal() {
37 buffer.pos[start].x_advance += advance_to_add;
38 } else {
39 buffer.pos[start].y_advance += advance_to_add;
40 }
41 });
42
43 Some(())
44}
45
46trait TrakExt {
47 fn get_h_tracking(&self, ptem: f32, track: f32) -> f32;
48 fn get_v_tracking(&self, ptem: f32, track: f32) -> f32;
49}
50
51impl TrakExt for read_fonts::tables::trak::Trak<'_> {
52 fn get_h_tracking(&self, ptem: f32, track: f32) -> f32 {
55 self.horiz()
56 .transpose()
57 .ok()
58 .flatten()
59 .map_or(0.0, |t| t.get_tracking(self.offset_data(), ptem, track))
60 }
61
62 fn get_v_tracking(&self, ptem: f32, track: f32) -> f32 {
63 self.vert()
64 .transpose()
65 .ok()
66 .flatten()
67 .map_or(0.0, |t| t.get_tracking(self.offset_data(), ptem, track))
68 }
69}
70
71trait TrackDataExt {
72 fn get_tracking(&self, offset_data: FontData, ptem: f32, track: f32) -> f32;
73}
74
75impl TrackDataExt for read_fonts::tables::trak::TrackData<'_> {
76 fn get_tracking(&self, offset_data: FontData, ptem: f32, track: f32) -> f32 {
77 let Ok(sizes) = self.size_table(offset_data) else {
78 return 0.0;
79 };
80
81 if sizes.is_empty() {
82 return 0.0;
83 }
84
85 let tracks = self.track_table();
86 if tracks.is_empty() {
87 return 0.0;
88 }
89
90 let get_value = |entry: &TrackTableEntry| {
91 let Ok(values) = entry.per_size_values(offset_data, sizes.len() as u16) else {
92 return 0.0;
93 };
94 entry.get_value(ptem, sizes, values)
95 };
96
97 if tracks.len() == 1 {
98 return tracks.first().map_or(0.0, get_value);
99 }
100
101 let mut i = 0;
102 let mut j = tracks.len() - 1;
103
104 while i + 1 < tracks.len() && tracks.get(i + 1).map_or(0.0, |t| t.track().to_f32()) <= track
105 {
106 i += 1;
107 }
108 while j > 0 && tracks.get(j - 1).map_or(0.0, |t| t.track().to_f32()) >= track {
109 j -= 1;
110 }
111
112 if i == j {
113 return tracks.get(i).map_or(0.0, get_value);
114 }
115
116 let t0 = tracks.get(i).map_or(0.0, |t| t.track().to_f32());
117 let t1 = tracks.get(j).map_or(0.0, |t| t.track().to_f32());
118 let interp = if (t1 - t0).abs() < f32::EPSILON {
119 0.0
120 } else {
121 (track - t0) / (t1 - t0)
122 };
123
124 let a = tracks.get(i).map_or(0.0, get_value);
125 let b = tracks.get(j).map_or(0.0, get_value);
126 a + interp * (b - a)
127 }
128}
129
130trait TrackEntryExt {
131 fn get_value(&self, ptem: f32, sizes: &[BigEndian<Fixed>], values: &[BigEndian<i16>]) -> f32;
132}
133
134impl TrackEntryExt for TrackTableEntry {
135 fn get_value(&self, ptem: f32, sizes: &[BigEndian<Fixed>], values: &[BigEndian<i16>]) -> f32 {
136 let n = sizes.len().min(values.len());
137 if n == 0 {
138 return 0.0;
139 }
140
141 for i in 0..n {
142 let size_pt = sizes.get(i).map_or(0.0, |f| f.get().to_f32());
143 if size_pt >= ptem {
144 if i == 0 {
145 return values.first().map(|v| v.get() as f32).unwrap_or_default();
146 }
147
148 let s0 = sizes.get(i - 1).map_or(0.0, |f| f.get().to_f32());
149 let s1 = size_pt;
150 let v0 = values
151 .get(i - 1)
152 .map(|v| v.get() as f32)
153 .unwrap_or_default();
154 let v1 = values.get(i).map(|v| v.get() as f32).unwrap_or_default();
155
156 if (s1 - s0).abs() < f32::EPSILON {
157 return (v0 + v1) * 0.5;
158 }
159
160 let t = (ptem - s0) / (s1 - s0);
161 return v0 + t * (v1 - v0);
162 }
163 }
164
165 values
166 .get(n - 1)
167 .map(|v| v.get() as f32)
168 .unwrap_or_default()
169 }
170}