read_fonts/
offset_array.rs

1//! Arrays of offsets with dynamic resolution
2//!
3//! This module provides a number of types that wrap arrays of offsets, dynamically
4//! resolving individual offsets as they are accessed.
5
6use crate::offset::ResolveNullableOffset;
7use font_types::{BigEndian, Nullable, Offset16, Scalar};
8
9use crate::{FontData, FontReadWithArgs, Offset, ReadArgs, ReadError, ResolveOffset};
10
11/// An array of offsets that can be resolved on access.
12///
13/// This bundles up the raw offsets with the data used to resolve them, along
14/// with any arguments needed to resolve those offsets; it provides a simple
15/// ergonomic interface that unburdens the user from needing to manually
16/// determine the appropriate input data and arguments for a raw offset.
17#[derive(Clone)]
18pub struct ArrayOfOffsets<'a, T: ReadArgs, O: Scalar = Offset16> {
19    offsets: &'a [BigEndian<O>],
20    data: FontData<'a>,
21    args: T::Args,
22}
23
24/// An array of nullable offsets that can be resolved on access.
25///
26/// This is identical to [`ArrayOfOffsets`], except that each offset is
27/// allowed to be null.
28#[derive(Clone)]
29pub struct ArrayOfNullableOffsets<'a, T: ReadArgs, O: Scalar = Offset16> {
30    offsets: &'a [BigEndian<Nullable<O>>],
31    data: FontData<'a>,
32    args: T::Args,
33}
34
35impl<'a, T, O> ArrayOfOffsets<'a, T, O>
36where
37    O: Scalar,
38    T: ReadArgs,
39{
40    pub(crate) fn new(offsets: &'a [BigEndian<O>], data: FontData<'a>, args: T::Args) -> Self {
41        Self {
42            offsets,
43            data,
44            args,
45        }
46    }
47}
48
49impl<'a, T, O> ArrayOfOffsets<'a, T, O>
50where
51    O: Scalar + Offset,
52    T: ReadArgs + FontReadWithArgs<'a>,
53    T::Args: Copy + 'static,
54{
55    /// The number of offsets in the array
56    pub fn len(&self) -> usize {
57        self.offsets.len()
58    }
59
60    /// `true` if the array is empty
61    pub fn is_empty(&self) -> bool {
62        self.offsets.is_empty()
63    }
64
65    /// Resolve the offset at the provided index.
66    ///
67    /// Note: if the index is invalid this will return the `InvalidCollectionIndex`
68    /// error variant instead of `None`.
69    pub fn get(&self, idx: usize) -> Result<T, ReadError> {
70        self.offsets
71            .get(idx)
72            .ok_or(ReadError::InvalidCollectionIndex(idx as _))
73            .and_then(|o| o.get().resolve_with_args(self.data, &self.args))
74    }
75
76    /// Iterate over all of the offset targets.
77    ///
78    /// Each offset will be resolved as it is encountered.
79    pub fn iter(&self) -> impl Iterator<Item = Result<T, ReadError>> + 'a {
80        let mut iter = self.offsets.iter();
81        let args = self.args;
82        let data = self.data;
83        std::iter::from_fn(move || {
84            iter.next()
85                .map(|off| off.get().resolve_with_args(data, &args))
86        })
87    }
88}
89
90impl<'a, T, O> ArrayOfNullableOffsets<'a, T, O>
91where
92    O: Scalar + Offset,
93    T: ReadArgs,
94{
95    pub(crate) fn new(
96        offsets: &'a [BigEndian<Nullable<O>>],
97        data: FontData<'a>,
98        args: T::Args,
99    ) -> Self {
100        Self {
101            offsets,
102            data,
103            args,
104        }
105    }
106}
107
108impl<'a, T, O> ArrayOfNullableOffsets<'a, T, O>
109where
110    O: Scalar + Offset,
111    T: ReadArgs + FontReadWithArgs<'a>,
112    T::Args: Copy + 'static,
113{
114    /// The number of offsets in the array
115    pub fn len(&self) -> usize {
116        self.offsets.len()
117    }
118
119    /// `true` if the array is empty
120    pub fn is_empty(&self) -> bool {
121        self.offsets.is_empty()
122    }
123
124    /// Resolve the offset at the provided index.
125    ///
126    /// This will return `None` only if the offset *exists*, but is null. if the
127    /// provided index does not exist, this will return the `InvalidCollectionIndex`
128    /// error variant.
129    pub fn get(&self, idx: usize) -> Option<Result<T, ReadError>> {
130        let Some(offset) = self.offsets.get(idx) else {
131            return Some(Err(ReadError::InvalidCollectionIndex(idx as _)));
132        };
133        offset.get().resolve_with_args(self.data, &self.args)
134    }
135
136    /// Iterate over all of the offset targets.
137    ///
138    /// Each offset will be resolved as it is encountered.
139    pub fn iter(&self) -> impl Iterator<Item = Option<Result<T, ReadError>>> + 'a {
140        let mut iter = self.offsets.iter();
141        let args = self.args;
142        let data = self.data;
143        std::iter::from_fn(move || {
144            iter.next()
145                .map(|off| off.get().resolve_with_args(data, &args))
146        })
147    }
148}