|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
|
2 // Use of this source code is governed by a BSD-style license that can be |
|
3 // found in the LICENSE file. |
|
4 |
|
5 #include "name.h" |
|
6 |
|
7 #include <algorithm> |
|
8 #include <cstring> |
|
9 |
|
10 #include "cff.h" |
|
11 |
|
12 // name - Naming Table |
|
13 // http://www.microsoft.com/typography/otspec/name.htm |
|
14 |
|
15 #define TABLE_NAME "name" |
|
16 |
|
17 namespace { |
|
18 |
|
19 bool ValidInPsName(char c) { |
|
20 return (c > 0x20 && c < 0x7f && !std::strchr("[](){}<>/%", c)); |
|
21 } |
|
22 |
|
23 bool CheckPsNameAscii(const std::string& name) { |
|
24 for (unsigned i = 0; i < name.size(); ++i) { |
|
25 if (!ValidInPsName(name[i])) { |
|
26 return false; |
|
27 } |
|
28 } |
|
29 return true; |
|
30 } |
|
31 |
|
32 bool CheckPsNameUtf16Be(const std::string& name) { |
|
33 if ((name.size() & 1) != 0) |
|
34 return false; |
|
35 |
|
36 for (unsigned i = 0; i < name.size(); i += 2) { |
|
37 if (name[i] != 0) { |
|
38 return false; |
|
39 } |
|
40 if (!ValidInPsName(name[i+1])) { |
|
41 return false; |
|
42 } |
|
43 } |
|
44 return true; |
|
45 } |
|
46 |
|
47 void AssignToUtf16BeFromAscii(std::string* target, |
|
48 const std::string& source) { |
|
49 target->resize(source.size() * 2); |
|
50 for (unsigned i = 0, j = 0; i < source.size(); i++) { |
|
51 (*target)[j++] = '\0'; |
|
52 (*target)[j++] = source[i]; |
|
53 } |
|
54 } |
|
55 |
|
56 } // namespace |
|
57 |
|
58 |
|
59 namespace ots { |
|
60 |
|
61 bool ots_name_parse(OpenTypeFile* file, const uint8_t* data, size_t length) { |
|
62 Buffer table(data, length); |
|
63 |
|
64 OpenTypeNAME* name = new OpenTypeNAME; |
|
65 file->name = name; |
|
66 |
|
67 uint16_t format = 0; |
|
68 if (!table.ReadU16(&format) || format > 1) { |
|
69 return OTS_FAILURE_MSG("Failed to read name table format or bad format %d", format); |
|
70 } |
|
71 |
|
72 uint16_t count = 0; |
|
73 if (!table.ReadU16(&count)) { |
|
74 return OTS_FAILURE_MSG("Failed to read name count"); |
|
75 } |
|
76 |
|
77 uint16_t string_offset = 0; |
|
78 if (!table.ReadU16(&string_offset) || string_offset > length) { |
|
79 return OTS_FAILURE_MSG("Failed to read strings offset"); |
|
80 } |
|
81 const char* string_base = reinterpret_cast<const char*>(data) + |
|
82 string_offset; |
|
83 |
|
84 NameRecord prev_record; |
|
85 bool sort_required = false; |
|
86 |
|
87 // Read all the names, discarding any with invalid IDs, |
|
88 // and any where the offset/length would be outside the table. |
|
89 // A stricter alternative would be to reject the font if there |
|
90 // are invalid name records, but it's not clear that is necessary. |
|
91 for (unsigned i = 0; i < count; ++i) { |
|
92 NameRecord rec; |
|
93 uint16_t name_length, name_offset; |
|
94 if (!table.ReadU16(&rec.platform_id) || |
|
95 !table.ReadU16(&rec.encoding_id) || |
|
96 !table.ReadU16(&rec.language_id) || |
|
97 !table.ReadU16(&rec.name_id) || |
|
98 !table.ReadU16(&name_length) || |
|
99 !table.ReadU16(&name_offset)) { |
|
100 return OTS_FAILURE_MSG("Failed to read name entry %d", i); |
|
101 } |
|
102 // check platform & encoding, discard names with unknown values |
|
103 switch (rec.platform_id) { |
|
104 case 0: // Unicode |
|
105 if (rec.encoding_id > 6) { |
|
106 continue; |
|
107 } |
|
108 break; |
|
109 case 1: // Macintosh |
|
110 if (rec.encoding_id > 32) { |
|
111 continue; |
|
112 } |
|
113 break; |
|
114 case 2: // ISO |
|
115 if (rec.encoding_id > 2) { |
|
116 continue; |
|
117 } |
|
118 break; |
|
119 case 3: // Windows: IDs 7 to 9 are "reserved" |
|
120 if (rec.encoding_id > 6 && rec.encoding_id != 10) { |
|
121 continue; |
|
122 } |
|
123 break; |
|
124 case 4: // Custom (OTF Windows NT compatibility) |
|
125 if (rec.encoding_id > 255) { |
|
126 continue; |
|
127 } |
|
128 break; |
|
129 default: // unknown platform |
|
130 continue; |
|
131 } |
|
132 |
|
133 const unsigned name_end = static_cast<unsigned>(string_offset) + |
|
134 name_offset + name_length; |
|
135 if (name_end > length) { |
|
136 continue; |
|
137 } |
|
138 rec.text.resize(name_length); |
|
139 rec.text.assign(string_base + name_offset, name_length); |
|
140 |
|
141 if (rec.name_id == 6) { |
|
142 // PostScript name: check that it is valid, if not then discard it |
|
143 if (rec.platform_id == 1) { |
|
144 if (file->cff && !file->cff->name.empty()) { |
|
145 rec.text = file->cff->name; |
|
146 } else if (!CheckPsNameAscii(rec.text)) { |
|
147 continue; |
|
148 } |
|
149 } else if (rec.platform_id == 0 || rec.platform_id == 3) { |
|
150 if (file->cff && !file->cff->name.empty()) { |
|
151 AssignToUtf16BeFromAscii(&rec.text, file->cff->name); |
|
152 } else if (!CheckPsNameUtf16Be(rec.text)) { |
|
153 continue; |
|
154 } |
|
155 } |
|
156 } |
|
157 |
|
158 if ((i > 0) && !(prev_record < rec)) { |
|
159 OTS_WARNING("name records are not sorted."); |
|
160 sort_required = true; |
|
161 } |
|
162 |
|
163 name->names.push_back(rec); |
|
164 prev_record = rec; |
|
165 } |
|
166 |
|
167 if (format == 1) { |
|
168 // extended name table format with language tags |
|
169 uint16_t lang_tag_count; |
|
170 if (!table.ReadU16(&lang_tag_count)) { |
|
171 return OTS_FAILURE_MSG("Failed to read language tag count"); |
|
172 } |
|
173 for (unsigned i = 0; i < lang_tag_count; ++i) { |
|
174 uint16_t tag_length = 0; |
|
175 uint16_t tag_offset = 0; |
|
176 if (!table.ReadU16(&tag_length) || !table.ReadU16(&tag_offset)) { |
|
177 return OTS_FAILURE_MSG("Faile to read tag length or offset"); |
|
178 } |
|
179 const unsigned tag_end = static_cast<unsigned>(string_offset) + |
|
180 tag_offset + tag_length; |
|
181 if (tag_end > length) { |
|
182 return OTS_FAILURE_MSG("bad end of tag %d > %ld for name entry %d", tag_end, length, i); |
|
183 } |
|
184 std::string tag(string_base + tag_offset, tag_length); |
|
185 name->lang_tags.push_back(tag); |
|
186 } |
|
187 } |
|
188 |
|
189 if (table.offset() > string_offset) { |
|
190 // the string storage apparently overlapped the name/tag records; |
|
191 // consider this font to be badly broken |
|
192 return OTS_FAILURE_MSG("Bad table offset %ld > %d", table.offset(), string_offset); |
|
193 } |
|
194 |
|
195 // check existence of required name strings (synthesize if necessary) |
|
196 // [0 - copyright - skip] |
|
197 // 1 - family |
|
198 // 2 - subfamily |
|
199 // [3 - unique ID - skip] |
|
200 // 4 - full name |
|
201 // 5 - version |
|
202 // 6 - postscript name |
|
203 static const unsigned kStdNameCount = 7; |
|
204 static const char* kStdNames[kStdNameCount] = { |
|
205 NULL, |
|
206 "OTS derived font", |
|
207 "Unspecified", |
|
208 NULL, |
|
209 "OTS derived font", |
|
210 "1.000", |
|
211 "OTS-derived-font" |
|
212 }; |
|
213 // The spec says that "In CFF OpenType fonts, these two name strings, when |
|
214 // translated to ASCII, must also be identical to the font name as stored in |
|
215 // the CFF's Name INDEX." And actually, Mac OS X's font parser requires that. |
|
216 if (file->cff && !file->cff->name.empty()) { |
|
217 kStdNames[6] = file->cff->name.c_str(); |
|
218 } |
|
219 |
|
220 // scan the names to check whether the required "standard" ones are present; |
|
221 // if not, we'll add our fixed versions here |
|
222 bool mac_name[kStdNameCount] = { 0 }; |
|
223 bool win_name[kStdNameCount] = { 0 }; |
|
224 for (std::vector<NameRecord>::iterator name_iter = name->names.begin(); |
|
225 name_iter != name->names.end(); name_iter++) { |
|
226 const uint16_t id = name_iter->name_id; |
|
227 if (id >= kStdNameCount || kStdNames[id] == NULL) { |
|
228 continue; |
|
229 } |
|
230 if (name_iter->platform_id == 1) { |
|
231 mac_name[id] = true; |
|
232 continue; |
|
233 } |
|
234 if (name_iter->platform_id == 3) { |
|
235 win_name[id] = true; |
|
236 continue; |
|
237 } |
|
238 } |
|
239 |
|
240 for (unsigned i = 0; i < kStdNameCount; ++i) { |
|
241 if (kStdNames[i] == NULL) { |
|
242 continue; |
|
243 } |
|
244 if (!mac_name[i]) { |
|
245 NameRecord rec(1 /* platform_id */, 0 /* encoding_id */, |
|
246 0 /* language_id */ , i /* name_id */); |
|
247 rec.text.assign(kStdNames[i]); |
|
248 name->names.push_back(rec); |
|
249 sort_required = true; |
|
250 } |
|
251 if (!win_name[i]) { |
|
252 NameRecord rec(3 /* platform_id */, 1 /* encoding_id */, |
|
253 1033 /* language_id */ , i /* name_id */); |
|
254 AssignToUtf16BeFromAscii(&rec.text, std::string(kStdNames[i])); |
|
255 name->names.push_back(rec); |
|
256 sort_required = true; |
|
257 } |
|
258 } |
|
259 |
|
260 if (sort_required) { |
|
261 std::sort(name->names.begin(), name->names.end()); |
|
262 } |
|
263 |
|
264 return true; |
|
265 } |
|
266 |
|
267 bool ots_name_should_serialise(OpenTypeFile* file) { |
|
268 return file->name != NULL; |
|
269 } |
|
270 |
|
271 bool ots_name_serialise(OTSStream* out, OpenTypeFile* file) { |
|
272 const OpenTypeNAME* name = file->name; |
|
273 |
|
274 uint16_t name_count = name->names.size(); |
|
275 uint16_t lang_tag_count = name->lang_tags.size(); |
|
276 uint16_t format = 0; |
|
277 size_t string_offset = 6 + name_count * 12; |
|
278 |
|
279 if (name->lang_tags.size() > 0) { |
|
280 // lang tags require a format-1 name table |
|
281 format = 1; |
|
282 string_offset += 2 + lang_tag_count * 4; |
|
283 } |
|
284 if (string_offset > 0xffff) { |
|
285 return OTS_FAILURE_MSG("Bad string offset %ld", string_offset); |
|
286 } |
|
287 if (!out->WriteU16(format) || |
|
288 !out->WriteU16(name_count) || |
|
289 !out->WriteU16(string_offset)) { |
|
290 return OTS_FAILURE_MSG("Failed to write name header"); |
|
291 } |
|
292 |
|
293 std::string string_data; |
|
294 for (std::vector<NameRecord>::const_iterator name_iter = name->names.begin(); |
|
295 name_iter != name->names.end(); name_iter++) { |
|
296 const NameRecord& rec = *name_iter; |
|
297 if (!out->WriteU16(rec.platform_id) || |
|
298 !out->WriteU16(rec.encoding_id) || |
|
299 !out->WriteU16(rec.language_id) || |
|
300 !out->WriteU16(rec.name_id) || |
|
301 !out->WriteU16(rec.text.size()) || |
|
302 !out->WriteU16(string_data.size()) ) { |
|
303 return OTS_FAILURE_MSG("Faile to write name entry"); |
|
304 } |
|
305 string_data.append(rec.text); |
|
306 } |
|
307 |
|
308 if (format == 1) { |
|
309 if (!out->WriteU16(lang_tag_count)) { |
|
310 return OTS_FAILURE_MSG("Faile to write language tag count"); |
|
311 } |
|
312 for (std::vector<std::string>::const_iterator tag_iter = |
|
313 name->lang_tags.begin(); |
|
314 tag_iter != name->lang_tags.end(); tag_iter++) { |
|
315 if (!out->WriteU16(tag_iter->size()) || |
|
316 !out->WriteU16(string_data.size())) { |
|
317 return OTS_FAILURE_MSG("Failed to write string"); |
|
318 } |
|
319 string_data.append(*tag_iter); |
|
320 } |
|
321 } |
|
322 |
|
323 if (!out->Write(string_data.data(), string_data.size())) { |
|
324 return OTS_FAILURE_MSG("Faile to write string data"); |
|
325 } |
|
326 |
|
327 return true; |
|
328 } |
|
329 |
|
330 void ots_name_free(OpenTypeFile* file) { |
|
331 delete file->name; |
|
332 } |
|
333 |
|
334 } // namespace |