1use crate::{
2 tables::{Table, TableDirectory, TableError, cmap::GlyphId},
3 types::{FWORD, Tag, UFWORD, tags},
4};
5
6#[repr(C)]
9struct HmtxRaw {
10 raw_words: [FWORD; 0],
13 }
16
17#[repr(C)]
18pub struct LongHorMetricRaw {
19 pub advance_width: UFWORD,
20 pub lsb: FWORD,
21}
22
23impl<'a> Table<'a> for Hmtx<'a> {
24 const TAG: Tag = tags::hmtx;
25 fn new_in(dir: &'a TableDirectory) -> Result<Self, TableError> {
26 let rec = dir.table_record(Self::TAG).ok_or(TableError::NotFound)?;
27 let raw_words = rec.raw_as::<HmtxRaw>().unwrap().raw_words.as_ptr();
28
29 let num_h_metrics = dir
30 .hhea()
31 .map_err(|_| TableError::Dependency(tags::hhea))?
32 .number_of_h_metrics()
33 .ok_or(TableError::DependencyError(&"number_of_h_metrics not found"))?
34 .get() as usize;
35
36 let num_glyphs = dir
37 .maxp()
38 .map_err(|_| TableError::Dependency(tags::maxp))?
39 .num_glyphs()
40 .ok_or(TableError::DependencyError(&"num_glyphs not found"))?
41 .get() as usize;
42
43 let total_word_count = num_h_metrics + num_glyphs;
44 let raw_words = unsafe { std::slice::from_raw_parts(raw_words, total_word_count) };
45
46 if (rec.length.get() as usize) < total_word_count * size_of::<FWORD>() {
47 return Err(TableError::InvalidLen);
48 }
49
50 Ok(Self { raw_words, num_h_metrics })
51 }
52}
53
54#[derive(Copy)]
55#[derive_const(Clone)]
56pub struct Hmtx<'a> {
57 raw_words: &'a [FWORD],
58 num_h_metrics: usize,
59}
60
61#[derive(Debug, Copy, Hash)]
62#[derive_const(Clone, Default, PartialEq, Eq)]
63pub struct LongHorMetric {
64 pub aw: u16,
65 pub lsb: i16,
66}
67
68impl LongHorMetric {
69 pub const fn new(aw: u16, lsb: i16) -> Self {
70 Self { aw, lsb }
71 }
72}
73const impl From<&LongHorMetricRaw> for LongHorMetric {
74 fn from(value: &LongHorMetricRaw) -> Self {
75 Self { aw: value.advance_width.get(), lsb: value.lsb.get() }
76 }
77}
78
79impl<'a> Hmtx<'a> {
80 pub const fn num_h_metrics(&self) -> u16 {
81 self.num_h_metrics as u16
82 }
83 pub const fn num_glyphs(&self) -> u16 {
84 (self.raw_words.len() - self.num_h_metrics) as u16
85 }
86
87 const fn h_metrics(&self) -> &'a [LongHorMetricRaw] {
88 unsafe { std::slice::from_raw_parts(self.raw_words.as_ptr().cast(), self.num_h_metrics) }
89 }
90
91 pub const fn last_advance_width(&self) -> Option<u16> {
92 Some(self.h_metrics().last()?.advance_width.get())
93 }
94
95 pub const fn metric(&self, glyph_id: GlyphId) -> Option<LongHorMetric> {
96 struct _CodeExplanation;
128
129 let idx: usize = glyph_id.into();
130 let min = (idx as isize).min(self.num_h_metrics.wrapping_sub(1) as isize) as usize;
132
133 Some(LongHorMetric {
134 lsb: self.raw_words.get(min.wrapping_add(idx).wrapping_add(1))?.get(),
136
137 aw: {
138 if self.num_h_metrics != 0 {
139 unsafe { self.raw_words.get_unchecked(min.wrapping_mul(2)) }.get() as u16
141 } else {
142 0
144 }
145 },
146 })
147 }
148
149 pub const fn iter(&self) -> Iter<'_> {
150 Iter::new(*self)
151 }
152}
153
154const impl<'a> IntoIterator for Hmtx<'a> {
155 type Item = (GlyphId, LongHorMetric);
156 type IntoIter = Iter<'a>;
157 fn into_iter(self) -> Self::IntoIter {
158 Iter::new(self)
159 }
160}
161const impl<'a> IntoIterator for &Hmtx<'a> {
162 type Item = (GlyphId, LongHorMetric);
163 type IntoIter = Iter<'a>;
164 fn into_iter(self) -> Self::IntoIter {
165 Iter::new(*self)
166 }
167}
168
169#[derive(Clone)]
171pub struct Iter<'a> {
172 glyph_id: u16,
173 num_h_metrics: u16,
174 default_aw: u16,
175 raw_words: std::slice::Iter<'a, FWORD>,
176}
177
178impl<'a> Iter<'a> {
179 pub const fn new(hmtx: Hmtx<'a>) -> Self {
180 Self {
181 glyph_id: 0,
182 num_h_metrics: hmtx.num_h_metrics(),
183 default_aw: hmtx.last_advance_width().unwrap_or(0),
184 raw_words: hmtx.raw_words.iter(),
185 }
186 }
187}
188
189impl Iterator for Iter<'_> {
190 type Item = (GlyphId, LongHorMetric);
191
192 fn next(&mut self) -> Option<Self::Item> {
193 let aw = if self.glyph_id < self.num_h_metrics {
194 self.raw_words.next()?.get() as u16
195 } else {
196 self.default_aw
197 };
198 let lsb = self.raw_words.next()?.get();
199
200 let id = GlyphId::new(self.glyph_id);
201 self.glyph_id += 1;
202 Some((id, LongHorMetric::new(aw, lsb)))
203 }
204
205 fn size_hint(&self) -> (usize, Option<usize>) {
206 let len = self.len();
207 (len, Some(len))
208 }
209}
210impl ExactSizeIterator for Iter<'_> {
211 fn len(&self) -> usize {
212 let h_metrics_left = self.num_h_metrics.saturating_sub(self.glyph_id);
213 self.raw_words.len() - h_metrics_left as usize
214 }
215}
216impl std::iter::FusedIterator for Iter<'_> {}
217
218