ttf_parser/tables/
name.rs

1// https://docs.microsoft.com/en-us/typography/opentype/spec/name
2
3#[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
14/// A list of [name ID](https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids)'s.
15pub 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)]
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    // https://docs.microsoft.com/en-us/typography/opentype/spec/name#windows-encoding-ids
78    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/// A [Name Record](https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-records).
122#[derive(Clone, Copy)]
123pub struct Name<'a> {
124    data: NameRecord,
125    strings: &'a [u8],
126}
127
128impl<'a> Name<'a> {
129    /// Returns the platform ID.
130    pub fn platform_id(&self) -> PlatformId {
131        self.data.platform_id
132    }
133
134    /// Returns the platform-specific encoding ID.
135    pub fn encoding_id(&self) -> u16 {
136        self.data.encoding_id
137    }
138
139    /// Returns the language ID.
140    pub fn language_id(&self) -> u16 {
141        self.data.language_id
142    }
143
144    /// Returns the [Name ID](https://docs.microsoft.com/en-us/typography/opentype/spec/name#name-ids).
145    ///
146    /// A predefined list of ID's can be found in the [`name_id`](name_id/index.html) module.
147    pub fn name_id(&self) -> u16 {
148        self.data.name_id
149    }
150
151    /// Returns the Name's data as bytes.
152    ///
153    /// Can be empty.
154    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    /// Returns the Name's data as a UTF-8 string.
161    ///
162    /// Only Unicode names are supported. And since they are stored as UTF-16BE,
163    /// we can't return `&str` and have to allocate a `String`.
164    ///
165    /// Supports:
166    /// - Unicode Platform ID
167    /// - Windows Platform ID + Symbol
168    /// - Windows Platform ID + Unicode BMP
169    #[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    /// Checks that the current Name data has a Unicode encoding.
180    #[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        // TODO: https://github.com/rust-lang/rust/issues/50264
201
202        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/// An iterator over font's names.
229#[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    // https://docs.microsoft.com/en-us/typography/opentype/spec/name#naming-table-format-1
274    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>(); // offset
280
281    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)); // langTagRecords
289        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}