Skip to main content

ttf_view/tables/
directory.rs

1use crate::{
2    tables::{Table, TableHandle},
3    types::{Offset32, Tag, tags, uint16, uint32},
4    util::iterator_map,
5};
6
7#[repr(C)]
8#[non_exhaustive]
9pub struct TableDirectoryRepr {
10    pub sfnt_version: uint32,
11    pub num_tables: uint16,
12    pub search_range: uint16,
13    pub entry_selector: uint16,
14    pub range_shift: uint16,
15    table_records: [TableRecordRepr; 0],
16}
17#[repr(C)]
18pub struct TableRecordRepr {
19    pub table_tag: Tag,
20    pub checksum: uint32,
21    pub offset: Offset32,
22    pub length: uint32,
23}
24
25impl TableDirectoryRepr {
26    pub const unsafe fn new_unchecked(bytes: &[u8]) -> &Self {
27        unsafe { &*bytes.as_ptr().cast() }
28    }
29
30    pub const fn directory_as_bytes(&self) -> &[u8] {
31        let start = std::ptr::from_ref(self).cast();
32        let end = self.table_records_raw().as_ptr_range().end.cast();
33        unsafe { std::slice::from_ptr_range(start..end) }
34    }
35
36    pub const fn table_records_raw(&self) -> &[TableRecordRepr] {
37        let len = self.num_tables.get() as usize;
38        unsafe { std::slice::from_raw_parts(self.table_records.as_ptr(), len) }
39    }
40    pub fn table_record_raw(&self, tag: Tag) -> Option<&TableRecordRepr> {
41        self.table_records_raw().iter().find(|x| x.table_tag == tag)
42    }
43
44    pub const fn table_records(&self) -> TableRecordsIter<'_> {
45        TableRecordsIter::new(self)
46    }
47    pub fn table_record(&self, tag: Tag) -> Option<TableRecordHandle<'_>> {
48        Some(TableRecordHandle(self, self.table_record_raw(tag)?))
49    }
50
51    pub fn table_raw<T: Table>(&self) -> Option<&T> {
52        self.table_record(T::TAG)?.table_as()
53    }
54    pub fn table<'a, T: TableHandle<'a>>(&'a self) -> Option<T> {
55        T::in_directory(self)
56    }
57
58    // Note: see src/tables/mod.rs for specific table methods
59}
60
61#[derive(Copy)]
62#[derive_const(Clone)]
63pub struct TableRecordHandle<'a>(&'a TableDirectoryRepr, &'a TableRecordRepr);
64
65const impl std::ops::Deref for TableRecordHandle<'_> {
66    type Target = TableRecordRepr;
67    fn deref(&self) -> &Self::Target {
68        self.1
69    }
70}
71
72impl<'a> TableRecordHandle<'a> {
73    pub const fn table_as_bytes(&self) -> &'a [u8] {
74        unsafe {
75            let start = std::ptr::from_ref(self.0).cast::<u8>().add(self.offset.get() as _);
76            std::slice::from_raw_parts(start, self.length.get() as _)
77        }
78    }
79    pub const fn table_as<T: Table>(&self) -> Option<&'a T> {
80        if self.table_tag == T::TAG { Some(unsafe { self.table_as_unchecked() }) } else { None }
81    }
82    pub const unsafe fn table_as_unchecked<T: Table>(&self) -> &'a T {
83        debug_assert!(self.table_tag == T::TAG);
84        unsafe { &*self.table_as_bytes().as_ptr().cast() }
85    }
86
87    pub fn calculate_checksum(&self) -> u32 {
88        let (uint32s, unpadded) = self.table_as_bytes().as_chunks::<4>();
89        let mut sum: u32 = 0;
90
91        for chunk in uint32s {
92            sum = sum.wrapping_add(u32::from_be_bytes(*chunk));
93        }
94
95        if !unpadded.is_empty() {
96            let mut buf = [0; 4];
97            buf[..unpadded.len()].copy_from_slice(unpadded);
98            sum = sum.wrapping_add(u32::from_be_bytes(buf));
99        }
100
101        if self.table_tag == tags::head {
102            let checksum_adjustment = u32::from_be_bytes(uint32s[2]);
103            sum = sum.wrapping_sub(checksum_adjustment);
104        }
105
106        sum
107    }
108}
109
110// TODO: When std::slice::Iter's Clone is constified, make the derive const
111#[derive(Clone)]
112pub struct TableRecordsIter<'a> {
113    dir: &'a TableDirectoryRepr,
114    inner: std::slice::Iter<'a, TableRecordRepr>,
115}
116impl<'a> TableRecordsIter<'a> {
117    pub const fn new(dir: &'a TableDirectoryRepr) -> Self {
118        Self { dir, inner: dir.table_records_raw().iter() }
119    }
120    // TODO: When std::slice::Iter's as_slice() is constified, constify as_records()
121    pub fn as_records(&self) -> &'a [TableRecordRepr] {
122        self.inner.as_slice()
123    }
124}
125iterator_map!(TableRecordsIter<'a> {
126    type Item = TableRecordHandle<'a>;
127    |this, x| TableRecordHandle(this.dir, x)
128});
129
130impl std::fmt::Debug for TableDirectoryRepr {
131    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
132        f.debug_struct("TableDirectory")
133            .field_with("sfnt_version", |f| write!(f, "{:#010X}", self.sfnt_version))
134            .field("num_tables", &self.num_tables.get())
135            .field("search_range", &self.search_range.get())
136            .field("entry_selector", &self.entry_selector.get())
137            .field("range_shift", &self.range_shift.get())
138            .field_with("table_records", |f| {
139                let mut list = f.debug_list();
140
141                for table in self.table_records() {
142                    list.entry_with(|f| {
143                        table.fmt(&mut f.with_options(*f.options().alternate(false)))
144                    });
145                }
146                list.finish()
147            })
148            .finish()
149    }
150}
151impl std::fmt::Debug for TableRecordRepr {
152    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
153        f.debug_struct("TableRecord")
154            .field("table_tag", &self.table_tag)
155            .field_with("checksum", |f| write!(f, "{:#010X}", self.checksum))
156            .field_with("offset", |f| write!(f, "{:#010X}", self.offset))
157            .field_with("length", |f| write!(f, "{:#010X}", self.length))
158            .finish()
159    }
160}
161impl std::fmt::Debug for TableRecordHandle<'_> {
162    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
163        TableRecordRepr::fmt(self, f)
164    }
165}