| 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 |