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