Index: third_party/ots/src/name.cc |
diff --git a/third_party/ots/src/name.cc b/third_party/ots/src/name.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..2ea10dc8e67d2ffb93fbbf5d6f45e9bc05c5c82b |
--- /dev/null |
+++ b/third_party/ots/src/name.cc |
@@ -0,0 +1,340 @@ |
+// 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 <algorithm> |
+#include <cstring> |
+ |
+#include "cff.h" |
+ |
+// 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(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_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<const char*>(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 = 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<unsigned>(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_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<unsigned>(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" |
+ }; |
+ // 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<NameRecord>::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]) { |
+ 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 = static_cast<uint16_t>(name->names.size()); |
+ uint16_t lang_tag_count = static_cast<uint16_t>(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<uint16_t>(string_offset))) { |
+ return OTS_FAILURE_MSG("Failed to write name header"); |
+ } |
+ |
+ std::string string_data; |
+ for (std::vector<NameRecord>::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<uint16_t>::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<uint16_t>(rec.text.size())) || |
+ !out->WriteU16(static_cast<uint16_t>(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<std::string>::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<uint16_t>::max() || |
+ !out->WriteU16(static_cast<uint16_t>(tag_iter->size())) || |
+ !out->WriteU16(static_cast<uint16_t>(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_free(OpenTypeFile* file) { |
+ delete file->name; |
+} |
+ |
+} // namespace |
+ |
+#undef TABLE_NAME |