Skip to main content

fontdb/ttf_parser/
name.rs

1//! A [Naming Table](
2//! https://docs.microsoft.com/en-us/typography/opentype/spec/name) implementation.
3
4use super::parser::{FromData, LazyArray16, Offset, Offset16, Stream};
5use super::Language;
6
7/// A list of [name ID](https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids)'s.
8pub mod name_id {
9    #![allow(missing_docs)]
10
11    pub const FAMILY: u16 = 1;
12    pub const POST_SCRIPT_NAME: u16 = 6;
13    pub const TYPOGRAPHIC_FAMILY: u16 = 16;
14}
15
16/// A [platform ID](https://docs.microsoft.com/en-us/typography/opentype/spec/name#platform-ids).
17#[allow(missing_docs)]
18#[derive(Clone, Copy, PartialEq, Eq, Debug)]
19pub enum PlatformId {
20    Unicode,
21    Macintosh,
22    Iso,
23    Windows,
24    Custom,
25}
26
27impl FromData for PlatformId {
28    const SIZE: usize = 2;
29
30    #[inline]
31    fn parse(data: &[u8]) -> Option<Self> {
32        match u16::parse(data)? {
33            0 => Some(PlatformId::Unicode),
34            1 => Some(PlatformId::Macintosh),
35            2 => Some(PlatformId::Iso),
36            3 => Some(PlatformId::Windows),
37            4 => Some(PlatformId::Custom),
38            _ => None,
39        }
40    }
41}
42
43#[inline]
44fn is_unicode_encoding(platform_id: PlatformId, encoding_id: u16) -> bool {
45    // https://docs.microsoft.com/en-us/typography/opentype/spec/name#windows-encoding-ids
46    const WINDOWS_SYMBOL_ENCODING_ID: u16 = 0;
47    const WINDOWS_UNICODE_BMP_ENCODING_ID: u16 = 1;
48
49    match platform_id {
50        PlatformId::Unicode => true,
51        PlatformId::Windows => matches!(
52            encoding_id,
53            WINDOWS_SYMBOL_ENCODING_ID | WINDOWS_UNICODE_BMP_ENCODING_ID
54        ),
55        _ => false,
56    }
57}
58
59#[derive(Clone, Copy)]
60struct NameRecord {
61    platform_id: PlatformId,
62    encoding_id: u16,
63    language_id: u16,
64    name_id: u16,
65    length: u16,
66    offset: Offset16,
67}
68
69impl FromData for NameRecord {
70    const SIZE: usize = 12;
71
72    #[inline]
73    fn parse(data: &[u8]) -> Option<Self> {
74        let mut s = Stream::new(data);
75        Some(NameRecord {
76            platform_id: s.read::<PlatformId>()?,
77            encoding_id: s.read::<u16>()?,
78            language_id: s.read::<u16>()?,
79            name_id: s.read::<u16>()?,
80            length: s.read::<u16>()?,
81            offset: s.read::<Offset16>()?,
82        })
83    }
84}
85
86/// A [Name Record](https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-records).
87#[derive(Clone, Copy)]
88pub struct Name<'a> {
89    /// A platform ID.
90    pub platform_id: PlatformId,
91    /// A platform-specific encoding ID.
92    pub encoding_id: u16,
93    /// A language ID.
94    pub language_id: u16,
95    /// A [Name ID](https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids).
96    ///
97    /// A predefined list of ID's can be found in the [`name_id`](name_id/index.html) module.
98    pub name_id: u16,
99    /// A raw name data.
100    ///
101    /// Can be in any encoding. Can be empty.
102    pub name: &'a [u8],
103}
104
105impl<'a> Name<'a> {
106    /// Checks that the current Name data has a Unicode encoding.
107    #[inline]
108    pub fn is_unicode(&self) -> bool {
109        is_unicode_encoding(self.platform_id, self.encoding_id)
110    }
111
112    /// Returns a Name language.
113    pub fn language(&self) -> Language {
114        if self.platform_id == PlatformId::Windows {
115            Language::windows_language(self.language_id)
116        } else if self.platform_id == PlatformId::Macintosh
117            && self.encoding_id == 0
118            && self.language_id == 0
119        {
120            Language::English_UnitedStates
121        } else {
122            Language::Unknown
123        }
124    }
125}
126
127/// A list of face names.
128#[derive(Clone, Copy, Default)]
129pub struct Names<'a> {
130    records: LazyArray16<'a, NameRecord>,
131    storage: &'a [u8],
132}
133
134impl<'a> Names<'a> {
135    /// Returns a name at index.
136    pub fn get(&self, index: u16) -> Option<Name<'a>> {
137        let record = self.records.get(index)?;
138        let name_start = record.offset.to_usize();
139        let name_end = name_start + usize::from(record.length);
140        let name = self.storage.get(name_start..name_end)?;
141        Some(Name {
142            platform_id: record.platform_id,
143            encoding_id: record.encoding_id,
144            language_id: record.language_id,
145            name_id: record.name_id,
146            name,
147        })
148    }
149
150    /// Returns a number of name records.
151    pub fn len(&self) -> u16 {
152        self.records.len()
153    }
154}
155
156impl core::fmt::Debug for Names<'_> {
157    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
158        write!(f, "Names {{ ... }}")
159    }
160}
161
162impl<'a> IntoIterator for Names<'a> {
163    type Item = Name<'a>;
164    type IntoIter = NamesIter<'a>;
165
166    #[inline]
167    fn into_iter(self) -> Self::IntoIter {
168        NamesIter {
169            names: self,
170            index: 0,
171        }
172    }
173}
174
175/// An iterator over face names.
176#[derive(Clone, Copy)]
177pub struct NamesIter<'a> {
178    names: Names<'a>,
179    index: u16,
180}
181
182impl<'a> Iterator for NamesIter<'a> {
183    type Item = Name<'a>;
184
185    fn next(&mut self) -> Option<Self::Item> {
186        if self.index < self.names.len() {
187            self.index += 1;
188            self.names.get(self.index - 1)
189        } else {
190            None
191        }
192    }
193
194    #[inline]
195    fn count(self) -> usize {
196        usize::from(self.names.len().saturating_sub(self.index))
197    }
198}
199
200/// A [Naming Table](
201/// https://docs.microsoft.com/en-us/typography/opentype/spec/name).
202#[derive(Clone, Copy, Default, Debug)]
203pub struct Table<'a> {
204    /// A list of names.
205    pub names: Names<'a>,
206}
207
208impl<'a> Table<'a> {
209    /// Parses a table from raw data.
210    pub fn parse(data: &'a [u8]) -> Option<Self> {
211        // https://docs.microsoft.com/en-us/typography/opentype/spec/name#naming-table-format-1
212        const LANG_TAG_RECORD_SIZE: u16 = 4;
213
214        let mut s = Stream::new(data);
215        let version = s.read::<u16>()?;
216        let count = s.read::<u16>()?;
217        let storage_offset = s.read::<Offset16>()?.to_usize();
218
219        if version == 0 {
220            // Do nothing.
221        } else if version == 1 {
222            let lang_tag_count = s.read::<u16>()?;
223            let lang_tag_len = lang_tag_count.checked_mul(LANG_TAG_RECORD_SIZE)?;
224            s.advance(usize::from(lang_tag_len)); // langTagRecords
225        } else {
226            // Unsupported version.
227            return None;
228        }
229
230        let records = s.read_array16::<NameRecord>(count)?;
231
232        if s.offset() < storage_offset {
233            s.advance(storage_offset - s.offset());
234        }
235
236        let storage = s.tail()?;
237
238        Some(Table {
239            names: Names { records, storage },
240        })
241    }
242}