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