Skip to main content

ttf_view/tables/
loca.rs

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    // Note: this fn is not public because LocaFormat is not exhaustive.
19    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}