1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/gfx/ots/src/ots.cc Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,877 @@ 1.4 +// Copyright (c) 2009 The Chromium Authors. All rights reserved. 1.5 +// Use of this source code is governed by a BSD-style license that can be 1.6 +// found in the LICENSE file. 1.7 + 1.8 +#include "ots.h" 1.9 + 1.10 +#include <sys/types.h> 1.11 +#include <zlib.h> 1.12 + 1.13 +#include <algorithm> 1.14 +#include <cstdlib> 1.15 +#include <cstring> 1.16 +#include <limits> 1.17 +#include <map> 1.18 +#include <vector> 1.19 + 1.20 +#ifdef MOZ_OTS_WOFF2 1.21 +#include "woff2.h" 1.22 +#endif 1.23 + 1.24 +// The OpenType Font File 1.25 +// http://www.microsoft.com/typography/otspec/cmap.htm 1.26 + 1.27 +namespace { 1.28 + 1.29 +bool g_debug_output = true; 1.30 +#ifdef MOZ_OTS_WOFF2 1.31 +bool g_enable_woff2 = false; 1.32 +#endif 1.33 + 1.34 +ots::MessageFunc g_message_func = NULL; 1.35 +void *g_message_user_data = NULL; 1.36 + 1.37 +ots::TableActionFunc g_table_action_func = NULL; 1.38 +void *g_table_action_user_data = NULL; 1.39 + 1.40 +// Generate a message with or without a table tag, when 'header' is the OpenTypeFile pointer 1.41 +#define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_FAILURE_MSG_TAG_(header, msg_, tag_) 1.42 +#define OTS_FAILURE_MSG_HDR(msg_) OTS_FAILURE_MSG_(header, msg_) 1.43 + 1.44 + 1.45 +struct OpenTypeTable { 1.46 + uint32_t tag; 1.47 + uint32_t chksum; 1.48 + uint32_t offset; 1.49 + uint32_t length; 1.50 + uint32_t uncompressed_length; 1.51 +}; 1.52 + 1.53 +bool CheckTag(uint32_t tag_value) { 1.54 + for (unsigned i = 0; i < 4; ++i) { 1.55 + const uint32_t check = tag_value & 0xff; 1.56 + if (check < 32 || check > 126) { 1.57 + return false; // non-ASCII character found. 1.58 + } 1.59 + tag_value >>= 8; 1.60 + } 1.61 + return true; 1.62 +} 1.63 + 1.64 +uint32_t Tag(const char *tag_str) { 1.65 + uint32_t ret; 1.66 + std::memcpy(&ret, tag_str, 4); 1.67 + return ret; 1.68 +} 1.69 + 1.70 +struct OutputTable { 1.71 + uint32_t tag; 1.72 + size_t offset; 1.73 + size_t length; 1.74 + uint32_t chksum; 1.75 + 1.76 + static bool SortByTag(const OutputTable& a, const OutputTable& b) { 1.77 + const uint32_t atag = ntohl(a.tag); 1.78 + const uint32_t btag = ntohl(b.tag); 1.79 + return atag < btag; 1.80 + } 1.81 +}; 1.82 + 1.83 +struct Arena { 1.84 + public: 1.85 + ~Arena() { 1.86 + for (std::vector<uint8_t*>::iterator 1.87 + i = hunks_.begin(); i != hunks_.end(); ++i) { 1.88 + delete[] *i; 1.89 + } 1.90 + } 1.91 + 1.92 + uint8_t* Allocate(size_t length) { 1.93 + uint8_t* p = new uint8_t[length]; 1.94 + hunks_.push_back(p); 1.95 + return p; 1.96 + } 1.97 + 1.98 + private: 1.99 + std::vector<uint8_t*> hunks_; 1.100 +}; 1.101 + 1.102 +const struct { 1.103 + const char* tag; 1.104 + bool (*parse)(ots::OpenTypeFile *otf, const uint8_t *data, size_t length); 1.105 + bool (*serialise)(ots::OTSStream *out, ots::OpenTypeFile *file); 1.106 + bool (*should_serialise)(ots::OpenTypeFile *file); 1.107 + void (*free)(ots::OpenTypeFile *file); 1.108 + bool required; 1.109 +} table_parsers[] = { 1.110 + { "maxp", ots::ots_maxp_parse, ots::ots_maxp_serialise, 1.111 + ots::ots_maxp_should_serialise, ots::ots_maxp_free, true }, 1.112 + { "head", ots::ots_head_parse, ots::ots_head_serialise, 1.113 + ots::ots_head_should_serialise, ots::ots_head_free, true }, 1.114 + { "OS/2", ots::ots_os2_parse, ots::ots_os2_serialise, 1.115 + ots::ots_os2_should_serialise, ots::ots_os2_free, true }, 1.116 + { "cmap", ots::ots_cmap_parse, ots::ots_cmap_serialise, 1.117 + ots::ots_cmap_should_serialise, ots::ots_cmap_free, true }, 1.118 + { "hhea", ots::ots_hhea_parse, ots::ots_hhea_serialise, 1.119 + ots::ots_hhea_should_serialise, ots::ots_hhea_free, true }, 1.120 + { "hmtx", ots::ots_hmtx_parse, ots::ots_hmtx_serialise, 1.121 + ots::ots_hmtx_should_serialise, ots::ots_hmtx_free, true }, 1.122 + { "name", ots::ots_name_parse, ots::ots_name_serialise, 1.123 + ots::ots_name_should_serialise, ots::ots_name_free, true }, 1.124 + { "post", ots::ots_post_parse, ots::ots_post_serialise, 1.125 + ots::ots_post_should_serialise, ots::ots_post_free, true }, 1.126 + { "loca", ots::ots_loca_parse, ots::ots_loca_serialise, 1.127 + ots::ots_loca_should_serialise, ots::ots_loca_free, false }, 1.128 + { "glyf", ots::ots_glyf_parse, ots::ots_glyf_serialise, 1.129 + ots::ots_glyf_should_serialise, ots::ots_glyf_free, false }, 1.130 + { "CFF ", ots::ots_cff_parse, ots::ots_cff_serialise, 1.131 + ots::ots_cff_should_serialise, ots::ots_cff_free, false }, 1.132 + { "VDMX", ots::ots_vdmx_parse, ots::ots_vdmx_serialise, 1.133 + ots::ots_vdmx_should_serialise, ots::ots_vdmx_free, false }, 1.134 + { "hdmx", ots::ots_hdmx_parse, ots::ots_hdmx_serialise, 1.135 + ots::ots_hdmx_should_serialise, ots::ots_hdmx_free, false }, 1.136 + { "gasp", ots::ots_gasp_parse, ots::ots_gasp_serialise, 1.137 + ots::ots_gasp_should_serialise, ots::ots_gasp_free, false }, 1.138 + { "cvt ", ots::ots_cvt_parse, ots::ots_cvt_serialise, 1.139 + ots::ots_cvt_should_serialise, ots::ots_cvt_free, false }, 1.140 + { "fpgm", ots::ots_fpgm_parse, ots::ots_fpgm_serialise, 1.141 + ots::ots_fpgm_should_serialise, ots::ots_fpgm_free, false }, 1.142 + { "prep", ots::ots_prep_parse, ots::ots_prep_serialise, 1.143 + ots::ots_prep_should_serialise, ots::ots_prep_free, false }, 1.144 + { "LTSH", ots::ots_ltsh_parse, ots::ots_ltsh_serialise, 1.145 + ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, false }, 1.146 + { "VORG", ots::ots_vorg_parse, ots::ots_vorg_serialise, 1.147 + ots::ots_vorg_should_serialise, ots::ots_vorg_free, false }, 1.148 + { "kern", ots::ots_kern_parse, ots::ots_kern_serialise, 1.149 + ots::ots_kern_should_serialise, ots::ots_kern_free, false }, 1.150 + // We need to parse GDEF table in advance of parsing GSUB/GPOS tables 1.151 + // because they could refer GDEF table. 1.152 + { "GDEF", ots::ots_gdef_parse, ots::ots_gdef_serialise, 1.153 + ots::ots_gdef_should_serialise, ots::ots_gdef_free, false }, 1.154 + { "GPOS", ots::ots_gpos_parse, ots::ots_gpos_serialise, 1.155 + ots::ots_gpos_should_serialise, ots::ots_gpos_free, false }, 1.156 + { "GSUB", ots::ots_gsub_parse, ots::ots_gsub_serialise, 1.157 + ots::ots_gsub_should_serialise, ots::ots_gsub_free, false }, 1.158 + { "vhea", ots::ots_vhea_parse, ots::ots_vhea_serialise, 1.159 + ots::ots_vhea_should_serialise, ots::ots_vhea_free, false }, 1.160 + { "vmtx", ots::ots_vmtx_parse, ots::ots_vmtx_serialise, 1.161 + ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false }, 1.162 + { "MATH", ots::ots_math_parse, ots::ots_math_serialise, 1.163 + ots::ots_math_should_serialise, ots::ots_math_free, false }, 1.164 + // TODO(bashi): Support mort, base, and jstf tables. 1.165 + { 0, NULL, NULL, NULL, NULL, false }, 1.166 +}; 1.167 + 1.168 +bool ProcessGeneric(ots::OpenTypeFile *header, 1.169 + uint32_t signature, 1.170 + ots::OTSStream *output, 1.171 + const uint8_t *data, size_t length, 1.172 + const std::vector<OpenTypeTable>& tables, 1.173 + ots::Buffer& file); 1.174 + 1.175 +bool ProcessTTF(ots::OpenTypeFile *header, 1.176 + ots::OTSStream *output, const uint8_t *data, size_t length) { 1.177 + ots::Buffer file(data, length); 1.178 + 1.179 + // we disallow all files > 1GB in size for sanity. 1.180 + if (length > 1024 * 1024 * 1024) { 1.181 + return OTS_FAILURE_MSG_HDR("file exceeds 1GB"); 1.182 + } 1.183 + 1.184 + if (!file.ReadTag(&header->version)) { 1.185 + return OTS_FAILURE_MSG_HDR("error reading version tag"); 1.186 + } 1.187 + if (!ots::IsValidVersionTag(header->version)) { 1.188 + return OTS_FAILURE_MSG_HDR("invalid version tag"); 1.189 + } 1.190 + 1.191 + if (!file.ReadU16(&header->num_tables) || 1.192 + !file.ReadU16(&header->search_range) || 1.193 + !file.ReadU16(&header->entry_selector) || 1.194 + !file.ReadU16(&header->range_shift)) { 1.195 + return OTS_FAILURE_MSG_HDR("error reading table directory search header"); 1.196 + } 1.197 + 1.198 + // search_range is (Maximum power of 2 <= numTables) x 16. Thus, to avoid 1.199 + // overflow num_tables is, at most, 2^16 / 16 = 2^12 1.200 + if (header->num_tables >= 4096 || header->num_tables < 1) { 1.201 + return OTS_FAILURE_MSG_HDR("excessive (or zero) number of tables"); 1.202 + } 1.203 + 1.204 + unsigned max_pow2 = 0; 1.205 + while (1u << (max_pow2 + 1) <= header->num_tables) { 1.206 + max_pow2++; 1.207 + } 1.208 + const uint16_t expected_search_range = (1u << max_pow2) << 4; 1.209 + 1.210 + // Don't call ots_failure() here since ~25% of fonts (250+ fonts) in 1.211 + // http://www.princexml.com/fonts/ have unexpected search_range value. 1.212 + if (header->search_range != expected_search_range) { 1.213 + OTS_WARNING("bad search range"); 1.214 + header->search_range = expected_search_range; // Fix the value. 1.215 + } 1.216 + 1.217 + // entry_selector is Log2(maximum power of 2 <= numTables) 1.218 + if (header->entry_selector != max_pow2) { 1.219 + return OTS_FAILURE_MSG_HDR("incorrect entrySelector for table directory"); 1.220 + } 1.221 + 1.222 + // range_shift is NumTables x 16-searchRange. We know that 16*num_tables 1.223 + // doesn't over flow because we range checked it above. Also, we know that 1.224 + // it's > header->search_range by construction of search_range. 1.225 + const uint32_t expected_range_shift 1.226 + = 16 * header->num_tables - header->search_range; 1.227 + if (header->range_shift != expected_range_shift) { 1.228 + OTS_WARNING("bad range shift"); 1.229 + header->range_shift = expected_range_shift; // the same as above. 1.230 + } 1.231 + 1.232 + // Next up is the list of tables. 1.233 + std::vector<OpenTypeTable> tables; 1.234 + 1.235 + for (unsigned i = 0; i < header->num_tables; ++i) { 1.236 + OpenTypeTable table; 1.237 + if (!file.ReadTag(&table.tag) || 1.238 + !file.ReadU32(&table.chksum) || 1.239 + !file.ReadU32(&table.offset) || 1.240 + !file.ReadU32(&table.length)) { 1.241 + return OTS_FAILURE_MSG_HDR("error reading table directory"); 1.242 + } 1.243 + 1.244 + table.uncompressed_length = table.length; 1.245 + tables.push_back(table); 1.246 + } 1.247 + 1.248 + return ProcessGeneric(header, header->version, output, data, length, 1.249 + tables, file); 1.250 +} 1.251 + 1.252 +bool ProcessWOFF(ots::OpenTypeFile *header, 1.253 + ots::OTSStream *output, const uint8_t *data, size_t length) { 1.254 + ots::Buffer file(data, length); 1.255 + 1.256 + // we disallow all files > 1GB in size for sanity. 1.257 + if (length > 1024 * 1024 * 1024) { 1.258 + return OTS_FAILURE_MSG_HDR("file exceeds 1GB"); 1.259 + } 1.260 + 1.261 + uint32_t woff_tag; 1.262 + if (!file.ReadTag(&woff_tag)) { 1.263 + return OTS_FAILURE_MSG_HDR("error reading WOFF marker"); 1.264 + } 1.265 + 1.266 + if (woff_tag != Tag("wOFF")) { 1.267 + return OTS_FAILURE_MSG_HDR("invalid WOFF marker"); 1.268 + } 1.269 + 1.270 + if (!file.ReadTag(&header->version)) { 1.271 + return OTS_FAILURE_MSG_HDR("error reading version tag"); 1.272 + } 1.273 + if (!ots::IsValidVersionTag(header->version)) { 1.274 + return OTS_FAILURE_MSG_HDR("invalid version tag"); 1.275 + } 1.276 + 1.277 + header->search_range = 0; 1.278 + header->entry_selector = 0; 1.279 + header->range_shift = 0; 1.280 + 1.281 + uint32_t reported_length; 1.282 + if (!file.ReadU32(&reported_length) || length != reported_length) { 1.283 + return OTS_FAILURE_MSG_HDR("incorrect file size in WOFF header"); 1.284 + } 1.285 + 1.286 + if (!file.ReadU16(&header->num_tables) || !header->num_tables) { 1.287 + return OTS_FAILURE_MSG_HDR("error reading number of tables"); 1.288 + } 1.289 + 1.290 + uint16_t reserved_value; 1.291 + if (!file.ReadU16(&reserved_value) || reserved_value) { 1.292 + return OTS_FAILURE_MSG_HDR("error in reserved field of WOFF header"); 1.293 + } 1.294 + 1.295 + uint32_t reported_total_sfnt_size; 1.296 + if (!file.ReadU32(&reported_total_sfnt_size)) { 1.297 + return OTS_FAILURE_MSG_HDR("error reading total sfnt size"); 1.298 + } 1.299 + 1.300 + // We don't care about these fields of the header: 1.301 + // uint16_t major_version, minor_version 1.302 + if (!file.Skip(2 * 2)) { 1.303 + return OTS_FAILURE_MSG_HDR("error skipping WOFF header fields"); 1.304 + } 1.305 + 1.306 + // Checks metadata block size. 1.307 + uint32_t meta_offset; 1.308 + uint32_t meta_length; 1.309 + uint32_t meta_length_orig; 1.310 + if (!file.ReadU32(&meta_offset) || 1.311 + !file.ReadU32(&meta_length) || 1.312 + !file.ReadU32(&meta_length_orig)) { 1.313 + return OTS_FAILURE_MSG_HDR("error reading WOFF header fields"); 1.314 + } 1.315 + if (meta_offset) { 1.316 + if (meta_offset >= length || length - meta_offset < meta_length) { 1.317 + return OTS_FAILURE_MSG_HDR("invalid metadata block location/size"); 1.318 + } 1.319 + } 1.320 + 1.321 + // Checks private data block size. 1.322 + uint32_t priv_offset; 1.323 + uint32_t priv_length; 1.324 + if (!file.ReadU32(&priv_offset) || 1.325 + !file.ReadU32(&priv_length)) { 1.326 + return OTS_FAILURE_MSG_HDR("error reading WOFF header fields"); 1.327 + } 1.328 + if (priv_offset) { 1.329 + if (priv_offset >= length || length - priv_offset < priv_length) { 1.330 + return OTS_FAILURE_MSG_HDR("invalid private block location/size"); 1.331 + } 1.332 + } 1.333 + 1.334 + // Next up is the list of tables. 1.335 + std::vector<OpenTypeTable> tables; 1.336 + 1.337 + uint32_t first_index = 0; 1.338 + uint32_t last_index = 0; 1.339 + // Size of sfnt header plus size of table records. 1.340 + uint64_t total_sfnt_size = 12 + 16 * header->num_tables; 1.341 + for (unsigned i = 0; i < header->num_tables; ++i) { 1.342 + OpenTypeTable table; 1.343 + if (!file.ReadTag(&table.tag) || 1.344 + !file.ReadU32(&table.offset) || 1.345 + !file.ReadU32(&table.length) || 1.346 + !file.ReadU32(&table.uncompressed_length) || 1.347 + !file.ReadU32(&table.chksum)) { 1.348 + return OTS_FAILURE_MSG_HDR("error reading table directory"); 1.349 + } 1.350 + 1.351 + total_sfnt_size += ots::Round4(table.uncompressed_length); 1.352 + if (total_sfnt_size > std::numeric_limits<uint32_t>::max()) { 1.353 + return OTS_FAILURE_MSG_HDR("sfnt size overflow"); 1.354 + } 1.355 + tables.push_back(table); 1.356 + if (i == 0 || tables[first_index].offset > table.offset) 1.357 + first_index = i; 1.358 + if (i == 0 || tables[last_index].offset < table.offset) 1.359 + last_index = i; 1.360 + } 1.361 + 1.362 + if (reported_total_sfnt_size != total_sfnt_size) { 1.363 + return OTS_FAILURE_MSG_HDR("uncompressed sfnt size mismatch"); 1.364 + } 1.365 + 1.366 + // Table data must follow immediately after the header. 1.367 + if (tables[first_index].offset != ots::Round4(file.offset())) { 1.368 + return OTS_FAILURE_MSG_HDR("junk before tables in WOFF file"); 1.369 + } 1.370 + 1.371 + if (tables[last_index].offset >= length || 1.372 + length - tables[last_index].offset < tables[last_index].length) { 1.373 + return OTS_FAILURE_MSG_HDR("invalid table location/size"); 1.374 + } 1.375 + // Blocks must follow immediately after the previous block. 1.376 + // (Except for padding with a maximum of three null bytes) 1.377 + uint64_t block_end = ots::Round4( 1.378 + static_cast<uint64_t>(tables[last_index].offset) + 1.379 + static_cast<uint64_t>(tables[last_index].length)); 1.380 + if (block_end > std::numeric_limits<uint32_t>::max()) { 1.381 + return OTS_FAILURE_MSG_HDR("invalid table location/size"); 1.382 + } 1.383 + if (meta_offset) { 1.384 + if (block_end != meta_offset) { 1.385 + return OTS_FAILURE_MSG_HDR("invalid metadata block location"); 1.386 + } 1.387 + block_end = ots::Round4(static_cast<uint64_t>(meta_offset) + 1.388 + static_cast<uint64_t>(meta_length)); 1.389 + if (block_end > std::numeric_limits<uint32_t>::max()) { 1.390 + return OTS_FAILURE_MSG_HDR("invalid metadata block size"); 1.391 + } 1.392 + } 1.393 + if (priv_offset) { 1.394 + if (block_end != priv_offset) { 1.395 + return OTS_FAILURE_MSG_HDR("invalid private block location"); 1.396 + } 1.397 + block_end = ots::Round4(static_cast<uint64_t>(priv_offset) + 1.398 + static_cast<uint64_t>(priv_length)); 1.399 + if (block_end > std::numeric_limits<uint32_t>::max()) { 1.400 + return OTS_FAILURE_MSG_HDR("invalid private block size"); 1.401 + } 1.402 + } 1.403 + if (block_end != ots::Round4(length)) { 1.404 + return OTS_FAILURE_MSG_HDR("file length mismatch (trailing junk?)"); 1.405 + } 1.406 + 1.407 + return ProcessGeneric(header, woff_tag, output, data, length, tables, file); 1.408 +} 1.409 + 1.410 +#ifdef MOZ_OTS_WOFF2 1.411 +bool ProcessWOFF2(ots::OpenTypeFile *header, 1.412 + ots::OTSStream *output, const uint8_t *data, size_t length) { 1.413 + size_t decompressed_size = ots::ComputeWOFF2FinalSize(data, length); 1.414 + if (decompressed_size == 0) { 1.415 + return OTS_FAILURE(); 1.416 + } 1.417 + // decompressed font must be <= 30MB 1.418 + if (decompressed_size > 30 * 1024 * 1024) { 1.419 + return OTS_FAILURE(); 1.420 + } 1.421 + 1.422 + std::vector<uint8_t> decompressed_buffer(decompressed_size); 1.423 + if (!ots::ConvertWOFF2ToTTF(&decompressed_buffer[0], decompressed_size, 1.424 + data, length)) { 1.425 + return OTS_FAILURE(); 1.426 + } 1.427 + return ProcessTTF(header, output, &decompressed_buffer[0], decompressed_size); 1.428 +} 1.429 +#endif 1.430 + 1.431 +ots::TableAction GetTableAction(uint32_t tag) { 1.432 + ots::TableAction action = ots::TABLE_ACTION_DEFAULT; 1.433 + 1.434 + if (g_table_action_func != NULL) { 1.435 + action = g_table_action_func(htonl(tag), g_table_action_user_data); 1.436 + } 1.437 + 1.438 + if (action == ots::TABLE_ACTION_DEFAULT) { 1.439 + action = ots::TABLE_ACTION_DROP; 1.440 + 1.441 + for (unsigned i = 0; ; ++i) { 1.442 + if (table_parsers[i].parse == NULL) break; 1.443 + 1.444 + if (Tag(table_parsers[i].tag) == tag) { 1.445 + action = ots::TABLE_ACTION_SANITIZE; 1.446 + break; 1.447 + } 1.448 + } 1.449 + } 1.450 + 1.451 + assert(action != ots::TABLE_ACTION_DEFAULT); // Should never return this. 1.452 + return action; 1.453 +} 1.454 + 1.455 +bool GetTableData(const uint8_t *data, 1.456 + const OpenTypeTable table, 1.457 + Arena *arena, 1.458 + size_t *table_length, 1.459 + const uint8_t **table_data) { 1.460 + if (table.uncompressed_length != table.length) { 1.461 + // Compressed table. Need to uncompress into memory first. 1.462 + *table_length = table.uncompressed_length; 1.463 + *table_data = (*arena).Allocate(*table_length); 1.464 + uLongf dest_len = *table_length; 1.465 + int r = uncompress((Bytef*) *table_data, &dest_len, 1.466 + data + table.offset, table.length); 1.467 + if (r != Z_OK || dest_len != *table_length) { 1.468 + return false; 1.469 + } 1.470 + } else { 1.471 + // Uncompressed table. We can process directly from memory. 1.472 + *table_data = data + table.offset; 1.473 + *table_length = table.length; 1.474 + } 1.475 + 1.476 + return true; 1.477 +} 1.478 + 1.479 +bool ProcessGeneric(ots::OpenTypeFile *header, uint32_t signature, 1.480 + ots::OTSStream *output, 1.481 + const uint8_t *data, size_t length, 1.482 + const std::vector<OpenTypeTable>& tables, 1.483 + ots::Buffer& file) { 1.484 + const size_t data_offset = file.offset(); 1.485 + 1.486 + uint32_t uncompressed_sum = 0; 1.487 + 1.488 + for (unsigned i = 0; i < header->num_tables; ++i) { 1.489 + // the tables must be sorted by tag (when taken as big-endian numbers). 1.490 + // This also remove the possibility of duplicate tables. 1.491 + if (i) { 1.492 + const uint32_t this_tag = ntohl(tables[i].tag); 1.493 + const uint32_t prev_tag = ntohl(tables[i - 1].tag); 1.494 + if (this_tag <= prev_tag) { 1.495 + return OTS_FAILURE_MSG_HDR("table directory not correctly ordered"); 1.496 + } 1.497 + } 1.498 + 1.499 + // all tag names must be built from printable ASCII characters 1.500 + if (!CheckTag(tables[i].tag)) { 1.501 + return OTS_FAILURE_MSG_TAG("invalid table tag", &tables[i].tag); 1.502 + } 1.503 + 1.504 + // tables must be 4-byte aligned 1.505 + if (tables[i].offset & 3) { 1.506 + return OTS_FAILURE_MSG_TAG("misaligned table", &tables[i].tag); 1.507 + } 1.508 + 1.509 + // and must be within the file 1.510 + if (tables[i].offset < data_offset || tables[i].offset >= length) { 1.511 + return OTS_FAILURE_MSG_TAG("invalid table offset", &tables[i].tag); 1.512 + } 1.513 + // disallow all tables with a zero length 1.514 + if (tables[i].length < 1) { 1.515 + // Note: malayalam.ttf has zero length CVT table... 1.516 + return OTS_FAILURE_MSG_TAG("zero-length table", &tables[i].tag); 1.517 + } 1.518 + // disallow all tables with a length > 1GB 1.519 + if (tables[i].length > 1024 * 1024 * 1024) { 1.520 + return OTS_FAILURE_MSG_TAG("table length exceeds 1GB", &tables[i].tag); 1.521 + } 1.522 + // disallow tables where the uncompressed size is < the compressed size. 1.523 + if (tables[i].uncompressed_length < tables[i].length) { 1.524 + return OTS_FAILURE_MSG_TAG("invalid compressed table", &tables[i].tag); 1.525 + } 1.526 + if (tables[i].uncompressed_length > tables[i].length) { 1.527 + // We'll probably be decompressing this table. 1.528 + 1.529 + // disallow all tables which uncompress to > 30 MB 1.530 + if (tables[i].uncompressed_length > 30 * 1024 * 1024) { 1.531 + return OTS_FAILURE_MSG_TAG("uncompressed length exceeds 30MB", &tables[i].tag); 1.532 + } 1.533 + if (uncompressed_sum + tables[i].uncompressed_length < uncompressed_sum) { 1.534 + return OTS_FAILURE_MSG_TAG("overflow of uncompressed sum", &tables[i].tag); 1.535 + } 1.536 + 1.537 + uncompressed_sum += tables[i].uncompressed_length; 1.538 + } 1.539 + // since we required that the file be < 1GB in length, and that the table 1.540 + // length is < 1GB, the following addtion doesn't overflow 1.541 + uint32_t end_byte = tables[i].offset + tables[i].length; 1.542 + // Tables in the WOFF file must be aligned 4-byte boundary. 1.543 + if (signature == Tag("wOFF")) { 1.544 + end_byte = ots::Round4(end_byte); 1.545 + } 1.546 + if (!end_byte || end_byte > length) { 1.547 + return OTS_FAILURE_MSG_TAG("table overruns end of file", &tables[i].tag); 1.548 + } 1.549 + } 1.550 + 1.551 + // All decompressed tables uncompressed must be <= 30MB. 1.552 + if (uncompressed_sum > 30 * 1024 * 1024) { 1.553 + return OTS_FAILURE_MSG_HDR("uncompressed sum exceeds 30MB"); 1.554 + } 1.555 + 1.556 + std::map<uint32_t, OpenTypeTable> table_map; 1.557 + for (unsigned i = 0; i < header->num_tables; ++i) { 1.558 + table_map[tables[i].tag] = tables[i]; 1.559 + } 1.560 + 1.561 + // check that the tables are not overlapping. 1.562 + std::vector<std::pair<uint32_t, uint8_t> > overlap_checker; 1.563 + for (unsigned i = 0; i < header->num_tables; ++i) { 1.564 + overlap_checker.push_back( 1.565 + std::make_pair(tables[i].offset, static_cast<uint8_t>(1) /* start */)); 1.566 + overlap_checker.push_back( 1.567 + std::make_pair(tables[i].offset + tables[i].length, 1.568 + static_cast<uint8_t>(0) /* end */)); 1.569 + } 1.570 + std::sort(overlap_checker.begin(), overlap_checker.end()); 1.571 + int overlap_count = 0; 1.572 + for (unsigned i = 0; i < overlap_checker.size(); ++i) { 1.573 + overlap_count += (overlap_checker[i].second ? 1 : -1); 1.574 + if (overlap_count > 1) { 1.575 + return OTS_FAILURE_MSG_HDR("overlapping tables"); 1.576 + } 1.577 + } 1.578 + 1.579 + Arena arena; 1.580 + 1.581 + for (unsigned i = 0; ; ++i) { 1.582 + if (table_parsers[i].parse == NULL) break; 1.583 + 1.584 + const std::map<uint32_t, OpenTypeTable>::const_iterator it 1.585 + = table_map.find(Tag(table_parsers[i].tag)); 1.586 + 1.587 + ots::TableAction action = GetTableAction(Tag(table_parsers[i].tag)); 1.588 + if (it == table_map.end()) { 1.589 + if (table_parsers[i].required && action == ots::TABLE_ACTION_SANITIZE) { 1.590 + return OTS_FAILURE_MSG_TAG("missing required table", table_parsers[i].tag); 1.591 + } 1.592 + continue; 1.593 + } 1.594 + 1.595 + const uint8_t* table_data; 1.596 + size_t table_length; 1.597 + 1.598 + if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) { 1.599 + return OTS_FAILURE_MSG_TAG("uncompress failed", table_parsers[i].tag); 1.600 + } 1.601 + 1.602 + if (action == ots::TABLE_ACTION_SANITIZE && 1.603 + !table_parsers[i].parse(header, table_data, table_length)) { 1.604 + // TODO: parsers should generate specific messages detailing the failure; 1.605 + // once those are all added, we won't need a generic failure message here 1.606 + return OTS_FAILURE_MSG_TAG("failed to parse table", table_parsers[i].tag); 1.607 + } 1.608 + } 1.609 + 1.610 + if (header->cff) { 1.611 + // font with PostScript glyph 1.612 + if (header->version != Tag("OTTO")) { 1.613 + return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data"); 1.614 + } 1.615 + if (header->glyf || header->loca) { 1.616 + // mixing outline formats is not recommended 1.617 + return OTS_FAILURE_MSG_HDR("font contains both PS and TT glyphs"); 1.618 + } 1.619 + } else { 1.620 + if (!header->glyf || !header->loca) { 1.621 + // No TrueType glyph found. 1.622 + // Note: bitmap-only fonts are not supported. 1.623 + return OTS_FAILURE_MSG_HDR("neither PS nor TT glyphs present"); 1.624 + } 1.625 + } 1.626 + 1.627 + unsigned num_output_tables = 0; 1.628 + for (unsigned i = 0; ; ++i) { 1.629 + if (table_parsers[i].parse == NULL) { 1.630 + break; 1.631 + } 1.632 + 1.633 + if (table_parsers[i].should_serialise(header)) { 1.634 + num_output_tables++; 1.635 + } 1.636 + } 1.637 + 1.638 + for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin(); 1.639 + it != table_map.end(); ++it) { 1.640 + ots::TableAction action = GetTableAction(it->first); 1.641 + if (action == ots::TABLE_ACTION_PASSTHRU) { 1.642 + num_output_tables++; 1.643 + } 1.644 + } 1.645 + 1.646 + unsigned max_pow2 = 0; 1.647 + while (1u << (max_pow2 + 1) <= num_output_tables) { 1.648 + max_pow2++; 1.649 + } 1.650 + const uint16_t output_search_range = (1u << max_pow2) << 4; 1.651 + 1.652 + // most of the errors here are highly unlikely - they'd only occur if the 1.653 + // output stream returns a failure, e.g. lack of space to write 1.654 + output->ResetChecksum(); 1.655 + if (!output->WriteTag(header->version) || 1.656 + !output->WriteU16(num_output_tables) || 1.657 + !output->WriteU16(output_search_range) || 1.658 + !output->WriteU16(max_pow2) || 1.659 + !output->WriteU16((num_output_tables << 4) - output_search_range)) { 1.660 + return OTS_FAILURE_MSG_HDR("error writing output"); 1.661 + } 1.662 + const uint32_t offset_table_chksum = output->chksum(); 1.663 + 1.664 + const size_t table_record_offset = output->Tell(); 1.665 + if (!output->Pad(16 * num_output_tables)) { 1.666 + return OTS_FAILURE_MSG_HDR("error writing output"); 1.667 + } 1.668 + 1.669 + std::vector<OutputTable> out_tables; 1.670 + 1.671 + size_t head_table_offset = 0; 1.672 + for (unsigned i = 0; ; ++i) { 1.673 + if (table_parsers[i].parse == NULL) { 1.674 + break; 1.675 + } 1.676 + 1.677 + if (!table_parsers[i].should_serialise(header)) { 1.678 + continue; 1.679 + } 1.680 + 1.681 + OutputTable out; 1.682 + uint32_t tag = Tag(table_parsers[i].tag); 1.683 + out.tag = tag; 1.684 + out.offset = output->Tell(); 1.685 + 1.686 + output->ResetChecksum(); 1.687 + if (tag == Tag("head")) { 1.688 + head_table_offset = out.offset; 1.689 + } 1.690 + if (!table_parsers[i].serialise(output, header)) { 1.691 + return OTS_FAILURE_MSG_TAG("failed to serialize table", table_parsers[i].tag); 1.692 + } 1.693 + 1.694 + const size_t end_offset = output->Tell(); 1.695 + if (end_offset <= out.offset) { 1.696 + // paranoid check. |end_offset| is supposed to be greater than the offset, 1.697 + // as long as the Tell() interface is implemented correctly. 1.698 + return OTS_FAILURE_MSG_HDR("error writing output"); 1.699 + } 1.700 + out.length = end_offset - out.offset; 1.701 + 1.702 + // align tables to four bytes 1.703 + if (!output->Pad((4 - (end_offset & 3)) % 4)) { 1.704 + return OTS_FAILURE_MSG_HDR("error writing output"); 1.705 + } 1.706 + out.chksum = output->chksum(); 1.707 + out_tables.push_back(out); 1.708 + } 1.709 + 1.710 + for (std::map<uint32_t, OpenTypeTable>::const_iterator it = table_map.begin(); 1.711 + it != table_map.end(); ++it) { 1.712 + ots::TableAction action = GetTableAction(it->first); 1.713 + if (action == ots::TABLE_ACTION_PASSTHRU) { 1.714 + OutputTable out; 1.715 + out.tag = it->second.tag; 1.716 + out.offset = output->Tell(); 1.717 + 1.718 + output->ResetChecksum(); 1.719 + if (it->second.tag == Tag("head")) { 1.720 + head_table_offset = out.offset; 1.721 + } 1.722 + 1.723 + const uint8_t* table_data; 1.724 + size_t table_length; 1.725 + 1.726 + if (!GetTableData(data, it->second, &arena, &table_length, &table_data)) { 1.727 + return OTS_FAILURE_MSG_HDR("Failed to uncompress table"); 1.728 + } 1.729 + 1.730 + if (!output->Write(table_data, table_length)) { 1.731 + return OTS_FAILURE_MSG_HDR("Failed to serialize table"); 1.732 + } 1.733 + 1.734 + const size_t end_offset = output->Tell(); 1.735 + if (end_offset <= out.offset) { 1.736 + // paranoid check. |end_offset| is supposed to be greater than the offset, 1.737 + // as long as the Tell() interface is implemented correctly. 1.738 + return OTS_FAILURE_MSG_HDR("error writing output"); 1.739 + } 1.740 + out.length = end_offset - out.offset; 1.741 + 1.742 + // align tables to four bytes 1.743 + if (!output->Pad((4 - (end_offset & 3)) % 4)) { 1.744 + return OTS_FAILURE_MSG_HDR("error writing output"); 1.745 + } 1.746 + out.chksum = output->chksum(); 1.747 + out_tables.push_back(out); 1.748 + } 1.749 + } 1.750 + 1.751 + const size_t end_of_file = output->Tell(); 1.752 + 1.753 + // Need to sort the output tables for inclusion in the file 1.754 + std::sort(out_tables.begin(), out_tables.end(), OutputTable::SortByTag); 1.755 + if (!output->Seek(table_record_offset)) { 1.756 + return OTS_FAILURE_MSG_HDR("error writing output"); 1.757 + } 1.758 + 1.759 + output->ResetChecksum(); 1.760 + uint32_t tables_chksum = 0; 1.761 + for (unsigned i = 0; i < out_tables.size(); ++i) { 1.762 + if (!output->WriteTag(out_tables[i].tag) || 1.763 + !output->WriteU32(out_tables[i].chksum) || 1.764 + !output->WriteU32(out_tables[i].offset) || 1.765 + !output->WriteU32(out_tables[i].length)) { 1.766 + return OTS_FAILURE_MSG_HDR("error writing output"); 1.767 + } 1.768 + tables_chksum += out_tables[i].chksum; 1.769 + } 1.770 + const uint32_t table_record_chksum = output->chksum(); 1.771 + 1.772 + // http://www.microsoft.com/typography/otspec/otff.htm 1.773 + const uint32_t file_chksum 1.774 + = offset_table_chksum + tables_chksum + table_record_chksum; 1.775 + const uint32_t chksum_magic = static_cast<uint32_t>(0xb1b0afba) - file_chksum; 1.776 + 1.777 + // seek into the 'head' table and write in the checksum magic value 1.778 + if (!head_table_offset) { 1.779 + return OTS_FAILURE_MSG_HDR("internal error!"); 1.780 + } 1.781 + if (!output->Seek(head_table_offset + 8)) { 1.782 + return OTS_FAILURE_MSG_HDR("error writing output"); 1.783 + } 1.784 + if (!output->WriteU32(chksum_magic)) { 1.785 + return OTS_FAILURE_MSG_HDR("error writing output"); 1.786 + } 1.787 + 1.788 + if (!output->Seek(end_of_file)) { 1.789 + return OTS_FAILURE_MSG_HDR("error writing output"); 1.790 + } 1.791 + 1.792 + return true; 1.793 +} 1.794 + 1.795 +} // namespace 1.796 + 1.797 +namespace ots { 1.798 + 1.799 +bool IsValidVersionTag(uint32_t tag) { 1.800 + return tag == Tag("\x00\x01\x00\x00") || 1.801 + // OpenType fonts with CFF data have 'OTTO' tag. 1.802 + tag == Tag("OTTO") || 1.803 + // Older Mac fonts might have 'true' or 'typ1' tag. 1.804 + tag == Tag("true") || 1.805 + tag == Tag("typ1"); 1.806 +} 1.807 + 1.808 +void DisableDebugOutput() { 1.809 + g_debug_output = false; 1.810 +} 1.811 + 1.812 +#ifdef MOZ_OTS_WOFF2 1.813 +void EnableWOFF2() { 1.814 + g_enable_woff2 = true; 1.815 +} 1.816 +#endif 1.817 + 1.818 +void SetMessageCallback(MessageFunc func, void *user_data) { 1.819 + g_message_func = func; 1.820 + g_message_user_data = user_data; 1.821 +} 1.822 + 1.823 +void SetTableActionCallback(TableActionFunc func, void *user_data) { 1.824 + g_table_action_func = func; 1.825 + g_table_action_user_data = user_data; 1.826 +} 1.827 + 1.828 +bool Process(OTSStream *output, const uint8_t *data, size_t length) { 1.829 + OpenTypeFile header; 1.830 + 1.831 + header.message_func = g_message_func; 1.832 + header.user_data = g_message_user_data; 1.833 + 1.834 + if (length < 4) { 1.835 + return OTS_FAILURE_MSG_(&header, "file less than 4 bytes"); 1.836 + } 1.837 + 1.838 + bool result; 1.839 + if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') { 1.840 + result = ProcessWOFF(&header, output, data, length); 1.841 +#ifdef MOZ_OTS_WOFF2 1.842 + } else if (g_enable_woff2 && 1.843 + data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && 1.844 + data[3] == '2') { 1.845 + result = ProcessWOFF2(&header, output, data, length); 1.846 +#endif 1.847 + } else { 1.848 + result = ProcessTTF(&header, output, data, length); 1.849 + } 1.850 + 1.851 + for (unsigned i = 0; ; ++i) { 1.852 + if (table_parsers[i].parse == NULL) break; 1.853 + table_parsers[i].free(&header); 1.854 + } 1.855 + return result; 1.856 +} 1.857 + 1.858 +#if !defined(_MSC_VER) && defined(OTS_DEBUG) 1.859 +bool Failure(const char *f, int l, const char *fn) { 1.860 + if (g_debug_output) { 1.861 + std::fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn); 1.862 + std::fflush(stderr); 1.863 + } 1.864 + return false; 1.865 +} 1.866 + 1.867 +void Warning(const char *f, int l, const char *format, ...) { 1.868 + if (g_debug_output) { 1.869 + std::fprintf(stderr, "WARNING at %s:%d: ", f, l); 1.870 + std::va_list va; 1.871 + va_start(va, format); 1.872 + std::vfprintf(stderr, format, va); 1.873 + va_end(va); 1.874 + std::fprintf(stderr, "\n"); 1.875 + std::fflush(stderr); 1.876 + } 1.877 +} 1.878 +#endif 1.879 + 1.880 +} // namespace ots