ttf_view/tables/
directory.rs1use 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 }
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#[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 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}