1use crate::error::Result;
2use crate::stream::{FontReader, FontWriter};
3use crate::tables::{TtfTable, TtfTableWrite};
4use std::collections::HashMap;
5
6#[derive(Debug, Clone)]
8pub struct NameTable {
9 pub format: u16,
10 pub count: u16,
11 pub string_offset: u16,
12 pub name_records: Vec<NameRecord>,
13 pub string_data: HashMap<(u16, u16, u16, u16), Vec<u8>>, }
15
16#[derive(Debug, Clone)]
17pub struct NameRecord {
18 pub platform_id: u16,
19 pub encoding_id: u16,
20 pub language_id: u16,
21 pub name_id: u16,
22 pub length: u16,
23 pub offset: u16,
24}
25
26impl NameRecord {
27 pub const COPYRIGHT_NOTICE: u16 = 0;
28 pub const FONT_FAMILY_NAME: u16 = 1;
29 pub const FONT_SUBFAMILY_NAME: u16 = 2;
30 pub const UNIQUE_FONT_ID: u16 = 3;
31 pub const FULL_FONT_NAME: u16 = 4;
32 pub const VERSION_STRING: u16 = 5;
33 pub const POSTSCRIPT_NAME: u16 = 6;
34 pub const TRADEMARK: u16 = 7;
35 pub const MANUFACTURER_NAME: u16 = 8;
36 pub const DESIGNER: u16 = 9;
37 pub const DESCRIPTION: u16 = 10;
38 pub const VENDOR_URL: u16 = 11;
39 pub const DESIGNER_URL: u16 = 12;
40 pub const LICENSE_DESCRIPTION: u16 = 13;
41 pub const LICENSE_URL: u16 = 14;
42 pub const TYPOGRAPHIC_FAMILY_NAME: u16 = 16;
44 pub const TYPOGRAPHIC_SUBFAMILY_NAME: u16 = 17;
45 pub const COMPATIBLE_FULL_NAME: u16 = 18;
46 pub const SAMPLE_TEXT: u16 = 19;
47 pub const POSTSCRIPT_CID: u16 = 20;
48 pub const WWS_FAMILY_NAME: u16 = 21;
49 pub const WWS_SUBFAMILY_NAME: u16 = 22;
50 pub const LIGHT_BACKGROUND_PALETTE: u16 = 23;
51 pub const DARK_BACKGROUND_PALETTE: u16 = 24;
52}
53
54impl NameTable {
55 pub fn get_name(&self, name_id: u16) -> Option<(&NameRecord, String)> {
56 self.name_records
57 .iter()
58 .find(|r| r.name_id == name_id)
59 .map(|record| (record, String::new())) }
61
62 pub fn get_font_name(&self) -> Option<&NameRecord> {
63 self.name_records
64 .iter()
65 .find(|r| r.name_id == NameRecord::FONT_FAMILY_NAME)
66 }
67
68 pub fn get_full_name(&self) -> Option<&NameRecord> {
69 self.name_records
70 .iter()
71 .find(|r| r.name_id == NameRecord::FULL_FONT_NAME)
72 }
73
74 pub fn get_postscript_name(&self) -> Option<&NameRecord> {
75 self.name_records
76 .iter()
77 .find(|r| r.name_id == NameRecord::POSTSCRIPT_NAME)
78 }
79
80 pub fn set_name(&mut self, name: &str, platform_id: u16, encoding_id: u16, language_id: u16, name_id: u16) {
82 let name_bytes: Vec<u16> = name.encode_utf16().collect();
84 let mut name_data = Vec::new();
85 for code_unit in name_bytes {
86 name_data.extend_from_slice(&code_unit.to_be_bytes());
87 }
88
89 let key = (platform_id, encoding_id, language_id, name_id);
90 self.string_data.insert(key, name_data.clone());
91
92 self.name_records.retain(|r| {
94 !(r.platform_id == platform_id
95 && r.encoding_id == encoding_id
96 && r.language_id == language_id
97 && r.name_id == name_id)
98 });
99
100 self.name_records.push(NameRecord {
102 platform_id,
103 encoding_id,
104 language_id,
105 name_id,
106 length: name_data.len() as u16,
107 offset: 0, });
109
110 self.count = self.name_records.len() as u16;
111 }
112}
113
114impl TtfTable for NameTable {
115 fn from_reader(reader: &mut FontReader, _length: u32) -> Result<Self> {
116 let format = reader.read_u16()?;
117 let count = reader.read_u16()?;
118 let string_offset = reader.read_u16()?;
119
120 let mut name_records = Vec::with_capacity(count as usize);
121 for _ in 0..count {
122 name_records.push(NameRecord {
123 platform_id: reader.read_u16()?,
124 encoding_id: reader.read_u16()?,
125 language_id: reader.read_u16()?,
126 name_id: reader.read_u16()?,
127 length: reader.read_u16()?,
128 offset: reader.read_u16()?,
129 });
130 }
131
132 Ok(NameTable {
136 format,
137 count,
138 string_offset,
139 name_records,
140 string_data: HashMap::new(),
141 })
142 }
143}
144
145impl TtfTableWrite for NameTable {
146 fn table_tag() -> &'static [u8; 4] {
147 b"name"
148 }
149
150 fn write(&self, writer: &mut FontWriter) -> Result<()> {
151 let header_size = 6 + (self.name_records.len() * 12);
153 let mut current_offset = header_size as u16;
154 let mut all_string_data = Vec::new();
155
156 let mut updated_records = self.name_records.clone();
158 for record in &mut updated_records {
159 let key = (record.platform_id, record.encoding_id, record.language_id, record.name_id);
160 if let Some(data) = self.string_data.get(&key) {
161 record.offset = current_offset;
162 record.length = data.len() as u16;
163 all_string_data.extend_from_slice(data);
164 current_offset += data.len() as u16;
165 }
166 }
167
168 writer.write_u16(self.format);
170 writer.write_u16(updated_records.len() as u16);
171 writer.write_u16(header_size as u16);
172
173 for record in &updated_records {
175 writer.write_u16(record.platform_id);
176 writer.write_u16(record.encoding_id);
177 writer.write_u16(record.language_id);
178 writer.write_u16(record.name_id);
179 writer.write_u16(record.length);
180 writer.write_u16(record.offset);
181 }
182
183 writer.write_bytes(&all_string_data);
185
186 Ok(())
187 }
188}