OLD | NEW |
| (Empty) |
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 = 0; | |
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_o
ffset); | |
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 uint16_t 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 (uint16_t 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 = static_cast<uint16_t>(name->names.size()); | |
275 uint16_t lang_tag_count = static_cast<uint16_t>(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(static_cast<uint16_t>(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 (string_data.size() + rec.text.size() > | |
298 std::numeric_limits<uint16_t>::max() || | |
299 !out->WriteU16(rec.platform_id) || | |
300 !out->WriteU16(rec.encoding_id) || | |
301 !out->WriteU16(rec.language_id) || | |
302 !out->WriteU16(rec.name_id) || | |
303 !out->WriteU16(static_cast<uint16_t>(rec.text.size())) || | |
304 !out->WriteU16(static_cast<uint16_t>(string_data.size())) ) { | |
305 return OTS_FAILURE_MSG("Faile to write name entry"); | |
306 } | |
307 string_data.append(rec.text); | |
308 } | |
309 | |
310 if (format == 1) { | |
311 if (!out->WriteU16(lang_tag_count)) { | |
312 return OTS_FAILURE_MSG("Faile to write language tag count"); | |
313 } | |
314 for (std::vector<std::string>::const_iterator tag_iter = | |
315 name->lang_tags.begin(); | |
316 tag_iter != name->lang_tags.end(); tag_iter++) { | |
317 if (string_data.size() + tag_iter->size() > | |
318 std::numeric_limits<uint16_t>::max() || | |
319 !out->WriteU16(static_cast<uint16_t>(tag_iter->size())) || | |
320 !out->WriteU16(static_cast<uint16_t>(string_data.size()))) { | |
321 return OTS_FAILURE_MSG("Failed to write string"); | |
322 } | |
323 string_data.append(*tag_iter); | |
324 } | |
325 } | |
326 | |
327 if (!out->Write(string_data.data(), string_data.size())) { | |
328 return OTS_FAILURE_MSG("Faile to write string data"); | |
329 } | |
330 | |
331 return true; | |
332 } | |
333 | |
334 void ots_name_free(OpenTypeFile* file) { | |
335 delete file->name; | |
336 } | |
337 | |
338 } // namespace | |
339 | |
340 #undef TABLE_NAME | |
OLD | NEW |