Skip to main content

font_map_core/raw/ttf/
name.rs

1use crate::error::ParseResult;
2use crate::reader::{BinaryReader, Parse};
3
4use super::PlatformType;
5
6/// A name record in a TrueType font
7#[derive(Debug)]
8pub struct NameRecord {
9    pub platform_id: PlatformType,
10    pub encoding_id: u16,
11    pub language_id: u16,
12
13    pub name_id: NameKind,
14    pub name: String,
15}
16
17/// The name table of a TrueType font
18#[derive(Debug, Default)]
19pub struct NameTable {
20    /// The name records in the table
21    pub records: Vec<NameRecord>,
22}
23
24impl Parse for NameTable {
25    fn parse(reader: &mut BinaryReader) -> ParseResult<Self> {
26        let mut table = Self::default();
27
28        //
29        // Table header
30        reader.skip_u16()?; // format
31        let num_records = reader.read_u16()?;
32
33        //
34        // Name records
35        let string_offset = reader.read_u16()?;
36
37        //
38        // Records
39        table.records.reserve(num_records as usize);
40        for _ in 0..num_records {
41            let platform_id = reader.read_u16()?.into();
42            let encoding_id = reader.read_u16()?;
43            let language_id = reader.read_u16()?;
44            let name_id = reader.read_u16()?.into();
45            let length = reader.read_u16()?;
46            let offset = reader.read_u16()?;
47
48            // Read the name
49            let mut name_reader = reader.clone();
50            name_reader.advance_to(string_offset as usize + offset as usize)?;
51            let name = name_reader.read(length as usize)?;
52            let name = name.decode(platform_id, encoding_id);
53
54            debug_msg!(
55                "  Name record: {platform_id:?}::{encoding_id}::{language_id}::{name_id:?} = {name}"
56            );
57
58            table.records.push(NameRecord {
59                platform_id,
60                encoding_id,
61                language_id,
62                name_id,
63                name,
64            });
65        }
66
67        Ok(table)
68    }
69}
70
71/// The strings supported by the name table
72#[allow(missing_docs)]
73#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)]
74#[repr(u16)]
75pub enum NameKind {
76    CopyrightNotice = 0,
77    FontFamily = 1,
78    FontSubfamily = 2,
79    UniqueIdentifier = 3,
80    FullFontName = 4,
81    NameTableVersion = 5,
82    PostscriptName = 6,
83    Trademark = 7,
84    Manufacturer = 8,
85    Designer = 9,
86    Description = 10,
87    VendorUrl = 11,
88    DesignerUrl = 12,
89    LicenseDescription = 13,
90    LicenseInfoUrl = 14,
91
92    PreferredFamily = 16,
93    PreferredSubfamily = 17,
94    CompatibleFull = 18,
95
96    SampleText = 19,
97
98    PostscriptCid = 20,
99    WwsFamily = 21,
100    WwsSubfamily = 22,
101    LightBackgroundPalette = 23,
102    DarkBackgroundPalette = 24,
103    VariationsPostscriptNamePrefix = 25,
104
105    Other = 0xFFFF,
106}
107impl From<u16> for NameKind {
108    fn from(value: u16) -> Self {
109        match value {
110            0 => Self::CopyrightNotice,
111            1 => Self::FontFamily,
112            2 => Self::FontSubfamily,
113            3 => Self::UniqueIdentifier,
114            4 => Self::FullFontName,
115            5 => Self::NameTableVersion,
116            6 => Self::PostscriptName,
117            7 => Self::Trademark,
118            8 => Self::Manufacturer,
119            9 => Self::Designer,
120            10 => Self::Description,
121            11 => Self::VendorUrl,
122            12 => Self::DesignerUrl,
123            13 => Self::LicenseDescription,
124            14 => Self::LicenseInfoUrl,
125            16 => Self::PreferredFamily,
126            17 => Self::PreferredSubfamily,
127            18 => Self::CompatibleFull,
128            19 => Self::SampleText,
129            20 => Self::PostscriptCid,
130            21 => Self::WwsFamily,
131            22 => Self::WwsSubfamily,
132            23 => Self::LightBackgroundPalette,
133            24 => Self::DarkBackgroundPalette,
134            25 => Self::VariationsPostscriptNamePrefix,
135            _ => Self::Other,
136        }
137    }
138}
139
140/// Extension trait to decode a string from a byte array
141pub trait StringDecoderExt {
142    /// Decode a string from a byte array
143    fn decode(&self, platform_id: PlatformType, encoding_id: u16) -> String;
144}
145impl StringDecoderExt for [u8] {
146    fn decode(&self, platform_id: PlatformType, encoding_id: u16) -> String {
147        match (platform_id, encoding_id) {
148            //
149            // These are UTF-16 encoded strings
150            (PlatformType::Unicode, _) | (PlatformType::Microsoft, 1 | 10) => {
151                let words = self
152                    .chunks_exact(2)
153                    .map(|chunk| u16::from_be_bytes([chunk[0], chunk[1]]));
154                String::from_utf16_lossy(&words.collect::<Vec<u16>>())
155            }
156
157            (PlatformType::Macintosh, 0) => {
158                let (s, _, _) = encoding_rs::MACINTOSH.decode(self);
159                s.to_string()
160            }
161
162            _ => format!("UNSUPPORTED_STRING::{platform_id:?}::{encoding_id}"),
163        }
164    }
165}