summaryrefslogtreecommitdiffstats
path: root/gfx/ots/src/name.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gfx/ots/src/name.cc')
-rw-r--r--gfx/ots/src/name.cc330
1 files changed, 330 insertions, 0 deletions
diff --git a/gfx/ots/src/name.cc b/gfx/ots/src/name.cc
new file mode 100644
index 000000000..e55be7537
--- /dev/null
+++ b/gfx/ots/src/name.cc
@@ -0,0 +1,330 @@
+// 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>
+
+// 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<const char*>(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<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 (!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<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"
+ };
+
+ // 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] && !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<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_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