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