1 //! Parsing for PostScript INDEX objects.
2 //!
3 //! See <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#5-index-data>
4 
5 use super::{Error, Index1, Index2};
6 use crate::codegen_prelude::*;
7 
8 /// Common type for uniform access to CFF and CFF2 index formats.
9 #[derive(Clone)]
10 pub enum Index<'a> {
11     Format1(Index1<'a>),
12     Format2(Index2<'a>),
13 }
14 
15 impl<'a> Index<'a> {
16     /// Creates a new index from the given data.
17     ///
18     /// The caller must specify whether the data comes from a `CFF2` table.
new(data: &'a [u8], is_cff2: bool) -> Result<Self, Error>19     pub fn new(data: &'a [u8], is_cff2: bool) -> Result<Self, Error> {
20         let data = FontData::new(data);
21         Ok(if is_cff2 {
22             Index2::read(data).map(|ix| ix.into())?
23         } else {
24             Index1::read(data).map(|ix| ix.into())?
25         })
26     }
27 
28     /// Returns the number of objects in the index.
count(&self) -> u3229     pub fn count(&self) -> u32 {
30         match self {
31             Self::Format1(ix) => ix.count() as u32,
32             Self::Format2(ix) => ix.count(),
33         }
34     }
35 
36     /// Computes a bias that is added to a subroutine operator in a
37     /// charstring.
38     ///
39     /// See <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#9-local-and-global-subr-indexes>
subr_bias(&self) -> i3240     pub fn subr_bias(&self) -> i32 {
41         let count = self.count();
42         if count < 1240 {
43             107
44         } else if count < 33900 {
45             1131
46         } else {
47             32768
48         }
49     }
50 
51     /// Returns the total size in bytes of the index table.
size_in_bytes(&self) -> Result<usize, ReadError>52     pub fn size_in_bytes(&self) -> Result<usize, ReadError> {
53         match self {
54             Self::Format1(ix) => ix.size_in_bytes(),
55             Self::Format2(ix) => ix.size_in_bytes(),
56         }
57     }
58 
59     /// Returns the offset at the given index.
get_offset(&self, index: usize) -> Result<usize, Error>60     pub fn get_offset(&self, index: usize) -> Result<usize, Error> {
61         match self {
62             Self::Format1(ix) => ix.get_offset(index),
63             Self::Format2(ix) => ix.get_offset(index),
64         }
65     }
66 
67     /// Returns the data for the object at the given index.
get(&self, index: usize) -> Result<&'a [u8], Error>68     pub fn get(&self, index: usize) -> Result<&'a [u8], Error> {
69         match self {
70             Self::Format1(ix) => ix.get(index),
71             Self::Format2(ix) => ix.get(index),
72         }
73     }
74 }
75 
76 impl<'a> From<Index1<'a>> for Index<'a> {
from(value: Index1<'a>) -> Self77     fn from(value: Index1<'a>) -> Self {
78         Self::Format1(value)
79     }
80 }
81 
82 impl<'a> From<Index2<'a>> for Index<'a> {
from(value: Index2<'a>) -> Self83     fn from(value: Index2<'a>) -> Self {
84         Self::Format2(value)
85     }
86 }
87 
88 impl<'a> Index1<'a> {
89     /// Returns the total size in bytes of the index table.
size_in_bytes(&self) -> Result<usize, ReadError>90     pub fn size_in_bytes(&self) -> Result<usize, ReadError> {
91         // 2 byte count + 1 byte off_size
92         const HEADER_SIZE: usize = 3;
93         // An empty CFF index contains only a 2 byte count field
94         const EMPTY_SIZE: usize = 2;
95         let count = self.count() as usize;
96         Ok(match count {
97             0 => EMPTY_SIZE,
98             _ => {
99                 HEADER_SIZE
100                     + self.offsets().len()
101                     + self.get_offset(count).map_err(|_| ReadError::OutOfBounds)?
102             }
103         })
104     }
105 
106     /// Returns the offset of the object at the given index.
get_offset(&self, index: usize) -> Result<usize, Error>107     pub fn get_offset(&self, index: usize) -> Result<usize, Error> {
108         read_offset(
109             index,
110             self.count() as usize,
111             self.off_size(),
112             self.offsets(),
113         )
114     }
115 
116     /// Returns the data for the object at the given index.
get(&self, index: usize) -> Result<&'a [u8], Error>117     pub fn get(&self, index: usize) -> Result<&'a [u8], Error> {
118         self.data()
119             .get(self.get_offset(index)?..self.get_offset(index + 1)?)
120             .ok_or(ReadError::OutOfBounds.into())
121     }
122 }
123 
124 impl<'a> Index2<'a> {
125     /// Returns the total size in bytes of the index table.
size_in_bytes(&self) -> Result<usize, ReadError>126     pub fn size_in_bytes(&self) -> Result<usize, ReadError> {
127         // 4 byte count + 1 byte off_size
128         const HEADER_SIZE: usize = 5;
129         // An empty CFF2 index contains only a 4 byte count field
130         const EMPTY_SIZE: usize = 4;
131         let count = self.count() as usize;
132         Ok(match count {
133             0 => EMPTY_SIZE,
134             _ => {
135                 HEADER_SIZE
136                     + self.offsets().len()
137                     + self.get_offset(count).map_err(|_| ReadError::OutOfBounds)?
138             }
139         })
140     }
141 
142     /// Returns the offset of the object at the given index.
get_offset(&self, index: usize) -> Result<usize, Error>143     pub fn get_offset(&self, index: usize) -> Result<usize, Error> {
144         read_offset(
145             index,
146             self.count() as usize,
147             self.off_size(),
148             self.offsets(),
149         )
150     }
151 
152     /// Returns the data for the object at the given index.
get(&self, index: usize) -> Result<&'a [u8], Error>153     pub fn get(&self, index: usize) -> Result<&'a [u8], Error> {
154         self.data()
155             .get(self.get_offset(index)?..self.get_offset(index + 1)?)
156             .ok_or(ReadError::OutOfBounds.into())
157     }
158 }
159 
160 /// Reads an offset which is encoded as a variable sized integer.
read_offset( index: usize, count: usize, offset_size: u8, offset_data: &[u8], ) -> Result<usize, Error>161 fn read_offset(
162     index: usize,
163     count: usize,
164     offset_size: u8,
165     offset_data: &[u8],
166 ) -> Result<usize, Error> {
167     // There are actually count + 1 entries in the offset array.
168     //
169     // "Offsets in the offset array are relative to the byte that precedes
170     // the object data. Therefore the first element of the offset array is
171     // always 1. (This ensures that every object has a corresponding offset
172     // which is always nonzero and permits the efficient implementation of
173     // dynamic object loading.)"
174     //
175     // See <https://learn.microsoft.com/en-us/typography/opentype/spec/cff2#table-7-index-format>
176     if index > count {
177         Err(ReadError::OutOfBounds)?;
178     }
179     let data_offset = index * offset_size as usize;
180     let offset_data = FontData::new(offset_data);
181     match offset_size {
182         1 => offset_data.read_at::<u8>(data_offset)? as usize,
183         2 => offset_data.read_at::<u16>(data_offset)? as usize,
184         3 => offset_data.read_at::<Uint24>(data_offset)?.to_u32() as usize,
185         4 => offset_data.read_at::<u32>(data_offset)? as usize,
186         _ => return Err(Error::InvalidIndexOffsetSize(offset_size)),
187     }
188     // As above, subtract one to get the actual offset.
189     .checked_sub(1)
190     .ok_or(Error::ZeroOffsetInIndex)
191 }
192 
193 #[cfg(test)]
194 mod tests {
195     use super::*;
196     use crate::test_helpers::BeBuffer;
197 
198     enum IndexParams {
199         Format1 { off_size: u8, count: usize },
200         Format2 { off_size: u8, count: usize },
201     }
202 
203     #[test]
index_format1_offsize1_count4()204     fn index_format1_offsize1_count4() {
205         test_index(IndexParams::Format1 {
206             off_size: 1,
207             count: 4,
208         });
209     }
210 
211     #[test]
index_format1_offsize2_count64()212     fn index_format1_offsize2_count64() {
213         test_index(IndexParams::Format1 {
214             off_size: 2,
215             count: 64,
216         });
217     }
218 
219     #[test]
index_format1_offsize3_count128()220     fn index_format1_offsize3_count128() {
221         test_index(IndexParams::Format1 {
222             off_size: 3,
223             count: 128,
224         });
225     }
226 
227     #[test]
index_format1_offsize4_count256()228     fn index_format1_offsize4_count256() {
229         test_index(IndexParams::Format1 {
230             off_size: 4,
231             count: 256,
232         });
233     }
234 
235     #[test]
index_format2_offsize1_count4()236     fn index_format2_offsize1_count4() {
237         test_index(IndexParams::Format2 {
238             off_size: 4,
239             count: 256,
240         });
241     }
242 
243     #[test]
index_format2_offsize2_count64()244     fn index_format2_offsize2_count64() {
245         test_index(IndexParams::Format2 {
246             off_size: 2,
247             count: 64,
248         });
249     }
250 
251     #[test]
index_format2_offsize3_count128()252     fn index_format2_offsize3_count128() {
253         test_index(IndexParams::Format2 {
254             off_size: 3,
255             count: 128,
256         });
257     }
258 
259     #[test]
index_format2_offsize4_count256()260     fn index_format2_offsize4_count256() {
261         test_index(IndexParams::Format2 {
262             off_size: 4,
263             count: 256,
264         });
265     }
266 
test_index(params: IndexParams)267     fn test_index(params: IndexParams) {
268         let (fmt, off_size, count) = match params {
269             IndexParams::Format1 { off_size, count } => (1, off_size, count),
270             IndexParams::Format2 { off_size, count } => (2, off_size, count),
271         };
272         let buf = make_index(fmt, off_size, count);
273         let index = Index::new(buf.font_data().as_bytes(), fmt == 2).unwrap();
274         let built_off_size = match &index {
275             Index::Format1(v1) => v1.off_size(),
276             Index::Format2(v2) => v2.off_size(),
277         };
278         assert_eq!(built_off_size, off_size);
279         assert_eq!(index.count(), count as u32);
280         for i in 0..count {
281             let object = index.get(i).unwrap();
282             let expected_len = (i + 1) * 10;
283             let expected_bytes = vec![i as u8; expected_len];
284             assert_eq!(object, expected_bytes);
285         }
286     }
287 
make_index(fmt: u8, off_size: u8, count: usize) -> BeBuffer288     fn make_index(fmt: u8, off_size: u8, count: usize) -> BeBuffer {
289         // We'll add `count` objects to the INDEX, each containing
290         // `(i + 1) * 10` bytes of the value `i`.
291         let mut buf = BeBuffer::new();
292         match fmt {
293             1 => buf = buf.push(count as u16),
294             2 => buf = buf.push(count as u32),
295             _ => panic!("INDEX fmt should be 1 or 2"),
296         }
297         if count == 0 {
298             return buf;
299         }
300         buf = buf.push(off_size);
301         // Offsets start at 1.
302         let mut offset = 1usize;
303         for i in 0..count + 1 {
304             buf = match off_size {
305                 1 => buf.push(offset as u8),
306                 2 => buf.push(offset as u16),
307                 3 => buf.push(Uint24::checked_new(offset as u32).unwrap()),
308                 4 => buf.push(offset as u32),
309                 _ => panic!("off_size should be 1-4"),
310             };
311             offset += (i + 1) * 10;
312         }
313         // Now the data
314         for i in 0..count {
315             buf = buf.extend(std::iter::repeat(i as u8).take((i + 1) * 10));
316         }
317         buf
318     }
319 }
320