// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "name.h" #include #include #include "cff.h" // name - Naming Table // http://www.microsoft.com/opentype/otspec/name.htm namespace { bool ValidInPsName(char c) { return (c > 0x20 && c < 0x7f && !std::strchr("[](){}<>/%", c)); } bool CheckPsNameAscii(const std::string& name) { for (unsigned i = 0; i < name.size(); ++i) { if (!ValidInPsName(name[i])) { return false; } } return true; } bool CheckPsNameUtf16Be(const std::string& name) { if ((name.size() & 1) != 0) return false; for (unsigned i = 0; i < name.size(); i += 2) { if (name[i] != 0) { return false; } if (!ValidInPsName(name[i+1])) { return false; } } return true; } void AssignToUtf16BeFromAscii(std::string* target, const std::string& source) { target->resize(source.size() * 2); for (unsigned i = 0, j = 0; i < source.size(); i++) { (*target)[j++] = '\0'; (*target)[j++] = source[i]; } } } // namespace namespace ots { bool ots_name_parse(OpenTypeFile* file, const uint8_t* data, size_t length) { Buffer table(data, length); OpenTypeNAME* name = new OpenTypeNAME; file->name = name; uint16_t format = 0; if (!table.ReadU16(&format) || format > 1) { return OTS_FAILURE(); } uint16_t count = 0; if (!table.ReadU16(&count)) { return OTS_FAILURE(); } uint16_t string_offset = 0; if (!table.ReadU16(&string_offset) || string_offset > length) { return OTS_FAILURE(); } const char* string_base = reinterpret_cast(data) + string_offset; NameRecord prev_record; bool sort_required = false; // Read all the names, discarding any with invalid IDs, // and any where the offset/length would be outside the table. // A stricter alternative would be to reject the font if there // are invalid name records, but it's not clear that is necessary. for (unsigned i = 0; i < count; ++i) { NameRecord rec; uint16_t name_length, name_offset; if (!table.ReadU16(&rec.platform_id) || !table.ReadU16(&rec.encoding_id) || !table.ReadU16(&rec.language_id) || !table.ReadU16(&rec.name_id) || !table.ReadU16(&name_length) || !table.ReadU16(&name_offset)) { return OTS_FAILURE(); } // check platform & encoding, discard names with unknown values switch (rec.platform_id) { case 0: // Unicode if (rec.encoding_id > 6) { continue; } break; case 1: // Macintosh if (rec.encoding_id > 32) { continue; } break; case 2: // ISO if (rec.encoding_id > 2) { continue; } break; case 3: // Windows: IDs 7 to 9 are "reserved" if (rec.encoding_id > 6 && rec.encoding_id != 10) { continue; } break; case 4: // Custom (OTF Windows NT compatibility) if (rec.encoding_id > 255) { continue; } break; default: // unknown platform continue; } const unsigned name_end = static_cast(string_offset) + name_offset + name_length; if (name_end > length) { continue; } rec.text.resize(name_length); rec.text.assign(string_base + name_offset, name_length); if (rec.name_id == 6) { // PostScript name: check that it is valid, if not then discard it if (rec.platform_id == 1) { if (file->cff && !file->cff->name.empty()) { rec.text = file->cff->name; } else if (!CheckPsNameAscii(rec.text)) { continue; } } else if (rec.platform_id == 0 || rec.platform_id == 3) { if (file->cff && !file->cff->name.empty()) { AssignToUtf16BeFromAscii(&rec.text, file->cff->name); } else if (!CheckPsNameUtf16Be(rec.text)) { continue; } } } if ((i > 0) && !(prev_record < rec)) { OTS_WARNING("name records are not sorted."); sort_required = true; } name->names.push_back(rec); prev_record = rec; } if (format == 1) { // extended name table format with language tags uint16_t lang_tag_count; if (!table.ReadU16(&lang_tag_count)) { return OTS_FAILURE(); } for (unsigned i = 0; i < lang_tag_count; ++i) { uint16_t tag_length = 0; uint16_t tag_offset = 0; if (!table.ReadU16(&tag_length) || !table.ReadU16(&tag_offset)) { return OTS_FAILURE(); } const unsigned tag_end = static_cast(string_offset) + tag_offset + tag_length; if (tag_end > length) { return OTS_FAILURE(); } std::string tag(string_base + tag_offset, tag_length); name->lang_tags.push_back(tag); } } if (table.offset() > string_offset) { // the string storage apparently overlapped the name/tag records; // consider this font to be badly broken return OTS_FAILURE(); } // check existence of required name strings (synthesize if necessary) // [0 - copyright - skip] // 1 - family // 2 - subfamily // [3 - unique ID - skip] // 4 - full name // 5 - version // 6 - postscript name static const unsigned kStdNameCount = 7; static const char* kStdNames[kStdNameCount] = { NULL, "OTS derived font", "Unspecified", NULL, "OTS derived font", "1.000", "OTS-derived-font" }; // The spec says that "In CFF OpenType fonts, these two name strings, when // translated to ASCII, must also be identical to the font name as stored in // the CFF's Name INDEX." And actually, Mac OS X's font parser requires that. if (file->cff && !file->cff->name.empty()) { kStdNames[6] = file->cff->name.c_str(); } // scan the names to check whether the required "standard" ones are present; // if not, we'll add our fixed versions here bool mac_name[kStdNameCount] = { 0 }; bool win_name[kStdNameCount] = { 0 }; for (std::vector::iterator name_iter = name->names.begin(); name_iter != name->names.end(); name_iter++) { const uint16_t id = name_iter->name_id; if (id >= kStdNameCount || kStdNames[id] == NULL) { continue; } if (name_iter->platform_id == 1) { mac_name[id] = true; continue; } if (name_iter->platform_id == 3) { win_name[id] = true; continue; } } for (unsigned i = 0; i < kStdNameCount; ++i) { if (kStdNames[i] == NULL) { continue; } if (!mac_name[i]) { NameRecord rec(1 /* platform_id */, 0 /* encoding_id */, 0 /* language_id */ , i /* name_id */); rec.text.assign(kStdNames[i]); name->names.push_back(rec); sort_required = true; } if (!win_name[i]) { NameRecord rec(3 /* platform_id */, 1 /* encoding_id */, 1033 /* language_id */ , i /* name_id */); AssignToUtf16BeFromAscii(&rec.text, std::string(kStdNames[i])); name->names.push_back(rec); sort_required = true; } } if (sort_required) { std::sort(name->names.begin(), name->names.end()); } return true; } bool ots_name_should_serialise(OpenTypeFile* file) { return file->name != NULL; } bool ots_name_serialise(OTSStream* out, OpenTypeFile* file) { const OpenTypeNAME* name = file->name; uint16_t name_count = name->names.size(); uint16_t lang_tag_count = name->lang_tags.size(); uint16_t format = 0; size_t string_offset = 6 + name_count * 12; if (name->lang_tags.size() > 0) { // lang tags require a format-1 name table format = 1; string_offset += 2 + lang_tag_count * 4; } if (string_offset > 0xffff) { return OTS_FAILURE(); } if (!out->WriteU16(format) || !out->WriteU16(name_count) || !out->WriteU16(string_offset)) { return OTS_FAILURE(); } std::string string_data; for (std::vector::const_iterator name_iter = name->names.begin(); name_iter != name->names.end(); name_iter++) { const NameRecord& rec = *name_iter; if (!out->WriteU16(rec.platform_id) || !out->WriteU16(rec.encoding_id) || !out->WriteU16(rec.language_id) || !out->WriteU16(rec.name_id) || !out->WriteU16(rec.text.size()) || !out->WriteU16(string_data.size()) ) { return OTS_FAILURE(); } string_data.append(rec.text); } if (format == 1) { if (!out->WriteU16(lang_tag_count)) { return OTS_FAILURE(); } for (std::vector::const_iterator tag_iter = name->lang_tags.begin(); tag_iter != name->lang_tags.end(); tag_iter++) { if (!out->WriteU16(tag_iter->size()) || !out->WriteU16(string_data.size())) { return OTS_FAILURE(); } string_data.append(*tag_iter); } } if (!out->Write(string_data.data(), string_data.size())) { return OTS_FAILURE(); } return true; } void ots_name_free(OpenTypeFile* file) { delete file->name; } } // namespace