Skip to main content

ttf_view/tables/
name.rs

1use crate::{
2    platform::{EncodingError, EncodingId, PlatformId},
3    tables::{Table, TableDirectory, TableError},
4    types::{Offset16, Tag, tags, uint16},
5    util::custom_iterator,
6};
7use std::{borrow::Cow, bstr::ByteStr};
8
9#[repr(C)]
10pub struct NameV0 {
11    // version ≥ 0:
12    pub version: uint16,
13    pub count: uint16,
14    pub storage_offset: Offset16,
15    name_records: [NameRecordRaw; 0],
16}
17#[repr(C)]
18pub struct NameV1 {
19    v0: NameV0,
20    // version ≥ 1:
21    // : lang_tag_count: uint16,
22    // : lang_tag_records: [LangTagRecord; lang_tag_count],
23}
24
25const impl std::ops::Deref for NameV1 {
26    type Target = NameV0;
27    fn deref(&self) -> &Self::Target {
28        &self.v0
29    }
30}
31
32#[repr(C)]
33pub struct NameRecordRaw {
34    pub platform_id: uint16,
35    pub encoding_id: uint16,
36    pub language_id: uint16,
37    pub name_id: uint16,
38    pub length: uint16,
39    pub string_offset: Offset16,
40}
41#[repr(C)]
42pub struct LangTagRecordRaw {
43    pub length: uint16,
44    pub lang_tag_offset: Offset16,
45}
46
47impl<'a> Table<'a> for Name<'a> {
48    const TAG: Tag = tags::name;
49    fn new_in(dir: &'a TableDirectory) -> Result<Self, TableError> {
50        let rec = dir.table_record(Self::TAG).ok_or(TableError::NotFound)?;
51        let v0 = rec.raw_as::<NameV0>().ok_or(TableError::InvalidLen)?;
52
53        let mut required_len =
54            size_of::<NameV0>() + v0.count.get() as usize * size_of::<NameRecordRaw>();
55        // Validate that all name records are in range
56        if (rec.length.get() as usize) < required_len {
57            return Err(TableError::InvalidLen);
58        }
59
60        let storage_offset = v0.storage_offset.get() as u32;
61
62        // Validate that all names are in range
63        for name in v0.name_records() {
64            let offset = (name.string_offset.get() as u32) + (name.length.get() as u32);
65            if rec.length.get() < storage_offset + offset {
66                return Err(TableError::InvalidLen);
67            }
68        }
69
70        if v0.version.get() >= 1 {
71            required_len += size_of::<uint16>(); // lang_tag_count
72            // Validate that lang_tag_count is in range
73            if (rec.length.get() as usize) < required_len {
74                return Err(TableError::InvalidLen);
75            }
76
77            let v1 = rec.raw_as::<NameV1>().unwrap();
78            required_len += v1.lang_tag_count().get() as usize * size_of::<LangTagRecordRaw>();
79
80            // Validate that all lang tag records are in range
81            if (rec.length.get() as usize) < required_len {
82                return Err(TableError::InvalidLen);
83            }
84
85            // Validate that all lang tags are in range
86            for tag in v1.lang_tag_records() {
87                let offset = (tag.lang_tag_offset.get() as u32) + (tag.length.get() as u32);
88                if rec.length.get() < storage_offset + offset {
89                    return Err(TableError::InvalidLen);
90                }
91            }
92        }
93
94        Ok(Self { name: v0 })
95    }
96}
97
98#[derive(Copy)]
99#[derive_const(Clone)]
100pub struct Name<'a> {
101    name: &'a NameV0,
102}
103
104const impl<'a> std::ops::Deref for Name<'a> {
105    type Target = &'a NameV0;
106    fn deref(&self) -> &Self::Target {
107        &self.name
108    }
109}
110
111impl<'a> Name<'a> {
112    pub const fn version(&self) -> u16 {
113        self.version.get()
114    }
115
116    pub const fn v1(&self) -> Option<&'a NameV1> {
117        if self.version() >= 1 {
118            Some(unsafe { std::mem::transmute::<&NameV0, &NameV1>(self.name) })
119        } else {
120            None
121        }
122    }
123
124    pub const fn names(&self) -> NameRecordsIter<'a> {
125        NameRecordsIter::new(*self)
126    }
127    pub const fn lang_tags(&self) -> LangTagRecordsIter<'a> {
128        LangTagRecordsIter::new(*self)
129    }
130
131    // version ≥ 1:
132    pub const fn lang_tag_count(&self) -> Option<uint16> {
133        Some(self.v1()?.lang_tag_count())
134    }
135    pub const fn lang_tag_records(&self) -> Option<&'a [LangTagRecordRaw]> {
136        Some(self.v1()?.lang_tag_records())
137    }
138}
139
140impl NameV0 {
141    pub const fn name_records(&self) -> &[NameRecordRaw] {
142        unsafe { std::slice::from_raw_parts(self.name_records.as_ptr(), self.count.get() as _) }
143    }
144    pub const fn string_storage(&self) -> &StringStorage {
145        let offset = self.storage_offset.get() as usize;
146        unsafe { &*std::ptr::from_ref(self).byte_add(offset).cast() }
147    }
148}
149
150impl NameV1 {
151    pub const fn lang_tag_count(&self) -> uint16 {
152        unsafe { *self.name_records().as_ptr_range().end.cast::<uint16>() }
153    }
154    pub const fn lang_tag_records(&self) -> &[LangTagRecordRaw] {
155        let len_ptr = self.name_records().as_ptr_range().end.cast::<uint16>();
156        unsafe { std::slice::from_raw_parts(len_ptr.add(1).cast(), (*len_ptr).get() as _) }
157    }
158}
159
160pub struct StringStorage {
161    _dont_instantiate: (),
162}
163
164impl StringStorage {
165    pub const fn as_ptr(&self) -> *const u8 {
166        std::ptr::from_ref(self).cast()
167    }
168    pub(crate) const unsafe fn get(&self, offset: u16, length: u16) -> &[u8] {
169        unsafe { std::slice::from_raw_parts(self.as_ptr().add(offset as _), length as _) }
170    }
171}
172
173#[derive(Copy)]
174#[derive_const(Clone)]
175pub struct NameRecord<'a>(Name<'a>, &'a NameRecordRaw);
176
177#[derive(Copy)]
178#[derive_const(Clone)]
179pub struct LangTagRecord<'a>(Name<'a>, &'a LangTagRecordRaw);
180
181const impl<'a> std::ops::Deref for NameRecord<'a> {
182    type Target = &'a NameRecordRaw;
183    fn deref(&self) -> &Self::Target {
184        &self.1
185    }
186}
187const impl<'a> std::ops::Deref for LangTagRecord<'a> {
188    type Target = &'a LangTagRecordRaw;
189    fn deref(&self) -> &Self::Target {
190        &self.1
191    }
192}
193
194impl<'a> NameRecord<'a> {
195    pub const fn bytes(&self) -> &'a [u8] {
196        unsafe { self.0.string_storage().get(self.1.string_offset.get(), self.1.length.get()) }
197    }
198    pub fn string(&self) -> Result<String, EncodingError> {
199        let encoding = EncodingId::new(self.1.platform_id.get(), self.1.encoding_id.get())?;
200        encoding.decode_utf16be(self.bytes())
201    }
202}
203
204impl<'a> LangTagRecord<'a> {
205    pub const fn bytes(&self) -> &'a [u8] {
206        unsafe { self.0.string_storage().get(self.1.lang_tag_offset.get(), self.1.length.get()) }
207    }
208    pub fn string(&self) -> String {
209        // Note: LangTags are always encoded in UTF-16BE.
210        String::from_utf16be_lossy(self.bytes())
211    }
212}
213
214// TODO: When std::slice::Iter's Clone is constified, make the derive const
215#[derive(Clone)]
216pub struct NameRecordsIter<'a> {
217    table: Name<'a>,
218    inner: std::slice::Iter<'a, NameRecordRaw>,
219}
220impl<'a> NameRecordsIter<'a> {
221    pub const fn new(table: Name<'a>) -> Self {
222        Self { table, inner: table.name_records().iter() }
223    }
224    // TODO: When std::slice::Iter's as_slice() is constified, constify as_records()
225    pub fn as_records(&self) -> &'a [NameRecordRaw] {
226        self.inner.as_slice()
227    }
228}
229custom_iterator!(NameRecordsIter<'a> as this {
230    type Item = NameRecord<'a>;
231    map: |x| NameRecord(this.table, x);
232});
233
234// TODO: When std::slice::Iter's Clone is constified, make the derive const
235#[derive(Clone)]
236pub struct LangTagRecordsIter<'a> {
237    table: Name<'a>,
238    inner: std::slice::Iter<'a, LangTagRecordRaw>,
239}
240impl<'a> LangTagRecordsIter<'a> {
241    pub const fn new(table: Name<'a>) -> Self {
242        Self { table, inner: table.lang_tag_records().unwrap_or(&[]).iter() }
243    }
244    // TODO: When std::slice::Iter's as_slice() is constified, constify as_records()
245    pub fn as_records(&self) -> &'a [LangTagRecordRaw] {
246        self.inner.as_slice()
247    }
248}
249custom_iterator!(LangTagRecordsIter<'a> as this {
250    type Item = LangTagRecord<'a>;
251    map: |x| LangTagRecord(this.table, x);
252});
253
254impl std::fmt::Debug for Name<'_> {
255    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
256        let mut f = f.debug_struct("NameTable");
257
258        f.field("version", &self.version.get())
259            .field("count", &self.count.get())
260            .field_with("storage_offset", |f| write!(f, "{:#06X}", self.storage_offset))
261            .field_with("name_records", |f| f.debug_list().entries(self.names()).finish());
262
263        if let Some(v1) = self.v1() {
264            f.field("lang_tag_count", &v1.lang_tag_records().len());
265            f.field_with("lang_tag_records", |f| f.debug_list().entries(self.lang_tags()).finish());
266        }
267
268        f.finish()
269    }
270}
271
272// Monomorphize all fmts to the above fmt for Name<'a>
273impl std::fmt::Debug for NameV0 {
274    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
275        Name { name: self }.fmt(f)
276    }
277}
278impl std::fmt::Debug for NameV1 {
279    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
280        Name { name: self }.fmt(f)
281    }
282}
283
284impl std::fmt::Debug for NameRecord<'_> {
285    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
286        let Self(name, rec) = *self;
287
288        let value = self.string().map_err(|err| (err, ByteStr::new(self.bytes())));
289
290        let plat_id = PlatformId::new(rec.platform_id.get());
291        let plat_name = plat_id.map_or("Unknown", |x| x.name());
292
293        let enc_id = plat_id.and_then(|x| x.encoding(rec.encoding_id.get()));
294        let enc_name = enc_id.map_or(Cow::Borrowed("Unknown"), |x| x.name());
295
296        let lang_id = plat_id.and_then(|x| x.language(rec.language_id.get()));
297        let lang_name = format!(
298            "{}: {}",
299            lang_id.and_then(|x| x.tag(Some(name))).unwrap_or(Cow::Borrowed("und")),
300            lang_id.and_then(|x| x.english_name(Some(name))).unwrap_or(Cow::Borrowed("Unknown")),
301        );
302
303        f.debug_struct("NameRecord")
304            .field_with("platform_id", |f| write!(f, "{} ({})", rec.platform_id, plat_name))
305            .field_with("encoding_id", |f| write!(f, "{} ({})", rec.encoding_id, enc_name))
306            .field_with("language_id", |f| write!(f, "{:#06X} ({})", rec.language_id, lang_name))
307            // TODO: Parse name_id and display its name
308            .field("name_id", &rec.name_id.get())
309            .field("length", &rec.length.get())
310            .field_with("string_offset", |f| write!(f, "{:#06X}", rec.string_offset))
311            .field_with("value", |f| {
312                let mut f = f.with_options(*f.options().alternate(false));
313                value.fmt(&mut f)
314            })
315            .finish()
316    }
317}
318
319impl std::fmt::Debug for LangTagRecord<'_> {
320    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
321        let Self(_, rec) = *self;
322
323        f.debug_struct("LangTagRecord")
324            .field("length", &rec.length.get())
325            .field_with("lang_tag_offset", |f| write!(f, "{:#06X}", rec.lang_tag_offset))
326            .field_with("value", |f| {
327                let mut f = f.with_options(*f.options().alternate(false));
328                self.string().fmt(&mut f)
329            })
330            .finish()
331    }
332}