ttf_parser/tables/
name.rs
1#[cfg(feature = "std")]
4use std::vec::Vec;
5#[cfg(feature = "std")]
6use std::string::String;
7
8#[cfg(feature = "std")]
9use crate::parser::LazyArray16;
10
11use crate::parser::{Stream, FromData};
12
13
14pub mod name_id {
16 #![allow(missing_docs)]
17
18 pub const COPYRIGHT_NOTICE: u16 = 0;
19 pub const FAMILY: u16 = 1;
20 pub const SUBFAMILY: u16 = 2;
21 pub const UNIQUE_ID: u16 = 3;
22 pub const FULL_NAME: u16 = 4;
23 pub const VERSION: u16 = 5;
24 pub const POST_SCRIPT_NAME: u16 = 6;
25 pub const TRADEMARK: u16 = 7;
26 pub const MANUFACTURER: u16 = 8;
27 pub const DESIGNER: u16 = 9;
28 pub const DESCRIPTION: u16 = 10;
29 pub const VENDOR_URL: u16 = 11;
30 pub const DESIGNER_URL: u16 = 12;
31 pub const LICENSE: u16 = 13;
32 pub const LICENSE_URL: u16 = 14;
33 pub const TYPOGRAPHIC_FAMILY: u16 = 16;
35 pub const TYPOGRAPHIC_SUBFAMILY: u16 = 17;
36 pub const COMPATIBLE_FULL: u16 = 18;
37 pub const SAMPLE_TEXT: u16 = 19;
38 pub const POST_SCRIPT_CID: u16 = 20;
39 pub const WWS_FAMILY: u16 = 21;
40 pub const WWS_SUBFAMILY: u16 = 22;
41 pub const LIGHT_BACKGROUND_PALETTE: u16 = 23;
42 pub const DARK_BACKGROUND_PALETTE: u16 = 24;
43 pub const VARIATIONS_POST_SCRIPT_NAME_PREFIX: u16 = 25;
44}
45
46
47#[derive(Clone, Copy, PartialEq, Debug)]
49#[allow(missing_docs)]
50pub enum PlatformId {
51 Unicode,
52 Macintosh,
53 Iso,
54 Windows,
55 Custom,
56}
57
58impl FromData for PlatformId {
59 const SIZE: usize = 2;
60
61 #[inline]
62 fn parse(data: &[u8]) -> Option<Self> {
63 match u16::parse(data)? {
64 0 => Some(PlatformId::Unicode),
65 1 => Some(PlatformId::Macintosh),
66 2 => Some(PlatformId::Iso),
67 3 => Some(PlatformId::Windows),
68 4 => Some(PlatformId::Custom),
69 _ => None,
70 }
71 }
72}
73
74
75#[inline]
76fn is_unicode_encoding(platform_id: PlatformId, encoding_id: u16) -> bool {
77 const WINDOWS_SYMBOL_ENCODING_ID: u16 = 0;
79 const WINDOWS_UNICODE_BMP_ENCODING_ID: u16 = 1;
80
81 match platform_id {
82 PlatformId::Unicode => true,
83 PlatformId::Windows => match encoding_id {
84 WINDOWS_SYMBOL_ENCODING_ID |
85 WINDOWS_UNICODE_BMP_ENCODING_ID => true,
86 _ => false,
87 }
88 _ => false,
89 }
90}
91
92
93#[derive(Clone, Copy)]
94struct NameRecord {
95 platform_id: PlatformId,
96 encoding_id: u16,
97 language_id: u16,
98 name_id: u16,
99 length: u16,
100 offset: u16,
101}
102
103impl FromData for NameRecord {
104 const SIZE: usize = 12;
105
106 #[inline]
107 fn parse(data: &[u8]) -> Option<Self> {
108 let mut s = Stream::new(data);
109 Some(NameRecord {
110 platform_id: s.read::<PlatformId>()?,
111 encoding_id: s.read::<u16>()?,
112 language_id: s.read::<u16>()?,
113 name_id: s.read::<u16>()?,
114 length: s.read::<u16>()?,
115 offset: s.read::<u16>()?,
116 })
117 }
118}
119
120
121#[derive(Clone, Copy)]
123pub struct Name<'a> {
124 data: NameRecord,
125 strings: &'a [u8],
126}
127
128impl<'a> Name<'a> {
129 pub fn platform_id(&self) -> PlatformId {
131 self.data.platform_id
132 }
133
134 pub fn encoding_id(&self) -> u16 {
136 self.data.encoding_id
137 }
138
139 pub fn language_id(&self) -> u16 {
141 self.data.language_id
142 }
143
144 pub fn name_id(&self) -> u16 {
148 self.data.name_id
149 }
150
151 pub fn name(&self) -> &'a [u8] {
155 let start = usize::from(self.data.offset);
156 let end = start + usize::from(self.data.length);
157 self.strings.get(start..end).unwrap_or(&[])
158 }
159
160 #[cfg(feature = "std")]
170 #[inline(never)]
171 pub fn to_string(&self) -> Option<String> {
172 if self.is_unicode() {
173 self.name_from_utf16_be()
174 } else {
175 None
176 }
177 }
178
179 #[inline]
181 pub fn is_unicode(&self) -> bool {
182 is_unicode_encoding(self.platform_id(), self.encoding_id())
183 }
184
185 #[cfg(feature = "std")]
186 #[inline(never)]
187 fn name_from_utf16_be(&self) -> Option<String> {
188 let mut name: Vec<u16> = Vec::new();
189 for c in LazyArray16::<u16>::new(self.name()) {
190 name.push(c);
191 }
192
193 String::from_utf16(&name).ok()
194 }
195}
196
197#[cfg(feature = "std")]
198impl<'a> core::fmt::Debug for Name<'a> {
199 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
200 let name = self.to_string();
203 f.debug_struct("Name")
204 .field("name", &name.as_ref().map(core::ops::Deref::deref)
205 .unwrap_or("unsupported encoding"))
206 .field("platform_id", &self.platform_id())
207 .field("encoding_id", &self.encoding_id())
208 .field("language_id", &self.language_id())
209 .field("name_id", &self.name_id())
210 .finish()
211 }
212}
213
214#[cfg(not(feature = "std"))]
215impl<'a> core::fmt::Debug for Name<'a> {
216 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
217 f.debug_struct("Name")
218 .field("name", &self.name())
219 .field("platform_id", &self.platform_id())
220 .field("encoding_id", &self.encoding_id())
221 .field("language_id", &self.language_id())
222 .field("name_id", &self.name_id())
223 .finish()
224 }
225}
226
227
228#[derive(Clone, Copy, Default)]
230#[allow(missing_debug_implementations)]
231pub struct Names<'a> {
232 names: &'a [u8],
233 storage: &'a [u8],
234 index: u16,
235 total: u16,
236}
237
238impl<'a> Names<'a> {
239 fn new(names: &'a [u8], storage: &'a [u8], total: u16) -> Self {
240 Names {
241 names,
242 storage,
243 index: 0,
244 total,
245 }
246 }
247}
248
249impl<'a> Iterator for Names<'a> {
250 type Item = Name<'a>;
251
252 fn next(&mut self) -> Option<Self::Item> {
253 if self.index < self.total {
254 let index = usize::from(self.index);
255 self.index += 1;
256 Some(Name {
257 data: Stream::read_at::<NameRecord>(self.names, NameRecord::SIZE * index)?,
258 strings: self.storage,
259 })
260 } else {
261 None
262 }
263 }
264
265 fn count(self) -> usize {
266 usize::from(self.total)
267 }
268}
269
270
271#[inline(never)]
272pub(crate) fn parse(data: &[u8]) -> Option<Names> {
273 const LANG_TAG_RECORD_SIZE: u16 = 4;
275
276 let mut s = Stream::new(data);
277 let format: u16 = s.read()?;
278 let count: u16 = s.read()?;
279 s.skip::<u16>(); if format == 0 {
282 let names_data = s.read_bytes(NameRecord::SIZE * usize::from(count))?;
283 Some(Names::new(names_data, s.tail()?, count))
284 } else if format == 1 {
285 let lang_tag_count: u16 = s.read()?;
286 let lang_tag_len = lang_tag_count.checked_mul(LANG_TAG_RECORD_SIZE)?;
287
288 s.advance(usize::from(lang_tag_len)); let names_data = s.read_bytes(NameRecord::SIZE * usize::from(count))?;
290 Some(Names::new(names_data, s.tail()?, count))
291 } else {
292 None
293 }
294}