Skip to main content

ttf_view/tables/
name.rs

1use crate::{
2    platform::{EncodingError, EncodingId, PlatformId},
3    types::{Offset16, Tag, tags, uint16},
4    util::iterator_map,
5};
6use std::{borrow::Cow, bstr::ByteStr};
7
8#[repr(C)]
9#[non_exhaustive]
10pub struct NameTableRepr {
11    // version ≥ 0:
12    pub version: uint16,
13    pub count: uint16,
14    pub storage_offset: Offset16,
15    name_records: [NameRecordRepr; 0],
16    // version ≥ 1:
17    // : lang_tag_count: uint16,
18    // : lang_tag_records: [LangTagRecordRepr; lang_tag_count],
19}
20#[repr(C)]
21pub struct NameRecordRepr {
22    pub platform_id: uint16,
23    pub encoding_id: uint16,
24    pub language_id: uint16,
25    pub name_id: uint16,
26    pub length: uint16,
27    pub string_offset: Offset16,
28}
29#[repr(C)]
30pub struct LangTagRecordRepr {
31    pub length: uint16,
32    pub lang_tag_offset: Offset16,
33}
34
35impl super::Table for NameTableRepr {
36    const TAG: Tag = tags::name;
37    type Handle<'a> = &'a Self;
38}
39
40impl NameTableRepr {
41    pub const fn name_records(&self) -> &[NameRecordRepr] {
42        unsafe { std::slice::from_raw_parts(self.name_records.as_ptr(), self.count.get() as _) }
43    }
44    pub const fn names(&self) -> NamesIter<'_> {
45        NamesIter::new(self)
46    }
47
48    // Note: I decided not to include lang_tag_count() getter here, because we've already got
49    // not only a better higher-level API - lang_tags(), but also just lang_tag_records().
50
51    pub const fn lang_tag_records(&self) -> &[LangTagRecordRepr] {
52        if self.version.get() == 0 {
53            return &[];
54        }
55        let len_ptr = self.name_records().as_ptr_range().end.cast::<uint16>();
56        unsafe { std::slice::from_raw_parts(len_ptr.add(1).cast(), (*len_ptr).get() as _) }
57    }
58    pub const fn lang_tags(&self) -> LangTagsIter<'_> {
59        LangTagsIter::new(self)
60    }
61
62    pub const fn string_storage(&self) -> &StringStorage {
63        let offset = self.storage_offset.get() as usize;
64        unsafe { &*std::ptr::from_ref(self).byte_add(offset).cast() }
65    }
66}
67
68#[non_exhaustive]
69pub struct StringStorage {}
70
71impl StringStorage {
72    pub const fn as_ptr(&self) -> *const u8 {
73        std::ptr::from_ref(self).cast()
74    }
75    pub(crate) const unsafe fn get(&self, offset: u16, length: u16) -> &[u8] {
76        unsafe { std::slice::from_raw_parts(self.as_ptr().add(offset as _), length as _) }
77    }
78}
79
80#[derive(Copy)]
81#[derive_const(Clone)]
82pub struct NameHandle<'a>(&'a NameTableRepr, &'a NameRecordRepr);
83
84#[derive(Copy)]
85#[derive_const(Clone)]
86pub struct LangTagHandle<'a>(&'a NameTableRepr, &'a LangTagRecordRepr);
87
88const impl std::ops::Deref for NameHandle<'_> {
89    type Target = NameRecordRepr;
90    fn deref(&self) -> &Self::Target {
91        self.1
92    }
93}
94const impl std::ops::Deref for LangTagHandle<'_> {
95    type Target = LangTagRecordRepr;
96    fn deref(&self) -> &Self::Target {
97        self.1
98    }
99}
100
101impl<'a> NameHandle<'a> {
102    pub const fn bytes(&self) -> &'a [u8] {
103        unsafe { self.0.string_storage().get(self.1.string_offset.get(), self.1.length.get()) }
104    }
105    pub fn string(&self) -> Result<String, EncodingError> {
106        let encoding = EncodingId::new(self.1.platform_id.get(), self.1.encoding_id.get())?;
107        encoding.decode_utf16be(self.bytes())
108    }
109}
110
111impl<'a> LangTagHandle<'a> {
112    pub const fn bytes(&self) -> &'a [u8] {
113        unsafe { self.0.string_storage().get(self.1.lang_tag_offset.get(), self.1.length.get()) }
114    }
115    pub fn string(&self) -> String {
116        // Note: LangTags are always encoded in UTF-16BE.
117        String::from_utf16be_lossy(self.bytes())
118    }
119}
120
121// TODO: When std::slice::Iter's Clone is constified, make the derive const
122#[derive(Clone)]
123pub struct NamesIter<'a> {
124    table: &'a NameTableRepr,
125    inner: std::slice::Iter<'a, NameRecordRepr>,
126}
127impl<'a> NamesIter<'a> {
128    pub const fn new(table: &'a NameTableRepr) -> Self {
129        Self { table, inner: table.name_records().iter() }
130    }
131    // TODO: When std::slice::Iter's as_slice() is constified, constify as_records()
132    pub fn as_records(&self) -> &'a [NameRecordRepr] {
133        self.inner.as_slice()
134    }
135}
136iterator_map!(NamesIter<'a> {
137    type Item = NameHandle<'a>;
138    |this, x| NameHandle(this.table, x)
139});
140
141// TODO: When std::slice::Iter's Clone is constified, make the derive const
142#[derive(Clone)]
143pub struct LangTagsIter<'a> {
144    table: &'a NameTableRepr,
145    inner: std::slice::Iter<'a, LangTagRecordRepr>,
146}
147impl<'a> LangTagsIter<'a> {
148    pub const fn new(table: &'a NameTableRepr) -> Self {
149        Self { table, inner: table.lang_tag_records().iter() }
150    }
151    // TODO: When std::slice::Iter's as_slice() is constified, constify as_records()
152    pub fn as_records(&self) -> &'a [LangTagRecordRepr] {
153        self.inner.as_slice()
154    }
155}
156iterator_map!(LangTagsIter<'a> {
157    type Item = LangTagHandle<'a>;
158    |this, x| LangTagHandle(this.table, x)
159});
160
161impl std::fmt::Debug for NameTableRepr {
162    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
163        let mut builder = f.debug_struct("NameTable");
164
165        builder
166            .field("version", &self.version.get())
167            .field("count", &self.count.get())
168            .field_with("storage_offset", |f| write!(f, "{:#06X}", self.storage_offset))
169            .field_with("name_records", |f| f.debug_list().entries(self.names()).finish());
170
171        if self.version.get() != 0 {
172            builder
173                .field("lang_tag_count", &self.lang_tag_records().len())
174                .field_with("lang_tag_records", |f| {
175                    f.debug_list().entries(self.lang_tags()).finish()
176                });
177        }
178
179        builder.finish()
180    }
181}
182
183impl std::fmt::Debug for NameHandle<'_> {
184    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
185        let Self(name, rec) = *self;
186
187        let value = self.string().map_err(|err| (err, ByteStr::new(self.bytes())));
188
189        let plat_id = PlatformId::new(rec.platform_id.get());
190        let plat_name = plat_id.map_or("Unknown", |x| x.name());
191
192        let enc_id = plat_id.and_then(|x| x.encoding(rec.encoding_id.get()));
193        let enc_name = enc_id.map_or(Cow::Borrowed("Unknown"), |x| x.name());
194
195        let lang_id = plat_id.and_then(|x| x.language(rec.language_id.get()));
196        let lang_name = format!(
197            "{}: {}",
198            lang_id.and_then(|x| x.tag(Some(name))).unwrap_or(Cow::Borrowed("und")),
199            lang_id.and_then(|x| x.english_name(Some(name))).unwrap_or(Cow::Borrowed("Unknown")),
200        );
201
202        f.debug_struct("NameRecord")
203            .field_with("platform_id", |f| write!(f, "{} ({})", rec.platform_id, plat_name))
204            .field_with("encoding_id", |f| write!(f, "{} ({})", rec.encoding_id, enc_name))
205            .field_with("language_id", |f| write!(f, "{:#06X} ({})", rec.language_id, lang_name))
206            // TODO: Parse name_id and display its name
207            .field("name_id", &rec.name_id.get())
208            .field("length", &rec.length.get())
209            .field_with("string_offset", |f| write!(f, "{:#06X}", rec.string_offset))
210            .field_with("value", |f| {
211                let mut f = f.with_options(*f.options().alternate(false));
212                value.fmt(&mut f)
213            })
214            .finish()
215    }
216}
217
218impl std::fmt::Debug for LangTagHandle<'_> {
219    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
220        let Self(_, rec) = *self;
221
222        f.debug_struct("LangTagRecord")
223            .field("length", &rec.length.get())
224            .field_with("lang_tag_offset", |f| write!(f, "{:#06X}", rec.lang_tag_offset))
225            .field_with("value", |f| {
226                let mut f = f.with_options(*f.options().alternate(false));
227                self.string().fmt(&mut f)
228            })
229            .finish()
230    }
231}