1use crate::{
2 tables::{Table, TableDirectory, TableError, cmap::GlyphId},
3 types::{Offset16, Offset32, Tag, tags},
4 util::{PackedDualIter, custom_iterator},
5};
6use std::{num::NonZero, ops::Range, ptr::NonNull};
7
8#[derive(Debug, Copy, Hash)]
9#[derive_const(Clone, PartialEq, Eq, PartialOrd, Ord)]
10#[repr(i16)]
11#[non_exhaustive]
12pub enum LocaFormat {
13 Short = 0,
14 Long = 1,
15}
16
17impl LocaFormat {
18 const fn stride(&self) -> u8 {
20 match self {
21 Self::Short => 2,
22 Self::Long => 4,
23 }
24 }
25}
26const impl TryFrom<i16> for LocaFormat {
27 type Error = ();
28 fn try_from(value: i16) -> Result<Self, Self::Error> {
29 Ok(match value {
30 0 => Self::Short,
31 1 => Self::Long,
32 _ => return Err(()),
33 })
34 }
35}
36
37#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38#[non_exhaustive]
39pub enum LocaOffsets<'a> {
40 Short(&'a [Offset16]),
41 Long(&'a [Offset32]),
42}
43
44impl<'a> Table<'a> for Loca<'a> {
45 const TAG: Tag = tags::loca;
46 fn new_in(dir: &'a TableDirectory) -> Result<Self, TableError> {
47 let rec = dir.table_record(Self::TAG).ok_or(TableError::NotFound)?;
48
49 let format = dir
50 .head()
51 .map_err(|_| TableError::Dependency(tags::head))?
52 .index_to_loc_format()
53 .ok_or(TableError::DependencyError(&"index_to_loc_format not found"))
54 .and_then(|format| {
55 LocaFormat::try_from(format.get()).map_err(|_| TableError::UnknownFormat)
56 })?;
57
58 let num_glyphs = dir
59 .maxp()
60 .map_err(|_| TableError::Dependency(tags::maxp))?
61 .num_glyphs()
62 .ok_or(TableError::DependencyError(&"num_glyphs not found"))?
63 .get();
64
65 let ptr = NonNull::from_ref(rec.raw_as::<()>().unwrap());
66
67 let req_len = (num_glyphs as usize + 1) * format.stride() as usize;
68 if (rec.length.get() as usize) < req_len {
69 return Err(TableError::InvalidLen);
70 }
71
72 let loca = Self { ptr, num_glyphs, format, _phantom: Default::default() };
73
74 if !loca.iter_raw().is_sorted() {
75 return Err(TableError::Malformed(&"'loca' offsets not in order"));
76 }
77
78 Ok(loca)
79 }
80}
81
82#[derive(Copy)]
83#[derive_const(Clone)]
84pub struct Loca<'a> {
85 ptr: NonNull<()>,
86 num_glyphs: u16,
87 format: LocaFormat,
88 _phantom: std::marker::PhantomData<&'a ()>,
89}
90
91impl<'a> Loca<'a> {
92 pub const fn num_glyphs(&self) -> u16 {
93 self.num_glyphs
94 }
95 pub const fn format(&self) -> LocaFormat {
96 self.format
97 }
98 pub const fn offsets(&self) -> LocaOffsets<'a> {
99 let len = self.num_glyphs as usize + 1;
100 match self.format {
101 LocaFormat::Short => LocaOffsets::Short(unsafe {
102 std::slice::from_raw_parts(self.ptr.as_ptr().cast(), len)
103 }),
104 LocaFormat::Long => LocaOffsets::Long(unsafe {
105 std::slice::from_raw_parts(self.ptr.as_ptr().cast(), len)
106 }),
107 }
108 }
109
110 pub fn range(&self, glyph_id: GlyphId) -> Option<Range<u32>> {
111 Some(self.iter().nth(glyph_id.get() as usize)?.1)
112 }
113
114 pub fn iter_raw(&self) -> RawIter<'a> {
115 RawIter::new(*self)
116 }
117 pub fn iter(&self) -> Iter<'a> {
118 Iter::new(*self)
119 }
120}
121
122#[derive(Clone)]
123pub struct RawIter<'a> {
124 inner: PackedDualIter<'a, u32, Offset32, Offset16>,
125}
126impl<'a> RawIter<'a> {
127 pub fn new(loca: Loca<'a>) -> Self {
128 let inner = match loca.offsets() {
129 LocaOffsets::Long(offsets) => PackedDualIter::new_a(offsets),
130 LocaOffsets::Short(offsets) => PackedDualIter::new_b(offsets),
131 };
132 Self { inner }
133 }
134}
135custom_iterator!(RawIter<'a> as this {
136 type Item = u32;
137 map: |x| x;
138});
139
140#[derive(Clone)]
141pub struct Iter<'a> {
142 inner: RawIter<'a>,
143 glyph_id: u16,
144 prev: u32,
145}
146impl<'a> Iter<'a> {
147 pub fn new(loca: Loca<'a>) -> Self {
148 let mut inner = RawIter::new(loca);
149 let first = inner.next().unwrap();
150 Self { inner, glyph_id: 0, prev: first }
151 }
152}
153impl<'a> Iterator for Iter<'a> {
154 type Item = (GlyphId, Range<u32>);
155 fn next(&mut self) -> Option<Self::Item> {
156 let next = self.inner.next()?;
157 let ret = (self.glyph_id.into(), self.prev..next);
158 self.prev = next;
159 self.glyph_id = self.glyph_id.wrapping_add(1);
160 Some(ret)
161 }
162 fn advance_by(&mut self, n: usize) -> Result<(), NonZero<usize>> {
163 let advance = self.len().min(n);
164 if advance > 0 {
165 self.prev = self.inner.nth(advance - 1).unwrap();
166 }
167 NonZero::new(n - advance).map_or(Ok(()), Err)
168 }
169 fn nth(&mut self, n: usize) -> Option<Self::Item> {
170 self.advance_by(n).ok()?;
171 self.next()
172 }
173 fn try_fold<B, F, R>(&mut self, init: B, mut f: F) -> R
174 where
175 F: FnMut(B, Self::Item) -> R,
176 R: std::ops::Try<Output = B>,
177 {
178 self.inner.try_fold(init, |init, next| {
179 let ret = (self.glyph_id.into(), self.prev..next);
180 self.prev = next;
181 self.glyph_id = self.glyph_id.wrapping_add(1);
182 f(init, ret)
183 })
184 }
185 fn fold<B, F>(mut self, init: B, mut f: F) -> B
186 where F: FnMut(B, Self::Item) -> B {
187 self.try_fold(init, |init, x| Ok::<_, !>(f(init, x))).unwrap()
188 }
189 fn last(mut self) -> Option<Self::Item> {
190 self.nth(self.len() - 1)
191 }
192 fn size_hint(&self) -> (usize, Option<usize>) {
193 let len = self.len();
194 (len, Some(len))
195 }
196}
197impl<'a> ExactSizeIterator for Iter<'a> {
198 fn len(&self) -> usize {
199 self.inner.len()
200 }
201}
202
203impl std::fmt::Debug for Loca<'_> {
204 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
205 let mut f = f.debug_struct("loca");
206
207 f.field_with("index_to_loc_format", |f| {
208 write!(f, "{} ({:?})", self.format as i16, self.format)
209 });
210 f.field("num_glyphs", &self.num_glyphs());
211
212 f.field_with("offsets", |f| {
213 write!(f, "{{")?;
214
215 let id_width = self.num_glyphs.checked_ilog10().unwrap_or(0) as usize + 1;
216 let last_offset = self.iter_raw().last();
217 let offset_width =
218 last_offset.and_then(|x| x.checked_ilog(16)).unwrap_or(0) as usize + 3;
219
220 for (glyph_id, range) in self.iter() {
221 if glyph_id.get() % 5 == 0 {
222 write!(f, "\n ")?;
223 }
224 write!(f, " {:id_width$}: {:#0offset_width$X},", glyph_id, range.start)?;
225 }
226 if let Some(last_offset) = last_offset {
227 write!(f, " {:>id_width$}: {:#0offset_width$X},", "_", last_offset)?;
228 }
229
230 write!(f, "\n}}")
231 });
232
233 f.finish()
234 }
235}