Skip to main content

read_fonts/generated/
font.rs

1// THIS FILE IS AUTOGENERATED.
2// Any changes to this file will be overwritten.
3// For more information about how codegen works, see font-codegen/README.md
4
5#[allow(unused_imports)]
6use crate::codegen_prelude::*;
7
8impl<'a> MinByteRange<'a> for TableDirectory<'a> {
9    fn min_byte_range(&self) -> Range<usize> {
10        0..self.table_records_byte_range().end
11    }
12    fn min_table_bytes(&self) -> &'a [u8] {
13        let range = self.min_byte_range();
14        self.data.as_bytes().get(range).unwrap_or_default()
15    }
16}
17
18impl<'a> FontRead<'a> for TableDirectory<'a> {
19    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
20        #[allow(clippy::absurd_extreme_comparisons)]
21        if data.len() < Self::MIN_SIZE {
22            return Err(ReadError::OutOfBounds);
23        }
24        Ok(Self { data })
25    }
26}
27
28/// The OpenType [Table Directory](https://docs.microsoft.com/en-us/typography/opentype/spec/otff#table-directory)
29#[derive(Clone)]
30pub struct TableDirectory<'a> {
31    data: FontData<'a>,
32}
33
34#[allow(clippy::needless_lifetimes)]
35impl<'a> TableDirectory<'a> {
36    pub const MIN_SIZE: usize = (u32::RAW_BYTE_LEN
37        + u16::RAW_BYTE_LEN
38        + u16::RAW_BYTE_LEN
39        + u16::RAW_BYTE_LEN
40        + u16::RAW_BYTE_LEN);
41    basic_table_impls!(impl_the_methods);
42
43    /// 0x00010000 or 0x4F54544F
44    pub fn sfnt_version(&self) -> u32 {
45        let range = self.sfnt_version_byte_range();
46        self.data.read_at(range.start).ok().unwrap()
47    }
48
49    /// Number of tables.
50    pub fn num_tables(&self) -> u16 {
51        let range = self.num_tables_byte_range();
52        self.data.read_at(range.start).ok().unwrap()
53    }
54
55    pub fn search_range(&self) -> u16 {
56        let range = self.search_range_byte_range();
57        self.data.read_at(range.start).ok().unwrap()
58    }
59
60    pub fn entry_selector(&self) -> u16 {
61        let range = self.entry_selector_byte_range();
62        self.data.read_at(range.start).ok().unwrap()
63    }
64
65    pub fn range_shift(&self) -> u16 {
66        let range = self.range_shift_byte_range();
67        self.data.read_at(range.start).ok().unwrap()
68    }
69
70    /// Table records array—one for each top-level table in the font
71    pub fn table_records(&self) -> &'a [TableRecord] {
72        let range = self.table_records_byte_range();
73        self.data.read_array(range).ok().unwrap_or_default()
74    }
75
76    pub fn sfnt_version_byte_range(&self) -> Range<usize> {
77        let start = 0;
78        let end = start + u32::RAW_BYTE_LEN;
79        start..end
80    }
81
82    pub fn num_tables_byte_range(&self) -> Range<usize> {
83        let start = self.sfnt_version_byte_range().end;
84        let end = start + u16::RAW_BYTE_LEN;
85        start..end
86    }
87
88    pub fn search_range_byte_range(&self) -> Range<usize> {
89        let start = self.num_tables_byte_range().end;
90        let end = start + u16::RAW_BYTE_LEN;
91        start..end
92    }
93
94    pub fn entry_selector_byte_range(&self) -> Range<usize> {
95        let start = self.search_range_byte_range().end;
96        let end = start + u16::RAW_BYTE_LEN;
97        start..end
98    }
99
100    pub fn range_shift_byte_range(&self) -> Range<usize> {
101        let start = self.entry_selector_byte_range().end;
102        let end = start + u16::RAW_BYTE_LEN;
103        start..end
104    }
105
106    pub fn table_records_byte_range(&self) -> Range<usize> {
107        let num_tables = self.num_tables();
108        let start = self.range_shift_byte_range().end;
109        let end =
110            start + (transforms::to_usize(num_tables)).saturating_mul(TableRecord::RAW_BYTE_LEN);
111        start..end
112    }
113}
114
115const _: () = assert!(FontData::default_data_long_enough(TableDirectory::MIN_SIZE));
116
117impl Default for TableDirectory<'_> {
118    fn default() -> Self {
119        Self {
120            data: FontData::default_table_data(),
121        }
122    }
123}
124
125#[cfg(feature = "experimental_traverse")]
126impl<'a> SomeTable<'a> for TableDirectory<'a> {
127    fn type_name(&self) -> &str {
128        "TableDirectory"
129    }
130    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
131        match idx {
132            0usize => Some(Field::new("sfnt_version", self.sfnt_version())),
133            1usize => Some(Field::new("num_tables", self.num_tables())),
134            2usize => Some(Field::new("search_range", self.search_range())),
135            3usize => Some(Field::new("entry_selector", self.entry_selector())),
136            4usize => Some(Field::new("range_shift", self.range_shift())),
137            5usize => Some(Field::new(
138                "table_records",
139                traversal::FieldType::array_of_records(
140                    stringify!(TableRecord),
141                    self.table_records(),
142                    self.offset_data(),
143                ),
144            )),
145            _ => None,
146        }
147    }
148}
149
150#[cfg(feature = "experimental_traverse")]
151#[allow(clippy::needless_lifetimes)]
152impl<'a> std::fmt::Debug for TableDirectory<'a> {
153    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154        (self as &dyn SomeTable<'a>).fmt(f)
155    }
156}
157
158/// Record for a table in a font.
159#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, bytemuck :: AnyBitPattern)]
160#[repr(C)]
161#[repr(packed)]
162pub struct TableRecord {
163    /// Table identifier.
164    pub tag: BigEndian<Tag>,
165    /// Checksum for the table.
166    pub checksum: BigEndian<u32>,
167    /// Offset from the beginning of the font data.
168    pub offset: BigEndian<u32>,
169    /// Length of the table.
170    pub length: BigEndian<u32>,
171}
172
173impl TableRecord {
174    /// Table identifier.
175    pub fn tag(&self) -> Tag {
176        self.tag.get()
177    }
178
179    /// Checksum for the table.
180    pub fn checksum(&self) -> u32 {
181        self.checksum.get()
182    }
183
184    /// Offset from the beginning of the font data.
185    pub fn offset(&self) -> u32 {
186        self.offset.get()
187    }
188
189    /// Length of the table.
190    pub fn length(&self) -> u32 {
191        self.length.get()
192    }
193}
194
195impl FixedSize for TableRecord {
196    const RAW_BYTE_LEN: usize =
197        Tag::RAW_BYTE_LEN + u32::RAW_BYTE_LEN + u32::RAW_BYTE_LEN + u32::RAW_BYTE_LEN;
198}
199
200#[cfg(feature = "experimental_traverse")]
201impl<'a> SomeRecord<'a> for TableRecord {
202    fn traverse(self, data: FontData<'a>) -> RecordResolver<'a> {
203        RecordResolver {
204            name: "TableRecord",
205            get_field: Box::new(move |idx, _data| match idx {
206                0usize => Some(Field::new("tag", self.tag())),
207                1usize => Some(Field::new("checksum", self.checksum())),
208                2usize => Some(Field::new("offset", self.offset())),
209                3usize => Some(Field::new("length", self.length())),
210                _ => None,
211            }),
212            data,
213        }
214    }
215}
216
217impl<'a> MinByteRange<'a> for TTCHeader<'a> {
218    fn min_byte_range(&self) -> Range<usize> {
219        0..self.table_directory_offsets_byte_range().end
220    }
221    fn min_table_bytes(&self) -> &'a [u8] {
222        let range = self.min_byte_range();
223        self.data.as_bytes().get(range).unwrap_or_default()
224    }
225}
226
227impl<'a> FontRead<'a> for TTCHeader<'a> {
228    fn read(data: FontData<'a>) -> Result<Self, ReadError> {
229        #[allow(clippy::absurd_extreme_comparisons)]
230        if data.len() < Self::MIN_SIZE {
231            return Err(ReadError::OutOfBounds);
232        }
233        Ok(Self { data })
234    }
235}
236
237/// [TTC Header](https://learn.microsoft.com/en-us/typography/opentype/spec/otff#ttc-header)
238#[derive(Clone)]
239pub struct TTCHeader<'a> {
240    data: FontData<'a>,
241}
242
243#[allow(clippy::needless_lifetimes)]
244impl<'a> TTCHeader<'a> {
245    pub const MIN_SIZE: usize = (Tag::RAW_BYTE_LEN + MajorMinor::RAW_BYTE_LEN + u32::RAW_BYTE_LEN);
246    basic_table_impls!(impl_the_methods);
247
248    /// Font Collection ID string: \"ttcf\"
249    pub fn ttc_tag(&self) -> Tag {
250        let range = self.ttc_tag_byte_range();
251        self.data.read_at(range.start).ok().unwrap()
252    }
253
254    /// Major/minor version of the TTC Header
255    pub fn version(&self) -> MajorMinor {
256        let range = self.version_byte_range();
257        self.data.read_at(range.start).ok().unwrap()
258    }
259
260    /// Number of fonts in TTC
261    pub fn num_fonts(&self) -> u32 {
262        let range = self.num_fonts_byte_range();
263        self.data.read_at(range.start).ok().unwrap()
264    }
265
266    /// Array of offsets to the TableDirectory for each font from the beginning of the file
267    pub fn table_directory_offsets(&self) -> &'a [BigEndian<u32>] {
268        let range = self.table_directory_offsets_byte_range();
269        self.data.read_array(range).ok().unwrap_or_default()
270    }
271
272    /// Tag indicating that a DSIG table exists, 0x44534947 ('DSIG') (null if no signature)
273    pub fn dsig_tag(&self) -> Option<u32> {
274        let range = self.dsig_tag_byte_range();
275        (!range.is_empty())
276            .then(|| self.data.read_at(range.start).ok())
277            .flatten()
278    }
279
280    /// The length (in bytes) of the DSIG table (null if no signature)
281    pub fn dsig_length(&self) -> Option<u32> {
282        let range = self.dsig_length_byte_range();
283        (!range.is_empty())
284            .then(|| self.data.read_at(range.start).ok())
285            .flatten()
286    }
287
288    /// The offset (in bytes) of the DSIG table from the beginning of the TTC file (null if no signature)
289    pub fn dsig_offset(&self) -> Option<u32> {
290        let range = self.dsig_offset_byte_range();
291        (!range.is_empty())
292            .then(|| self.data.read_at(range.start).ok())
293            .flatten()
294    }
295
296    pub fn ttc_tag_byte_range(&self) -> Range<usize> {
297        let start = 0;
298        let end = start + Tag::RAW_BYTE_LEN;
299        start..end
300    }
301
302    pub fn version_byte_range(&self) -> Range<usize> {
303        let start = self.ttc_tag_byte_range().end;
304        let end = start + MajorMinor::RAW_BYTE_LEN;
305        start..end
306    }
307
308    pub fn num_fonts_byte_range(&self) -> Range<usize> {
309        let start = self.version_byte_range().end;
310        let end = start + u32::RAW_BYTE_LEN;
311        start..end
312    }
313
314    pub fn table_directory_offsets_byte_range(&self) -> Range<usize> {
315        let num_fonts = self.num_fonts();
316        let start = self.num_fonts_byte_range().end;
317        let end = start + (transforms::to_usize(num_fonts)).saturating_mul(u32::RAW_BYTE_LEN);
318        start..end
319    }
320
321    pub fn dsig_tag_byte_range(&self) -> Range<usize> {
322        let start = self.table_directory_offsets_byte_range().end;
323        let end = if self.version().compatible((2u16, 0u16)) {
324            start + u32::RAW_BYTE_LEN
325        } else {
326            start
327        };
328        start..end
329    }
330
331    pub fn dsig_length_byte_range(&self) -> Range<usize> {
332        let start = self.dsig_tag_byte_range().end;
333        let end = if self.version().compatible((2u16, 0u16)) {
334            start + u32::RAW_BYTE_LEN
335        } else {
336            start
337        };
338        start..end
339    }
340
341    pub fn dsig_offset_byte_range(&self) -> Range<usize> {
342        let start = self.dsig_length_byte_range().end;
343        let end = if self.version().compatible((2u16, 0u16)) {
344            start + u32::RAW_BYTE_LEN
345        } else {
346            start
347        };
348        start..end
349    }
350}
351
352const _: () = assert!(FontData::default_data_long_enough(TTCHeader::MIN_SIZE));
353
354impl Default for TTCHeader<'_> {
355    fn default() -> Self {
356        Self {
357            data: FontData::default_table_data(),
358        }
359    }
360}
361
362#[cfg(feature = "experimental_traverse")]
363impl<'a> SomeTable<'a> for TTCHeader<'a> {
364    fn type_name(&self) -> &str {
365        "TTCHeader"
366    }
367    fn get_field(&self, idx: usize) -> Option<Field<'a>> {
368        match idx {
369            0usize => Some(Field::new("ttc_tag", self.ttc_tag())),
370            1usize => Some(Field::new("version", self.version())),
371            2usize => Some(Field::new("num_fonts", self.num_fonts())),
372            3usize => Some(Field::new(
373                "table_directory_offsets",
374                self.table_directory_offsets(),
375            )),
376            4usize if self.version().compatible((2u16, 0u16)) => {
377                Some(Field::new("dsig_tag", self.dsig_tag().unwrap()))
378            }
379            5usize if self.version().compatible((2u16, 0u16)) => {
380                Some(Field::new("dsig_length", self.dsig_length().unwrap()))
381            }
382            6usize if self.version().compatible((2u16, 0u16)) => {
383                Some(Field::new("dsig_offset", self.dsig_offset().unwrap()))
384            }
385            _ => None,
386        }
387    }
388}
389
390#[cfg(feature = "experimental_traverse")]
391#[allow(clippy::needless_lifetimes)]
392impl<'a> std::fmt::Debug for TTCHeader<'a> {
393    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
394        (self as &dyn SomeTable<'a>).fmt(f)
395    }
396}