fontdb/ttf_parser/
name.rs1use super::parser::{FromData, LazyArray16, Offset, Offset16, Stream};
5use super::Language;
6
7pub 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#[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 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#[derive(Clone, Copy)]
88pub struct Name<'a> {
89 pub platform_id: PlatformId,
91 pub encoding_id: u16,
93 pub language_id: u16,
95 pub name_id: u16,
99 pub name: &'a [u8],
103}
104
105impl<'a> Name<'a> {
106 #[inline]
108 pub fn is_unicode(&self) -> bool {
109 is_unicode_encoding(self.platform_id, self.encoding_id)
110 }
111
112 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#[derive(Clone, Copy, Default)]
129pub struct Names<'a> {
130 records: LazyArray16<'a, NameRecord>,
131 storage: &'a [u8],
132}
133
134impl<'a> Names<'a> {
135 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 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#[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#[derive(Clone, Copy, Default, Debug)]
203pub struct Table<'a> {
204 pub names: Names<'a>,
206}
207
208impl<'a> Table<'a> {
209 pub fn parse(data: &'a [u8]) -> Option<Self> {
211 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 } 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)); } else {
226 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}