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 |