1 // https://docs.microsoft.com/en-us/typography/opentype/spec/name
2 
3 #[cfg(feature = "std")]
4 use std::vec::Vec;
5 #[cfg(feature = "std")]
6 use std::string::String;
7 
8 #[cfg(feature = "std")]
9 use crate::parser::LazyArray16;
10 
11 use crate::parser::{Stream, FromData};
12 
13 
14 /// A list of [name ID](https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids)'s.
15 pub 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     //        RESERVED                                  = 15
34     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 /// A [platform ID](https://docs.microsoft.com/en-us/typography/opentype/spec/name#platform-ids).
48 #[derive(Clone, Copy, PartialEq, Debug)]
49 #[allow(missing_docs)]
50 pub enum PlatformId {
51     Unicode,
52     Macintosh,
53     Iso,
54     Windows,
55     Custom,
56 }
57 
58 impl PlatformId {
from_u16(n: u16) -> Option<Self>59     pub(crate) fn from_u16(n: u16) -> Option<Self> {
60         match n {
61             0 => Some(PlatformId::Unicode),
62             1 => Some(PlatformId::Macintosh),
63             2 => Some(PlatformId::Iso),
64             3 => Some(PlatformId::Windows),
65             4 => Some(PlatformId::Custom),
66             _ => None,
67         }
68     }
69 }
70 
71 
72 #[inline]
is_unicode_encoding(platform_id: PlatformId, encoding_id: u16) -> bool73 fn is_unicode_encoding(platform_id: PlatformId, encoding_id: u16) -> bool {
74     // https://docs.microsoft.com/en-us/typography/opentype/spec/name#windows-encoding-ids
75     const WINDOWS_SYMBOL_ENCODING_ID: u16 = 0;
76     const WINDOWS_UNICODE_BMP_ENCODING_ID: u16 = 1;
77 
78     match platform_id {
79         PlatformId::Unicode => true,
80         PlatformId::Windows => match encoding_id {
81             WINDOWS_SYMBOL_ENCODING_ID |
82             WINDOWS_UNICODE_BMP_ENCODING_ID => true,
83             _ => false,
84         }
85         _ => false,
86     }
87 }
88 
89 
90 #[derive(Clone, Copy)]
91 struct NameRecord {
92     platform_id: u16,
93     encoding_id: u16,
94     language_id: u16,
95     name_id: u16,
96     length: u16,
97     offset: u16,
98 }
99 
100 impl FromData for NameRecord {
101     const SIZE: usize = 12;
102 
103     #[inline]
parse(data: &[u8]) -> Option<Self>104     fn parse(data: &[u8]) -> Option<Self> {
105         let mut s = Stream::new(data);
106         Some(NameRecord {
107             platform_id: s.read()?,
108             encoding_id: s.read()?,
109             language_id: s.read()?,
110             name_id: s.read()?,
111             length: s.read()?,
112             offset: s.read()?,
113         })
114     }
115 }
116 
117 
118 /// A [Name Record](https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-records).
119 #[derive(Clone, Copy)]
120 pub struct Name<'a> {
121     data: NameRecord,
122     strings: &'a [u8],
123 }
124 
125 impl<'a> Name<'a> {
126     /// Parses the platform ID.
platform_id(&self) -> Option<PlatformId>127     pub fn platform_id(&self) -> Option<PlatformId> {
128         PlatformId::from_u16(self.data.platform_id)
129     }
130 
131     /// Parses the platform-specific encoding ID.
encoding_id(&self) -> u16132     pub fn encoding_id(&self) -> u16 {
133         self.data.encoding_id
134     }
135 
136     /// Parses the language ID.
language_id(&self) -> u16137     pub fn language_id(&self) -> u16 {
138         self.data.language_id
139     }
140 
141     /// Parses the [Name ID](https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids).
142     ///
143     /// A predefined list of ID's can be found in the [`name_id`](name_id/index.html) module.
name_id(&self) -> u16144     pub fn name_id(&self) -> u16 {
145         self.data.name_id
146     }
147 
148     /// Parses the Name's data as bytes.
149     ///
150     /// Can be empty.
name(&self) -> &'a [u8]151     pub fn name(&self) -> &'a [u8] {
152         let start = usize::from(self.data.offset);
153         let end = start + usize::from(self.data.length);
154         self.strings.get(start..end).unwrap_or(&[])
155     }
156 
157     /// Parses the Name's data as a UTF-8 string.
158     ///
159     /// Only Unicode names are supported. And since they are stored as UTF-16BE,
160     /// we can't return `&str` and have to allocate a `String`.
161     ///
162     /// Supports:
163     /// - Unicode Platform ID
164     /// - Windows Platform ID + Symbol
165     /// - Windows Platform ID + Unicode BMP
166     #[cfg(feature = "std")]
167     #[inline(never)]
name_utf8(&self) -> Option<String>168     pub fn name_utf8(&self) -> Option<String> {
169         if self.is_unicode() {
170             self.name_from_utf16_be()
171         } else {
172             None
173         }
174     }
175 
176     /// Checks that the current Name data has a Unicode encoding.
177     #[inline]
is_unicode(&self) -> bool178     pub fn is_unicode(&self) -> bool {
179         is_unicode_encoding(self.platform_id().unwrap(), self.encoding_id())
180     }
181 
182     #[cfg(feature = "std")]
183     #[inline(never)]
name_from_utf16_be(&self) -> Option<String>184     pub(crate) fn name_from_utf16_be(&self) -> Option<String> {
185         let mut name: Vec<u16> = Vec::new();
186         for c in LazyArray16::<u16>::new(self.name()) {
187             name.push(c);
188         }
189 
190         String::from_utf16(&name).ok()
191     }
192 }
193 
194 #[cfg(feature = "std")]
195 impl<'a> core::fmt::Debug for Name<'a> {
fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result196     fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
197         // TODO: https://github.com/rust-lang/rust/issues/50264
198 
199         let name = self.name_utf8();
200         f.debug_struct("Name")
201             .field("name", &name.as_ref().map(core::ops::Deref::deref)
202                                 .unwrap_or("unsupported encoding"))
203             .field("platform_id", &self.platform_id())
204             .field("encoding_id", &self.encoding_id())
205             .field("language_id", &self.language_id())
206             .field("name_id", &self.name_id())
207             .finish()
208     }
209 }
210 
211 #[cfg(not(feature = "std"))]
212 impl<'a> core::fmt::Debug for Name<'a> {
fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result213     fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
214         f.debug_struct("Name")
215             .field("name", &self.name())
216             .field("platform_id", &self.platform_id())
217             .field("encoding_id", &self.encoding_id())
218             .field("language_id", &self.language_id())
219             .field("name_id", &self.name_id())
220             .finish()
221     }
222 }
223 
224 
225 /// An iterator over font's names.
226 #[derive(Clone, Copy)]
227 #[allow(missing_debug_implementations)]
228 pub struct Names<'a> {
229     names: &'a [u8],
230     storage: &'a [u8],
231     index: u16,
232     total: u16,
233 }
234 
235 impl Default for Names<'_> {
default() -> Self236     fn default() -> Self {
237         Names {
238             names: &[],
239             storage: &[],
240             index: 0,
241             total: 0,
242         }
243     }
244 }
245 
246 impl<'a> Names<'a> {
new(names: &'a [u8], storage: &'a [u8], total: u16) -> Self247     fn new(names: &'a [u8], storage: &'a [u8], total: u16) -> Self {
248         Names {
249             names,
250             storage,
251             index: 0,
252             total,
253         }
254     }
255 }
256 
257 impl<'a> Iterator for Names<'a> {
258     type Item = Name<'a>;
259 
next(&mut self) -> Option<Self::Item>260     fn next(&mut self) -> Option<Self::Item> {
261         if self.index < self.total {
262             self.index += 1;
263             self.nth(usize::from(self.index) - 1)
264         } else {
265             None
266         }
267     }
268 
count(self) -> usize269     fn count(self) -> usize {
270         usize::from(self.total)
271     }
272 
nth(&mut self, n: usize) -> Option<Self::Item>273     fn nth(&mut self, n: usize) -> Option<Self::Item> {
274         Some(Name {
275             data: Stream::read_at(self.names, NameRecord::SIZE * n)?,
276             strings: self.storage,
277         })
278     }
279 }
280 
281 
282 #[inline(never)]
parse(data: &[u8]) -> Option<Names>283 pub(crate) fn parse(data: &[u8]) -> Option<Names> {
284     // https://docs.microsoft.com/en-us/typography/opentype/spec/name#naming-table-format-1
285     const LANG_TAG_RECORD_SIZE: u16 = 4;
286 
287     let mut s = Stream::new(data);
288     let format: u16 = s.read()?;
289     let count: u16 = s.read()?;
290     s.skip::<u16>(); // offset
291 
292     if format == 0 {
293         let names_data = s.read_bytes(NameRecord::SIZE * usize::from(count))?;
294         Some(Names::new(names_data, s.tail()?, count))
295     } else if format == 1 {
296         let lang_tag_count: u16 = s.read()?;
297         let lang_tag_len = lang_tag_count.checked_mul(LANG_TAG_RECORD_SIZE)?;
298 
299         s.advance(usize::from(lang_tag_len)); // langTagRecords
300         let names_data = s.read_bytes(NameRecord::SIZE * usize::from(count))?;
301         Some(Names::new(names_data, s.tail()?, count))
302     } else {
303         None
304     }
305 }
306