gfx/ots/src/name.cc

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
michael@0 2 // Use of this source code is governed by a BSD-style license that can be
michael@0 3 // found in the LICENSE file.
michael@0 4
michael@0 5 #include "name.h"
michael@0 6
michael@0 7 #include <algorithm>
michael@0 8 #include <cstring>
michael@0 9
michael@0 10 #include "cff.h"
michael@0 11
michael@0 12 // name - Naming Table
michael@0 13 // http://www.microsoft.com/typography/otspec/name.htm
michael@0 14
michael@0 15 #define TABLE_NAME "name"
michael@0 16
michael@0 17 namespace {
michael@0 18
michael@0 19 bool ValidInPsName(char c) {
michael@0 20 return (c > 0x20 && c < 0x7f && !std::strchr("[](){}<>/%", c));
michael@0 21 }
michael@0 22
michael@0 23 bool CheckPsNameAscii(const std::string& name) {
michael@0 24 for (unsigned i = 0; i < name.size(); ++i) {
michael@0 25 if (!ValidInPsName(name[i])) {
michael@0 26 return false;
michael@0 27 }
michael@0 28 }
michael@0 29 return true;
michael@0 30 }
michael@0 31
michael@0 32 bool CheckPsNameUtf16Be(const std::string& name) {
michael@0 33 if ((name.size() & 1) != 0)
michael@0 34 return false;
michael@0 35
michael@0 36 for (unsigned i = 0; i < name.size(); i += 2) {
michael@0 37 if (name[i] != 0) {
michael@0 38 return false;
michael@0 39 }
michael@0 40 if (!ValidInPsName(name[i+1])) {
michael@0 41 return false;
michael@0 42 }
michael@0 43 }
michael@0 44 return true;
michael@0 45 }
michael@0 46
michael@0 47 void AssignToUtf16BeFromAscii(std::string* target,
michael@0 48 const std::string& source) {
michael@0 49 target->resize(source.size() * 2);
michael@0 50 for (unsigned i = 0, j = 0; i < source.size(); i++) {
michael@0 51 (*target)[j++] = '\0';
michael@0 52 (*target)[j++] = source[i];
michael@0 53 }
michael@0 54 }
michael@0 55
michael@0 56 } // namespace
michael@0 57
michael@0 58
michael@0 59 namespace ots {
michael@0 60
michael@0 61 bool ots_name_parse(OpenTypeFile* file, const uint8_t* data, size_t length) {
michael@0 62 Buffer table(data, length);
michael@0 63
michael@0 64 OpenTypeNAME* name = new OpenTypeNAME;
michael@0 65 file->name = name;
michael@0 66
michael@0 67 uint16_t format = 0;
michael@0 68 if (!table.ReadU16(&format) || format > 1) {
michael@0 69 return OTS_FAILURE_MSG("Failed to read name table format or bad format %d", format);
michael@0 70 }
michael@0 71
michael@0 72 uint16_t count = 0;
michael@0 73 if (!table.ReadU16(&count)) {
michael@0 74 return OTS_FAILURE_MSG("Failed to read name count");
michael@0 75 }
michael@0 76
michael@0 77 uint16_t string_offset = 0;
michael@0 78 if (!table.ReadU16(&string_offset) || string_offset > length) {
michael@0 79 return OTS_FAILURE_MSG("Failed to read strings offset");
michael@0 80 }
michael@0 81 const char* string_base = reinterpret_cast<const char*>(data) +
michael@0 82 string_offset;
michael@0 83
michael@0 84 NameRecord prev_record;
michael@0 85 bool sort_required = false;
michael@0 86
michael@0 87 // Read all the names, discarding any with invalid IDs,
michael@0 88 // and any where the offset/length would be outside the table.
michael@0 89 // A stricter alternative would be to reject the font if there
michael@0 90 // are invalid name records, but it's not clear that is necessary.
michael@0 91 for (unsigned i = 0; i < count; ++i) {
michael@0 92 NameRecord rec;
michael@0 93 uint16_t name_length, name_offset;
michael@0 94 if (!table.ReadU16(&rec.platform_id) ||
michael@0 95 !table.ReadU16(&rec.encoding_id) ||
michael@0 96 !table.ReadU16(&rec.language_id) ||
michael@0 97 !table.ReadU16(&rec.name_id) ||
michael@0 98 !table.ReadU16(&name_length) ||
michael@0 99 !table.ReadU16(&name_offset)) {
michael@0 100 return OTS_FAILURE_MSG("Failed to read name entry %d", i);
michael@0 101 }
michael@0 102 // check platform & encoding, discard names with unknown values
michael@0 103 switch (rec.platform_id) {
michael@0 104 case 0: // Unicode
michael@0 105 if (rec.encoding_id > 6) {
michael@0 106 continue;
michael@0 107 }
michael@0 108 break;
michael@0 109 case 1: // Macintosh
michael@0 110 if (rec.encoding_id > 32) {
michael@0 111 continue;
michael@0 112 }
michael@0 113 break;
michael@0 114 case 2: // ISO
michael@0 115 if (rec.encoding_id > 2) {
michael@0 116 continue;
michael@0 117 }
michael@0 118 break;
michael@0 119 case 3: // Windows: IDs 7 to 9 are "reserved"
michael@0 120 if (rec.encoding_id > 6 && rec.encoding_id != 10) {
michael@0 121 continue;
michael@0 122 }
michael@0 123 break;
michael@0 124 case 4: // Custom (OTF Windows NT compatibility)
michael@0 125 if (rec.encoding_id > 255) {
michael@0 126 continue;
michael@0 127 }
michael@0 128 break;
michael@0 129 default: // unknown platform
michael@0 130 continue;
michael@0 131 }
michael@0 132
michael@0 133 const unsigned name_end = static_cast<unsigned>(string_offset) +
michael@0 134 name_offset + name_length;
michael@0 135 if (name_end > length) {
michael@0 136 continue;
michael@0 137 }
michael@0 138 rec.text.resize(name_length);
michael@0 139 rec.text.assign(string_base + name_offset, name_length);
michael@0 140
michael@0 141 if (rec.name_id == 6) {
michael@0 142 // PostScript name: check that it is valid, if not then discard it
michael@0 143 if (rec.platform_id == 1) {
michael@0 144 if (file->cff && !file->cff->name.empty()) {
michael@0 145 rec.text = file->cff->name;
michael@0 146 } else if (!CheckPsNameAscii(rec.text)) {
michael@0 147 continue;
michael@0 148 }
michael@0 149 } else if (rec.platform_id == 0 || rec.platform_id == 3) {
michael@0 150 if (file->cff && !file->cff->name.empty()) {
michael@0 151 AssignToUtf16BeFromAscii(&rec.text, file->cff->name);
michael@0 152 } else if (!CheckPsNameUtf16Be(rec.text)) {
michael@0 153 continue;
michael@0 154 }
michael@0 155 }
michael@0 156 }
michael@0 157
michael@0 158 if ((i > 0) && !(prev_record < rec)) {
michael@0 159 OTS_WARNING("name records are not sorted.");
michael@0 160 sort_required = true;
michael@0 161 }
michael@0 162
michael@0 163 name->names.push_back(rec);
michael@0 164 prev_record = rec;
michael@0 165 }
michael@0 166
michael@0 167 if (format == 1) {
michael@0 168 // extended name table format with language tags
michael@0 169 uint16_t lang_tag_count;
michael@0 170 if (!table.ReadU16(&lang_tag_count)) {
michael@0 171 return OTS_FAILURE_MSG("Failed to read language tag count");
michael@0 172 }
michael@0 173 for (unsigned i = 0; i < lang_tag_count; ++i) {
michael@0 174 uint16_t tag_length = 0;
michael@0 175 uint16_t tag_offset = 0;
michael@0 176 if (!table.ReadU16(&tag_length) || !table.ReadU16(&tag_offset)) {
michael@0 177 return OTS_FAILURE_MSG("Faile to read tag length or offset");
michael@0 178 }
michael@0 179 const unsigned tag_end = static_cast<unsigned>(string_offset) +
michael@0 180 tag_offset + tag_length;
michael@0 181 if (tag_end > length) {
michael@0 182 return OTS_FAILURE_MSG("bad end of tag %d > %ld for name entry %d", tag_end, length, i);
michael@0 183 }
michael@0 184 std::string tag(string_base + tag_offset, tag_length);
michael@0 185 name->lang_tags.push_back(tag);
michael@0 186 }
michael@0 187 }
michael@0 188
michael@0 189 if (table.offset() > string_offset) {
michael@0 190 // the string storage apparently overlapped the name/tag records;
michael@0 191 // consider this font to be badly broken
michael@0 192 return OTS_FAILURE_MSG("Bad table offset %ld > %d", table.offset(), string_offset);
michael@0 193 }
michael@0 194
michael@0 195 // check existence of required name strings (synthesize if necessary)
michael@0 196 // [0 - copyright - skip]
michael@0 197 // 1 - family
michael@0 198 // 2 - subfamily
michael@0 199 // [3 - unique ID - skip]
michael@0 200 // 4 - full name
michael@0 201 // 5 - version
michael@0 202 // 6 - postscript name
michael@0 203 static const unsigned kStdNameCount = 7;
michael@0 204 static const char* kStdNames[kStdNameCount] = {
michael@0 205 NULL,
michael@0 206 "OTS derived font",
michael@0 207 "Unspecified",
michael@0 208 NULL,
michael@0 209 "OTS derived font",
michael@0 210 "1.000",
michael@0 211 "OTS-derived-font"
michael@0 212 };
michael@0 213 // The spec says that "In CFF OpenType fonts, these two name strings, when
michael@0 214 // translated to ASCII, must also be identical to the font name as stored in
michael@0 215 // the CFF's Name INDEX." And actually, Mac OS X's font parser requires that.
michael@0 216 if (file->cff && !file->cff->name.empty()) {
michael@0 217 kStdNames[6] = file->cff->name.c_str();
michael@0 218 }
michael@0 219
michael@0 220 // scan the names to check whether the required "standard" ones are present;
michael@0 221 // if not, we'll add our fixed versions here
michael@0 222 bool mac_name[kStdNameCount] = { 0 };
michael@0 223 bool win_name[kStdNameCount] = { 0 };
michael@0 224 for (std::vector<NameRecord>::iterator name_iter = name->names.begin();
michael@0 225 name_iter != name->names.end(); name_iter++) {
michael@0 226 const uint16_t id = name_iter->name_id;
michael@0 227 if (id >= kStdNameCount || kStdNames[id] == NULL) {
michael@0 228 continue;
michael@0 229 }
michael@0 230 if (name_iter->platform_id == 1) {
michael@0 231 mac_name[id] = true;
michael@0 232 continue;
michael@0 233 }
michael@0 234 if (name_iter->platform_id == 3) {
michael@0 235 win_name[id] = true;
michael@0 236 continue;
michael@0 237 }
michael@0 238 }
michael@0 239
michael@0 240 for (unsigned i = 0; i < kStdNameCount; ++i) {
michael@0 241 if (kStdNames[i] == NULL) {
michael@0 242 continue;
michael@0 243 }
michael@0 244 if (!mac_name[i]) {
michael@0 245 NameRecord rec(1 /* platform_id */, 0 /* encoding_id */,
michael@0 246 0 /* language_id */ , i /* name_id */);
michael@0 247 rec.text.assign(kStdNames[i]);
michael@0 248 name->names.push_back(rec);
michael@0 249 sort_required = true;
michael@0 250 }
michael@0 251 if (!win_name[i]) {
michael@0 252 NameRecord rec(3 /* platform_id */, 1 /* encoding_id */,
michael@0 253 1033 /* language_id */ , i /* name_id */);
michael@0 254 AssignToUtf16BeFromAscii(&rec.text, std::string(kStdNames[i]));
michael@0 255 name->names.push_back(rec);
michael@0 256 sort_required = true;
michael@0 257 }
michael@0 258 }
michael@0 259
michael@0 260 if (sort_required) {
michael@0 261 std::sort(name->names.begin(), name->names.end());
michael@0 262 }
michael@0 263
michael@0 264 return true;
michael@0 265 }
michael@0 266
michael@0 267 bool ots_name_should_serialise(OpenTypeFile* file) {
michael@0 268 return file->name != NULL;
michael@0 269 }
michael@0 270
michael@0 271 bool ots_name_serialise(OTSStream* out, OpenTypeFile* file) {
michael@0 272 const OpenTypeNAME* name = file->name;
michael@0 273
michael@0 274 uint16_t name_count = name->names.size();
michael@0 275 uint16_t lang_tag_count = name->lang_tags.size();
michael@0 276 uint16_t format = 0;
michael@0 277 size_t string_offset = 6 + name_count * 12;
michael@0 278
michael@0 279 if (name->lang_tags.size() > 0) {
michael@0 280 // lang tags require a format-1 name table
michael@0 281 format = 1;
michael@0 282 string_offset += 2 + lang_tag_count * 4;
michael@0 283 }
michael@0 284 if (string_offset > 0xffff) {
michael@0 285 return OTS_FAILURE_MSG("Bad string offset %ld", string_offset);
michael@0 286 }
michael@0 287 if (!out->WriteU16(format) ||
michael@0 288 !out->WriteU16(name_count) ||
michael@0 289 !out->WriteU16(string_offset)) {
michael@0 290 return OTS_FAILURE_MSG("Failed to write name header");
michael@0 291 }
michael@0 292
michael@0 293 std::string string_data;
michael@0 294 for (std::vector<NameRecord>::const_iterator name_iter = name->names.begin();
michael@0 295 name_iter != name->names.end(); name_iter++) {
michael@0 296 const NameRecord& rec = *name_iter;
michael@0 297 if (!out->WriteU16(rec.platform_id) ||
michael@0 298 !out->WriteU16(rec.encoding_id) ||
michael@0 299 !out->WriteU16(rec.language_id) ||
michael@0 300 !out->WriteU16(rec.name_id) ||
michael@0 301 !out->WriteU16(rec.text.size()) ||
michael@0 302 !out->WriteU16(string_data.size()) ) {
michael@0 303 return OTS_FAILURE_MSG("Faile to write name entry");
michael@0 304 }
michael@0 305 string_data.append(rec.text);
michael@0 306 }
michael@0 307
michael@0 308 if (format == 1) {
michael@0 309 if (!out->WriteU16(lang_tag_count)) {
michael@0 310 return OTS_FAILURE_MSG("Faile to write language tag count");
michael@0 311 }
michael@0 312 for (std::vector<std::string>::const_iterator tag_iter =
michael@0 313 name->lang_tags.begin();
michael@0 314 tag_iter != name->lang_tags.end(); tag_iter++) {
michael@0 315 if (!out->WriteU16(tag_iter->size()) ||
michael@0 316 !out->WriteU16(string_data.size())) {
michael@0 317 return OTS_FAILURE_MSG("Failed to write string");
michael@0 318 }
michael@0 319 string_data.append(*tag_iter);
michael@0 320 }
michael@0 321 }
michael@0 322
michael@0 323 if (!out->Write(string_data.data(), string_data.size())) {
michael@0 324 return OTS_FAILURE_MSG("Faile to write string data");
michael@0 325 }
michael@0 326
michael@0 327 return true;
michael@0 328 }
michael@0 329
michael@0 330 void ots_name_free(OpenTypeFile* file) {
michael@0 331 delete file->name;
michael@0 332 }
michael@0 333
michael@0 334 } // namespace

mercurial