ttf_parser/tables/
name.rs1#[cfg(feature = "std")] use std::vec::Vec;
5#[cfg(feature = "std")] use std::string::String;
6
7use crate::parser::{LazyArray16, FromData, Offset, Offset16, Stream};
8
9pub mod name_id {
11 #![allow(missing_docs)]
12
13 pub const COPYRIGHT_NOTICE: u16 = 0;
14 pub const FAMILY: u16 = 1;
15 pub const SUBFAMILY: u16 = 2;
16 pub const UNIQUE_ID: u16 = 3;
17 pub const FULL_NAME: u16 = 4;
18 pub const VERSION: u16 = 5;
19 pub const POST_SCRIPT_NAME: u16 = 6;
20 pub const TRADEMARK: u16 = 7;
21 pub const MANUFACTURER: u16 = 8;
22 pub const DESIGNER: u16 = 9;
23 pub const DESCRIPTION: u16 = 10;
24 pub const VENDOR_URL: u16 = 11;
25 pub const DESIGNER_URL: u16 = 12;
26 pub const LICENSE: u16 = 13;
27 pub const LICENSE_URL: u16 = 14;
28 pub const TYPOGRAPHIC_FAMILY: u16 = 16;
30 pub const TYPOGRAPHIC_SUBFAMILY: u16 = 17;
31 pub const COMPATIBLE_FULL: u16 = 18;
32 pub const SAMPLE_TEXT: u16 = 19;
33 pub const POST_SCRIPT_CID: u16 = 20;
34 pub const WWS_FAMILY: u16 = 21;
35 pub const WWS_SUBFAMILY: u16 = 22;
36 pub const LIGHT_BACKGROUND_PALETTE: u16 = 23;
37 pub const DARK_BACKGROUND_PALETTE: u16 = 24;
38 pub const VARIATIONS_POST_SCRIPT_NAME_PREFIX: u16 = 25;
39}
40
41
42#[allow(missing_docs)]
44#[derive(Clone, Copy, PartialEq, Debug)]
45pub enum PlatformId {
46 Unicode,
47 Macintosh,
48 Iso,
49 Windows,
50 Custom,
51}
52
53impl FromData for PlatformId {
54 const SIZE: usize = 2;
55
56 #[inline]
57 fn parse(data: &[u8]) -> Option<Self> {
58 match u16::parse(data)? {
59 0 => Some(PlatformId::Unicode),
60 1 => Some(PlatformId::Macintosh),
61 2 => Some(PlatformId::Iso),
62 3 => Some(PlatformId::Windows),
63 4 => Some(PlatformId::Custom),
64 _ => None,
65 }
66 }
67}
68
69
70#[inline]
71fn is_unicode_encoding(platform_id: PlatformId, encoding_id: u16) -> bool {
72 const WINDOWS_SYMBOL_ENCODING_ID: u16 = 0;
74 const WINDOWS_UNICODE_BMP_ENCODING_ID: u16 = 1;
75
76 match platform_id {
77 PlatformId::Unicode => true,
78 PlatformId::Windows => match encoding_id {
79 WINDOWS_SYMBOL_ENCODING_ID |
80 WINDOWS_UNICODE_BMP_ENCODING_ID => true,
81 _ => false,
82 }
83 _ => false,
84 }
85}
86
87
88#[derive(Clone, Copy)]
89struct NameRecord {
90 platform_id: PlatformId,
91 encoding_id: u16,
92 language_id: u16,
93 name_id: u16,
94 length: u16,
95 offset: Offset16,
96}
97
98impl FromData for NameRecord {
99 const SIZE: usize = 12;
100
101 #[inline]
102 fn parse(data: &[u8]) -> Option<Self> {
103 let mut s = Stream::new(data);
104 Some(NameRecord {
105 platform_id: s.read::<PlatformId>()?,
106 encoding_id: s.read::<u16>()?,
107 language_id: s.read::<u16>()?,
108 name_id: s.read::<u16>()?,
109 length: s.read::<u16>()?,
110 offset: s.read::<Offset16>()?,
111 })
112 }
113}
114
115
116#[derive(Clone, Copy)]
118pub struct Name<'a> {
119 pub platform_id: PlatformId,
121 pub encoding_id: u16,
123 pub language_id: u16,
125 pub name_id: u16,
129 pub name: &'a [u8],
133}
134
135impl<'a> Name<'a> {
136 #[cfg(feature = "std")]
146 #[inline(never)]
147 pub fn to_string(&self) -> Option<String> {
148 if self.is_unicode() {
149 self.name_from_utf16_be()
150 } else {
151 None
152 }
153 }
154
155 #[inline]
157 pub fn is_unicode(&self) -> bool {
158 is_unicode_encoding(self.platform_id, self.encoding_id)
159 }
160
161 #[cfg(feature = "std")]
162 #[inline(never)]
163 fn name_from_utf16_be(&self) -> Option<String> {
164 let mut name: Vec<u16> = Vec::new();
165 for c in LazyArray16::<u16>::new(self.name) {
166 name.push(c);
167 }
168
169 String::from_utf16(&name).ok()
170 }
171}
172
173#[cfg(feature = "std")]
174impl<'a> core::fmt::Debug for Name<'a> {
175 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
176 let name = self.to_string();
179 f.debug_struct("Name")
180 .field("name", &name.as_ref().map(core::ops::Deref::deref)
181 .unwrap_or("unsupported encoding"))
182 .field("platform_id", &self.platform_id)
183 .field("encoding_id", &self.encoding_id)
184 .field("language_id", &self.language_id)
185 .field("name_id", &self.name_id)
186 .finish()
187 }
188}
189
190#[cfg(not(feature = "std"))]
191impl<'a> core::fmt::Debug for Name<'a> {
192 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
193 f.debug_struct("Name")
194 .field("name", &self.name)
195 .field("platform_id", &self.platform_id)
196 .field("encoding_id", &self.encoding_id)
197 .field("language_id", &self.language_id)
198 .field("name_id", &self.name_id)
199 .finish()
200 }
201}
202
203
204#[derive(Clone, Copy, Default)]
206pub struct Names<'a> {
207 records: LazyArray16<'a, NameRecord>,
208 storage: &'a [u8],
209}
210
211impl<'a> Names<'a> {
212 pub fn get(&self, index: u16) -> Option<Name<'a>> {
214 let record = self.records.get(index)?;
215 let name_start = record.offset.to_usize();
216 let name_end = name_start + usize::from(record.length);
217 let name = self.storage.get(name_start..name_end)?;
218 Some(Name {
219 platform_id: record.platform_id,
220 encoding_id: record.encoding_id,
221 language_id: record.language_id,
222 name_id: record.name_id,
223 name,
224 })
225 }
226
227 pub fn len(&self) -> u16 {
229 self.records.len()
230 }
231}
232
233impl core::fmt::Debug for Names<'_> {
234 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
235 write!(f, "Names {{ ... }}")
236 }
237}
238
239impl<'a> IntoIterator for Names<'a> {
240 type Item = Name<'a>;
241 type IntoIter = NamesIter<'a>;
242
243 #[inline]
244 fn into_iter(self) -> Self::IntoIter {
245 NamesIter {
246 names: self,
247 index: 0,
248 }
249 }
250}
251
252#[derive(Clone, Copy)]
254#[allow(missing_debug_implementations)]
255pub struct NamesIter<'a> {
256 names: Names<'a>,
257 index: u16,
258}
259
260impl<'a> Iterator for NamesIter<'a> {
261 type Item = Name<'a>;
262
263 fn next(&mut self) -> Option<Self::Item> {
264 if self.index < self.names.len() {
265 self.index += 1;
266 self.names.get(self.index - 1)
267 } else {
268 None
269 }
270 }
271
272 #[inline]
273 fn count(self) -> usize {
274 usize::from(self.names.len().checked_sub(self.index).unwrap_or(0))
275 }
276}
277
278
279#[derive(Clone, Copy, Default, Debug)]
282pub struct Table<'a> {
283 pub names: Names<'a>,
285}
286
287impl<'a> Table<'a> {
288 pub fn parse(data: &'a [u8]) -> Option<Self> {
290 const LANG_TAG_RECORD_SIZE: u16 = 4;
292
293 let mut s = Stream::new(data);
294 let version = s.read::<u16>()?;
295 let count = s.read::<u16>()?;
296 let storage_offset = s.read::<Offset16>()?.to_usize();
297
298 if version == 0 {
299 } else if version == 1 {
301 let lang_tag_count = s.read::<u16>()?;
302 let lang_tag_len = lang_tag_count.checked_mul(LANG_TAG_RECORD_SIZE)?;
303 s.advance(usize::from(lang_tag_len)); } else {
305 return None;
307 }
308
309 let records = s.read_array16::<NameRecord>(count)?;
310
311 if s.offset() < storage_offset {
312 s.advance(storage_offset - s.offset());
313 }
314
315 let storage = s.tail()?;
316
317 Some(Table { names: Names { records, storage } })
318 }
319}