1use crate::{
2 tables::{TableDirectoryRepr, cmap::GlyphId},
3 types::{FWORD, Tag, UFWORD, tags},
4};
5
6#[repr(C)]
7#[non_exhaustive]
8pub struct HmtxTableRepr {
9 raw_words: [FWORD; 0],
12 }
15#[repr(C)]
16pub struct LongHorMetricRepr {
17 pub advance_width: UFWORD,
18 pub lsb: FWORD,
19}
20
21impl super::Table for HmtxTableRepr {
22 const TAG: Tag = tags::hmtx;
23 type Handle<'a> = HmtxTableHandle<'a>;
24}
25impl<'a> super::TableHandle<'a> for HmtxTableHandle<'a> {
26 fn in_directory(dir: &'a TableDirectoryRepr) -> Option<Self> {
27 let raw_words = dir.table_raw::<HmtxTableRepr>()?.raw_words.as_ptr();
28 let num_h_metrics = dir.hhea()?.number_of_h_metrics.get() as usize;
29 let num_glyphs = dir.maxp()?.num_glyphs.get() as usize;
30
31 let total_word_count = num_h_metrics + num_glyphs;
32 let raw_words = unsafe { std::slice::from_raw_parts(raw_words, total_word_count) };
33
34 Some(Self { raw_words, num_h_metrics })
35 }
36}
37
38#[derive(Copy)]
41#[derive_const(Clone)]
42pub struct HmtxTableHandle<'a> {
43 raw_words: &'a [FWORD],
44 num_h_metrics: usize,
45}
46
47#[derive(Debug, Copy, Hash)]
48#[derive_const(Clone, Default, PartialEq, Eq)]
49pub struct LongHorMetric {
50 pub aw: u16,
51 pub lsb: i16,
52}
53
54impl LongHorMetric {
55 pub const fn new(aw: u16, lsb: i16) -> Self {
56 Self { aw, lsb }
57 }
58}
59const impl From<&LongHorMetricRepr> for LongHorMetric {
60 fn from(value: &LongHorMetricRepr) -> Self {
61 Self { aw: value.advance_width.get(), lsb: value.lsb.get() }
62 }
63}
64
65impl<'a> HmtxTableHandle<'a> {
66 pub const fn num_h_metrics(&self) -> u16 {
67 self.num_h_metrics as u16
68 }
69 pub const fn num_glyphs(&self) -> u16 {
70 (self.raw_words.len() - self.num_h_metrics) as u16
71 }
72
73 const fn h_metrics(&self) -> &'a [LongHorMetricRepr] {
74 unsafe { std::slice::from_raw_parts(self.raw_words.as_ptr().cast(), self.num_h_metrics) }
75 }
76
77 pub const fn last_advance_width(&self) -> Option<u16> {
78 Some(self.h_metrics().last()?.advance_width.get())
79 }
80
81 pub const fn metric(&self, glyph_id: GlyphId) -> Option<LongHorMetric> {
82 struct _CodeExplanation;
114
115 let idx: usize = glyph_id.into();
116 let min = (idx as isize).min(self.num_h_metrics.wrapping_sub(1) as isize) as usize;
118
119 Some(LongHorMetric {
120 lsb: self.raw_words.get(min.wrapping_add(idx).wrapping_add(1))?.get(),
122
123 aw: {
124 if self.num_h_metrics != 0 {
125 unsafe { self.raw_words.get_unchecked(min.wrapping_mul(2)) }.get() as u16
127 } else {
128 0
130 }
131 },
132 })
133 }
134
135 pub const fn iter(&self) -> Iter<'_> {
136 Iter::new(*self)
137 }
138}
139
140const impl<'a> IntoIterator for HmtxTableHandle<'a> {
141 type Item = (GlyphId, LongHorMetric);
142 type IntoIter = Iter<'a>;
143 fn into_iter(self) -> Self::IntoIter {
144 Iter::new(self)
145 }
146}
147const impl<'a> IntoIterator for &HmtxTableHandle<'a> {
148 type Item = (GlyphId, LongHorMetric);
149 type IntoIter = Iter<'a>;
150 fn into_iter(self) -> Self::IntoIter {
151 Iter::new(*self)
152 }
153}
154
155#[derive(Clone)]
157pub struct Iter<'a> {
158 glyph_id: u16,
159 num_h_metrics: u16,
160 default_aw: u16,
161 raw_words: std::slice::Iter<'a, FWORD>,
162}
163
164impl<'a> Iter<'a> {
165 pub const fn new(hmtx: HmtxTableHandle<'a>) -> Self {
166 Self {
167 glyph_id: 0,
168 num_h_metrics: hmtx.num_h_metrics(),
169 default_aw: hmtx.last_advance_width().unwrap_or(0),
170 raw_words: hmtx.raw_words.iter(),
171 }
172 }
173}
174
175impl Iterator for Iter<'_> {
176 type Item = (GlyphId, LongHorMetric);
177
178 fn next(&mut self) -> Option<Self::Item> {
179 let aw = if self.glyph_id < self.num_h_metrics {
180 self.raw_words.next()?.get() as u16
181 } else {
182 self.default_aw
183 };
184 let lsb = self.raw_words.next()?.get();
185
186 let id = GlyphId::new(self.glyph_id);
187 self.glyph_id += 1;
188 Some((id, LongHorMetric::new(aw, lsb)))
189 }
190
191 fn size_hint(&self) -> (usize, Option<usize>) {
192 let len = self.len();
193 (len, Some(len))
194 }
195}
196impl ExactSizeIterator for Iter<'_> {
197 fn len(&self) -> usize {
198 let h_metrics_left = self.num_h_metrics.saturating_sub(self.glyph_id);
199 self.raw_words.len() - h_metrics_left as usize
200 }
201}
202impl std::iter::FusedIterator for Iter<'_> {}