// 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 // name - Naming Table // http://www.microsoft.com/typography/otspec/name.htm #define TABLE_NAME "name" 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(Font *font, const uint8_t* data, size_t length) { Buffer table(data, length); OpenTypeNAME* name = new OpenTypeNAME; font->name = name; uint16_t format = 0; if (!table.ReadU16(&format) || format > 1) { return OTS_FAILURE_MSG("Failed to read name table format or bad format %d", format); } uint16_t count = 0; if (!table.ReadU16(&count)) { return OTS_FAILURE_MSG("Failed to read name count"); } uint16_t string_offset = 0; if (!table.ReadU16(&string_offset) || string_offset > length) { return OTS_FAILURE_MSG("Failed to read strings offset"); } const char* string_base = reinterpret_cast(data) + string_offset; 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 = 0; 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_MSG("Failed to read name entry %d", i); } // 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 (!CheckPsNameAscii(rec.text)) { continue; } } else if (rec.platform_id == 0 || rec.platform_id == 3) { if (!CheckPsNameUtf16Be(rec.text)) { continue; } } } if (!name->names.empty() && !(name->names.back() < rec)) { OTS_WARNING("name records are not sorted."); sort_required = true; } name->names.push_back(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_MSG("Failed to read language tag count"); } 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_MSG("Faile to read tag length or offset"); } const unsigned tag_end = static_cast(string_offset) + tag_offset + tag_length; if (tag_end > length) { return OTS_FAILURE_MSG("bad end of tag %d > %ld for name entry %d", tag_end, length, i); } 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_MSG("Bad table offset %ld > %d", table.offset(), string_offset); } // 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 uint16_t kStdNameCount = 7; static const char* kStdNames[kStdNameCount] = { NULL, "OTS derived font", "Unspecified", NULL, "OTS derived font", "1.000", "OTS-derived-font" }; // 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 (uint16_t i = 0; i < kStdNameCount; ++i) { if (kStdNames[i] == NULL) { continue; } if (!mac_name[i] && !win_name[i]) { NameRecord mac_rec(1 /* platform_id */, 0 /* encoding_id */, 0 /* language_id */ , i /* name_id */); mac_rec.text.assign(kStdNames[i]); NameRecord win_rec(3 /* platform_id */, 1 /* encoding_id */, 1033 /* language_id */ , i /* name_id */); AssignToUtf16BeFromAscii(&win_rec.text, std::string(kStdNames[i])); name->names.push_back(mac_rec); name->names.push_back(win_rec); sort_required = true; } } if (sort_required) { std::sort(name->names.begin(), name->names.end()); } return true; } bool ots_name_should_serialise(Font *font) { return font->name != NULL; } bool ots_name_serialise(OTSStream* out, Font *font) { const OpenTypeNAME* name = font->name; uint16_t name_count = static_cast(name->names.size()); uint16_t lang_tag_count = static_cast(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_MSG("Bad string offset %ld", string_offset); } if (!out->WriteU16(format) || !out->WriteU16(name_count) || !out->WriteU16(static_cast(string_offset))) { return OTS_FAILURE_MSG("Failed to write name header"); } 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 (string_data.size() + rec.text.size() > std::numeric_limits::max() || !out->WriteU16(rec.platform_id) || !out->WriteU16(rec.encoding_id) || !out->WriteU16(rec.language_id) || !out->WriteU16(rec.name_id) || !out->WriteU16(static_cast(rec.text.size())) || !out->WriteU16(static_cast(string_data.size())) ) { return OTS_FAILURE_MSG("Faile to write name entry"); } string_data.append(rec.text); } if (format == 1) { if (!out->WriteU16(lang_tag_count)) { return OTS_FAILURE_MSG("Faile to write language tag count"); } for (std::vector::const_iterator tag_iter = name->lang_tags.begin(); tag_iter != name->lang_tags.end(); ++tag_iter) { if (string_data.size() + tag_iter->size() > std::numeric_limits::max() || !out->WriteU16(static_cast(tag_iter->size())) || !out->WriteU16(static_cast(string_data.size()))) { return OTS_FAILURE_MSG("Failed to write string"); } string_data.append(*tag_iter); } } if (!out->Write(string_data.data(), string_data.size())) { return OTS_FAILURE_MSG("Faile to write string data"); } return true; } void ots_name_reuse(Font *font, Font *other) { font->name = other->name; font->name_reused = true; } void ots_name_free(Font *font) { delete font->name; } } // namespace #undef TABLE_NAME