mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
bug 670901 pt 1 - add support for an error-message callback to OTS. r=jdaggett
This commit is contained in:
parent
d5ef014643
commit
948b60acc6
@ -199,6 +199,13 @@ class OTSStream {
|
|||||||
unsigned chksum_buffer_offset_;
|
unsigned chksum_buffer_offset_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef MOZ_OTS_REPORT_ERRORS
|
||||||
|
// Signature of the function to be provided by the client in order to report errors.
|
||||||
|
// The return type is a boolean so that it can be used within an expression,
|
||||||
|
// but the actual value is ignored. (Suggested convention is to always return 'false'.)
|
||||||
|
typedef bool (*MessageFunc)(void *user_data, const char *format, ...);
|
||||||
|
#endif
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
// Process a given OpenType file and write out a sanitised version
|
// Process a given OpenType file and write out a sanitised version
|
||||||
// output: a pointer to an object implementing the OTSStream interface. The
|
// output: a pointer to an object implementing the OTSStream interface. The
|
||||||
@ -209,6 +216,9 @@ class OTSStream {
|
|||||||
// preserve_graphite_tables: whether to preserve Graphite Layout tables
|
// preserve_graphite_tables: whether to preserve Graphite Layout tables
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
bool OTS_API Process(OTSStream *output, const uint8_t *input, size_t length,
|
bool OTS_API Process(OTSStream *output, const uint8_t *input, size_t length,
|
||||||
|
#ifdef MOZ_OTS_REPORT_ERRORS
|
||||||
|
MessageFunc message_func, void *user_data,
|
||||||
|
#endif
|
||||||
bool preserve_graphite_tables = false);
|
bool preserve_graphite_tables = false);
|
||||||
|
|
||||||
// Force to disable debug output even when the library is compiled with
|
// Force to disable debug output even when the library is compiled with
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
// GDEF - The Glyph Definition Table
|
// GDEF - The Glyph Definition Table
|
||||||
// http://www.microsoft.com/typography/otspec/gdef.htm
|
// http://www.microsoft.com/typography/otspec/gdef.htm
|
||||||
|
|
||||||
|
#define TABLE_NAME "GDEF"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// The maximum class value in class definition tables.
|
// The maximum class value in class definition tables.
|
||||||
@ -242,7 +244,11 @@ bool ParseMarkGlyphSetsDefTable(ots::OpenTypeFile *file, const uint8_t *data,
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#define DROP_THIS_TABLE \
|
#define DROP_THIS_TABLE \
|
||||||
do { file->gdef->data = 0; file->gdef->length = 0; } while (0)
|
do { \
|
||||||
|
file->gdef->data = 0; \
|
||||||
|
file->gdef->length = 0; \
|
||||||
|
OTS_FAILURE_MSG("OpenType layout data discarded"); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
namespace ots {
|
namespace ots {
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
// GPOS - The Glyph Positioning Table
|
// GPOS - The Glyph Positioning Table
|
||||||
// http://www.microsoft.com/typography/otspec/gpos.htm
|
// http://www.microsoft.com/typography/otspec/gpos.htm
|
||||||
|
|
||||||
|
#define TABLE_NAME "GPOS"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
enum GPOS_TYPE {
|
enum GPOS_TYPE {
|
||||||
@ -669,7 +671,11 @@ bool ParseExtensionPositioning(const ots::OpenTypeFile *file,
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#define DROP_THIS_TABLE \
|
#define DROP_THIS_TABLE \
|
||||||
do { file->gpos->data = 0; file->gpos->length = 0; } while (0)
|
do { \
|
||||||
|
file->gpos->data = 0; \
|
||||||
|
file->gpos->length = 0; \
|
||||||
|
OTS_FAILURE_MSG("OpenType layout data discarded"); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
namespace ots {
|
namespace ots {
|
||||||
|
|
||||||
|
@ -15,6 +15,8 @@
|
|||||||
// GSUB - The Glyph Substitution Table
|
// GSUB - The Glyph Substitution Table
|
||||||
// http://www.microsoft.com/typography/otspec/gsub.htm
|
// http://www.microsoft.com/typography/otspec/gsub.htm
|
||||||
|
|
||||||
|
#define TABLE_NAME "GSUB"
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// The GSUB header size
|
// The GSUB header size
|
||||||
@ -529,7 +531,11 @@ bool ParseReverseChainingContextSingleSubstitution(
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
#define DROP_THIS_TABLE \
|
#define DROP_THIS_TABLE \
|
||||||
do { file->gsub->data = 0; file->gsub->length = 0; } while (0)
|
do { \
|
||||||
|
file->gsub->data = 0; \
|
||||||
|
file->gsub->length = 0; \
|
||||||
|
OTS_FAILURE_MSG("OpenType layout data discarded"); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
namespace ots {
|
namespace ots {
|
||||||
|
|
||||||
|
@ -21,6 +21,20 @@ namespace {
|
|||||||
|
|
||||||
bool g_debug_output = true;
|
bool g_debug_output = true;
|
||||||
|
|
||||||
|
#ifdef MOZ_OTS_REPORT_ERRORS
|
||||||
|
|
||||||
|
// Generate a message with or without a table tag, when 'header' is the OpenTypeFile pointer
|
||||||
|
#define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_FAILURE_MSG_TAG_(header, msg_, tag_)
|
||||||
|
#define OTS_FAILURE_MSG_HDR(msg_) OTS_FAILURE_MSG_(header, msg_)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
#define OTS_FAILURE_MSG_TAG(msg_,tag_) OTS_FAILURE()
|
||||||
|
#define OTS_FAILURE_MSG_HDR(msg_) OTS_FAILURE()
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
struct OpenTypeTable {
|
struct OpenTypeTable {
|
||||||
uint32_t tag;
|
uint32_t tag;
|
||||||
uint32_t chksum;
|
uint32_t chksum;
|
||||||
@ -182,27 +196,27 @@ bool ProcessTTF(ots::OpenTypeFile *header,
|
|||||||
|
|
||||||
// we disallow all files > 1GB in size for sanity.
|
// we disallow all files > 1GB in size for sanity.
|
||||||
if (length > 1024 * 1024 * 1024) {
|
if (length > 1024 * 1024 * 1024) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file.ReadTag(&header->version)) {
|
if (!file.ReadTag(&header->version)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error reading version tag");
|
||||||
}
|
}
|
||||||
if (!IsValidVersionTag(header->version)) {
|
if (!IsValidVersionTag(header->version)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("invalid version tag");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file.ReadU16(&header->num_tables) ||
|
if (!file.ReadU16(&header->num_tables) ||
|
||||||
!file.ReadU16(&header->search_range) ||
|
!file.ReadU16(&header->search_range) ||
|
||||||
!file.ReadU16(&header->entry_selector) ||
|
!file.ReadU16(&header->entry_selector) ||
|
||||||
!file.ReadU16(&header->range_shift)) {
|
!file.ReadU16(&header->range_shift)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error reading table directory search header");
|
||||||
}
|
}
|
||||||
|
|
||||||
// search_range is (Maximum power of 2 <= numTables) x 16. Thus, to avoid
|
// search_range is (Maximum power of 2 <= numTables) x 16. Thus, to avoid
|
||||||
// overflow num_tables is, at most, 2^16 / 16 = 2^12
|
// overflow num_tables is, at most, 2^16 / 16 = 2^12
|
||||||
if (header->num_tables >= 4096 || header->num_tables < 1) {
|
if (header->num_tables >= 4096 || header->num_tables < 1) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("excessive (or zero) number of tables");
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned max_pow2 = 0;
|
unsigned max_pow2 = 0;
|
||||||
@ -220,7 +234,7 @@ bool ProcessTTF(ots::OpenTypeFile *header,
|
|||||||
|
|
||||||
// entry_selector is Log2(maximum power of 2 <= numTables)
|
// entry_selector is Log2(maximum power of 2 <= numTables)
|
||||||
if (header->entry_selector != max_pow2) {
|
if (header->entry_selector != max_pow2) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("incorrect entrySelector for table directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
// range_shift is NumTables x 16-searchRange. We know that 16*num_tables
|
// range_shift is NumTables x 16-searchRange. We know that 16*num_tables
|
||||||
@ -242,7 +256,7 @@ bool ProcessTTF(ots::OpenTypeFile *header,
|
|||||||
!file.ReadU32(&table.chksum) ||
|
!file.ReadU32(&table.chksum) ||
|
||||||
!file.ReadU32(&table.offset) ||
|
!file.ReadU32(&table.offset) ||
|
||||||
!file.ReadU32(&table.length)) {
|
!file.ReadU32(&table.length)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error reading table directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
table.uncompressed_length = table.length;
|
table.uncompressed_length = table.length;
|
||||||
@ -258,23 +272,23 @@ bool ProcessWOFF(ots::OpenTypeFile *header,
|
|||||||
|
|
||||||
// we disallow all files > 1GB in size for sanity.
|
// we disallow all files > 1GB in size for sanity.
|
||||||
if (length > 1024 * 1024 * 1024) {
|
if (length > 1024 * 1024 * 1024) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("file exceeds 1GB");
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t woff_tag;
|
uint32_t woff_tag;
|
||||||
if (!file.ReadTag(&woff_tag)) {
|
if (!file.ReadTag(&woff_tag)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error reading WOFF marker");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (woff_tag != Tag("wOFF")) {
|
if (woff_tag != Tag("wOFF")) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("invalid WOFF marker");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file.ReadTag(&header->version)) {
|
if (!file.ReadTag(&header->version)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error reading version tag");
|
||||||
}
|
}
|
||||||
if (!IsValidVersionTag(header->version)) {
|
if (!IsValidVersionTag(header->version)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("invalid version tag");
|
||||||
}
|
}
|
||||||
|
|
||||||
header->search_range = 0;
|
header->search_range = 0;
|
||||||
@ -283,27 +297,27 @@ bool ProcessWOFF(ots::OpenTypeFile *header,
|
|||||||
|
|
||||||
uint32_t reported_length;
|
uint32_t reported_length;
|
||||||
if (!file.ReadU32(&reported_length) || length != reported_length) {
|
if (!file.ReadU32(&reported_length) || length != reported_length) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("incorrect file size in WOFF header");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!file.ReadU16(&header->num_tables) || !header->num_tables) {
|
if (!file.ReadU16(&header->num_tables) || !header->num_tables) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error reading number of tables");
|
||||||
}
|
}
|
||||||
|
|
||||||
uint16_t reserved_value;
|
uint16_t reserved_value;
|
||||||
if (!file.ReadU16(&reserved_value) || reserved_value) {
|
if (!file.ReadU16(&reserved_value) || reserved_value) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error in reserved field of WOFF header");
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t reported_total_sfnt_size;
|
uint32_t reported_total_sfnt_size;
|
||||||
if (!file.ReadU32(&reported_total_sfnt_size)) {
|
if (!file.ReadU32(&reported_total_sfnt_size)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error reading total sfnt size");
|
||||||
}
|
}
|
||||||
|
|
||||||
// We don't care about these fields of the header:
|
// We don't care about these fields of the header:
|
||||||
// uint16_t major_version, minor_version
|
// uint16_t major_version, minor_version
|
||||||
if (!file.Skip(2 * 2)) {
|
if (!file.Skip(2 * 2)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error skipping WOFF header fields");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks metadata block size.
|
// Checks metadata block size.
|
||||||
@ -313,11 +327,11 @@ bool ProcessWOFF(ots::OpenTypeFile *header,
|
|||||||
if (!file.ReadU32(&meta_offset) ||
|
if (!file.ReadU32(&meta_offset) ||
|
||||||
!file.ReadU32(&meta_length) ||
|
!file.ReadU32(&meta_length) ||
|
||||||
!file.ReadU32(&meta_length_orig)) {
|
!file.ReadU32(&meta_length_orig)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error reading WOFF header fields");
|
||||||
}
|
}
|
||||||
if (meta_offset) {
|
if (meta_offset) {
|
||||||
if (meta_offset >= length || length - meta_offset < meta_length) {
|
if (meta_offset >= length || length - meta_offset < meta_length) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("invalid metadata block location/size");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -326,11 +340,11 @@ bool ProcessWOFF(ots::OpenTypeFile *header,
|
|||||||
uint32_t priv_length;
|
uint32_t priv_length;
|
||||||
if (!file.ReadU32(&priv_offset) ||
|
if (!file.ReadU32(&priv_offset) ||
|
||||||
!file.ReadU32(&priv_length)) {
|
!file.ReadU32(&priv_length)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error reading WOFF header fields");
|
||||||
}
|
}
|
||||||
if (priv_offset) {
|
if (priv_offset) {
|
||||||
if (priv_offset >= length || length - priv_offset < priv_length) {
|
if (priv_offset >= length || length - priv_offset < priv_length) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("invalid private block location/size");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -348,12 +362,12 @@ bool ProcessWOFF(ots::OpenTypeFile *header,
|
|||||||
!file.ReadU32(&table.length) ||
|
!file.ReadU32(&table.length) ||
|
||||||
!file.ReadU32(&table.uncompressed_length) ||
|
!file.ReadU32(&table.uncompressed_length) ||
|
||||||
!file.ReadU32(&table.chksum)) {
|
!file.ReadU32(&table.chksum)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error reading table directory");
|
||||||
}
|
}
|
||||||
|
|
||||||
total_sfnt_size += Round4(table.uncompressed_length);
|
total_sfnt_size += Round4(table.uncompressed_length);
|
||||||
if (total_sfnt_size > std::numeric_limits<uint32_t>::max()) {
|
if (total_sfnt_size > std::numeric_limits<uint32_t>::max()) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("sfnt size overflow");
|
||||||
}
|
}
|
||||||
tables.push_back(table);
|
tables.push_back(table);
|
||||||
if (i == 0 || tables[first_index].offset > table.offset)
|
if (i == 0 || tables[first_index].offset > table.offset)
|
||||||
@ -363,17 +377,17 @@ bool ProcessWOFF(ots::OpenTypeFile *header,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (reported_total_sfnt_size != total_sfnt_size) {
|
if (reported_total_sfnt_size != total_sfnt_size) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("uncompressed sfnt size mismatch");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Table data must follow immediately after the header.
|
// Table data must follow immediately after the header.
|
||||||
if (tables[first_index].offset != Round4(file.offset())) {
|
if (tables[first_index].offset != Round4(file.offset())) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("junk before tables in WOFF file");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tables[last_index].offset >= length ||
|
if (tables[last_index].offset >= length ||
|
||||||
length - tables[last_index].offset < tables[last_index].length) {
|
length - tables[last_index].offset < tables[last_index].length) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("invalid table location/size");
|
||||||
}
|
}
|
||||||
// Blocks must follow immediately after the previous block.
|
// Blocks must follow immediately after the previous block.
|
||||||
// (Except for padding with a maximum of three null bytes)
|
// (Except for padding with a maximum of three null bytes)
|
||||||
@ -381,30 +395,30 @@ bool ProcessWOFF(ots::OpenTypeFile *header,
|
|||||||
static_cast<uint64_t>(tables[last_index].offset) +
|
static_cast<uint64_t>(tables[last_index].offset) +
|
||||||
static_cast<uint64_t>(tables[last_index].length));
|
static_cast<uint64_t>(tables[last_index].length));
|
||||||
if (block_end > std::numeric_limits<uint32_t>::max()) {
|
if (block_end > std::numeric_limits<uint32_t>::max()) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("invalid table location/size");
|
||||||
}
|
}
|
||||||
if (meta_offset) {
|
if (meta_offset) {
|
||||||
if (block_end != meta_offset) {
|
if (block_end != meta_offset) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("invalid metadata block location");
|
||||||
}
|
}
|
||||||
block_end = Round4(static_cast<uint64_t>(meta_offset) +
|
block_end = Round4(static_cast<uint64_t>(meta_offset) +
|
||||||
static_cast<uint64_t>(meta_length));
|
static_cast<uint64_t>(meta_length));
|
||||||
if (block_end > std::numeric_limits<uint32_t>::max()) {
|
if (block_end > std::numeric_limits<uint32_t>::max()) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("invalid metadata block size");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (priv_offset) {
|
if (priv_offset) {
|
||||||
if (block_end != priv_offset) {
|
if (block_end != priv_offset) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("invalid private block location");
|
||||||
}
|
}
|
||||||
block_end = Round4(static_cast<uint64_t>(priv_offset) +
|
block_end = Round4(static_cast<uint64_t>(priv_offset) +
|
||||||
static_cast<uint64_t>(priv_length));
|
static_cast<uint64_t>(priv_length));
|
||||||
if (block_end > std::numeric_limits<uint32_t>::max()) {
|
if (block_end > std::numeric_limits<uint32_t>::max()) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("invalid private block size");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (block_end != Round4(length)) {
|
if (block_end != Round4(length)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("file length mismatch (trailing junk?)");
|
||||||
}
|
}
|
||||||
|
|
||||||
return ProcessGeneric(header, output, data, length, tables, file);
|
return ProcessGeneric(header, output, data, length, tables, file);
|
||||||
@ -425,46 +439,46 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
|
|||||||
const uint32_t this_tag = ntohl(tables[i].tag);
|
const uint32_t this_tag = ntohl(tables[i].tag);
|
||||||
const uint32_t prev_tag = ntohl(tables[i - 1].tag);
|
const uint32_t prev_tag = ntohl(tables[i - 1].tag);
|
||||||
if (this_tag <= prev_tag) {
|
if (this_tag <= prev_tag) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("table directory not correctly ordered");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// all tag names must be built from printable ASCII characters
|
// all tag names must be built from printable ASCII characters
|
||||||
if (!CheckTag(tables[i].tag)) {
|
if (!CheckTag(tables[i].tag)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_TAG("invalid table tag", &tables[i].tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
// tables must be 4-byte aligned
|
// tables must be 4-byte aligned
|
||||||
if (tables[i].offset & 3) {
|
if (tables[i].offset & 3) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_TAG("misaligned table", &tables[i].tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
// and must be within the file
|
// and must be within the file
|
||||||
if (tables[i].offset < data_offset || tables[i].offset >= length) {
|
if (tables[i].offset < data_offset || tables[i].offset >= length) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_TAG("invalid table offset", &tables[i].tag);
|
||||||
}
|
}
|
||||||
// disallow all tables with a zero length
|
// disallow all tables with a zero length
|
||||||
if (tables[i].length < 1) {
|
if (tables[i].length < 1) {
|
||||||
// Note: malayalam.ttf has zero length CVT table...
|
// Note: malayalam.ttf has zero length CVT table...
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_TAG("zero-length table", &tables[i].tag);
|
||||||
}
|
}
|
||||||
// disallow all tables with a length > 1GB
|
// disallow all tables with a length > 1GB
|
||||||
if (tables[i].length > 1024 * 1024 * 1024) {
|
if (tables[i].length > 1024 * 1024 * 1024) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_TAG("table length exceeds 1GB", &tables[i].tag);
|
||||||
}
|
}
|
||||||
// disallow tables where the uncompressed size is < the compressed size.
|
// disallow tables where the uncompressed size is < the compressed size.
|
||||||
if (tables[i].uncompressed_length < tables[i].length) {
|
if (tables[i].uncompressed_length < tables[i].length) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_TAG("invalid compressed table", &tables[i].tag);
|
||||||
}
|
}
|
||||||
if (tables[i].uncompressed_length > tables[i].length) {
|
if (tables[i].uncompressed_length > tables[i].length) {
|
||||||
// We'll probably be decompressing this table.
|
// We'll probably be decompressing this table.
|
||||||
|
|
||||||
// disallow all tables which uncompress to > 30 MB
|
// disallow all tables which uncompress to > 30 MB
|
||||||
if (tables[i].uncompressed_length > 30 * 1024 * 1024) {
|
if (tables[i].uncompressed_length > 30 * 1024 * 1024) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_TAG("uncompressed length exceeds 30MB", &tables[i].tag);
|
||||||
}
|
}
|
||||||
if (uncompressed_sum + tables[i].uncompressed_length < uncompressed_sum) {
|
if (uncompressed_sum + tables[i].uncompressed_length < uncompressed_sum) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_TAG("overflow of uncompressed sum", &tables[i].tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
uncompressed_sum += tables[i].uncompressed_length;
|
uncompressed_sum += tables[i].uncompressed_length;
|
||||||
@ -476,13 +490,13 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
|
|||||||
// called TTX seems not to add 0-padding to the final table. It might be
|
// called TTX seems not to add 0-padding to the final table. It might be
|
||||||
// ok to accept these fonts so we round up the length of the font file.
|
// ok to accept these fonts so we round up the length of the font file.
|
||||||
if (!end_byte || end_byte > length) {
|
if (!end_byte || end_byte > length) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_TAG("table overruns end of file", &tables[i].tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// All decompressed tables uncompressed must be <= 30MB.
|
// All decompressed tables uncompressed must be <= 30MB.
|
||||||
if (uncompressed_sum > 30 * 1024 * 1024) {
|
if (uncompressed_sum > 30 * 1024 * 1024) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("uncompressed sum exceeds 30MB");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::map<uint32_t, OpenTypeTable> table_map;
|
std::map<uint32_t, OpenTypeTable> table_map;
|
||||||
@ -504,7 +518,7 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
|
|||||||
for (unsigned i = 0; i < overlap_checker.size(); ++i) {
|
for (unsigned i = 0; i < overlap_checker.size(); ++i) {
|
||||||
overlap_count += (overlap_checker[i].second ? 1 : -1);
|
overlap_count += (overlap_checker[i].second ? 1 : -1);
|
||||||
if (overlap_count > 1) {
|
if (overlap_count > 1) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("overlapping tables");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -518,7 +532,7 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
|
|||||||
|
|
||||||
if (it == table_map.end()) {
|
if (it == table_map.end()) {
|
||||||
if (table_parsers[i].required) {
|
if (table_parsers[i].required) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_TAG("missing required table", table_parsers[i].tag);
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -534,7 +548,7 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
|
|||||||
int r = uncompress((Bytef*) table_data, &dest_len,
|
int r = uncompress((Bytef*) table_data, &dest_len,
|
||||||
data + it->second.offset, it->second.length);
|
data + it->second.offset, it->second.length);
|
||||||
if (r != Z_OK || dest_len != table_length) {
|
if (r != Z_OK || dest_len != table_length) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_TAG("uncompress failed", table_parsers[i].tag);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// uncompressed table. We can process directly from memory.
|
// uncompressed table. We can process directly from memory.
|
||||||
@ -543,24 +557,26 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!table_parsers[i].parse(header, table_data, table_length)) {
|
if (!table_parsers[i].parse(header, table_data, table_length)) {
|
||||||
return OTS_FAILURE();
|
// TODO: parsers should generate specific messages detailing the failure;
|
||||||
|
// once those are all added, we won't need a generic failure message here
|
||||||
|
return OTS_FAILURE_MSG_TAG("failed to parse table", table_parsers[i].tag);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (header->cff) {
|
if (header->cff) {
|
||||||
// font with PostScript glyph
|
// font with PostScript glyph
|
||||||
if (header->version != Tag("OTTO")) {
|
if (header->version != Tag("OTTO")) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("wrong font version for PostScript glyph data");
|
||||||
}
|
}
|
||||||
if (header->glyf || header->loca) {
|
if (header->glyf || header->loca) {
|
||||||
// mixing outline formats is not recommended
|
// mixing outline formats is not recommended
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("font contains both PS and TT glyphs");
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (!header->glyf || !header->loca) {
|
if (!header->glyf || !header->loca) {
|
||||||
// No TrueType glyph found.
|
// No TrueType glyph found.
|
||||||
// Note: bitmap-only fonts are not supported.
|
// Note: bitmap-only fonts are not supported.
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("neither PS nor TT glyphs present");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,19 +597,21 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
|
|||||||
}
|
}
|
||||||
const uint16_t output_search_range = (1u << max_pow2) << 4;
|
const uint16_t output_search_range = (1u << max_pow2) << 4;
|
||||||
|
|
||||||
|
// most of the errors here are highly unlikely - they'd only occur if the
|
||||||
|
// output stream returns a failure, e.g. lack of space to write
|
||||||
output->ResetChecksum();
|
output->ResetChecksum();
|
||||||
if (!output->WriteTag(header->version) ||
|
if (!output->WriteTag(header->version) ||
|
||||||
!output->WriteU16(num_output_tables) ||
|
!output->WriteU16(num_output_tables) ||
|
||||||
!output->WriteU16(output_search_range) ||
|
!output->WriteU16(output_search_range) ||
|
||||||
!output->WriteU16(max_pow2) ||
|
!output->WriteU16(max_pow2) ||
|
||||||
!output->WriteU16((num_output_tables << 4) - output_search_range)) {
|
!output->WriteU16((num_output_tables << 4) - output_search_range)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error writing output");
|
||||||
}
|
}
|
||||||
const uint32_t offset_table_chksum = output->chksum();
|
const uint32_t offset_table_chksum = output->chksum();
|
||||||
|
|
||||||
const size_t table_record_offset = output->Tell();
|
const size_t table_record_offset = output->Tell();
|
||||||
if (!output->Pad(16 * num_output_tables)) {
|
if (!output->Pad(16 * num_output_tables)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error writing output");
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<OutputTable> out_tables;
|
std::vector<OutputTable> out_tables;
|
||||||
@ -618,20 +636,20 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
|
|||||||
head_table_offset = out.offset;
|
head_table_offset = out.offset;
|
||||||
}
|
}
|
||||||
if (!table_parsers[i].serialise(output, header)) {
|
if (!table_parsers[i].serialise(output, header)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_TAG("failed to serialize table", table_parsers[i].tag);
|
||||||
}
|
}
|
||||||
|
|
||||||
const size_t end_offset = output->Tell();
|
const size_t end_offset = output->Tell();
|
||||||
if (end_offset <= out.offset) {
|
if (end_offset <= out.offset) {
|
||||||
// paranoid check. |end_offset| is supposed to be greater than the offset,
|
// paranoid check. |end_offset| is supposed to be greater than the offset,
|
||||||
// as long as the Tell() interface is implemented correctly.
|
// as long as the Tell() interface is implemented correctly.
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error writing output");
|
||||||
}
|
}
|
||||||
out.length = end_offset - out.offset;
|
out.length = end_offset - out.offset;
|
||||||
|
|
||||||
// align tables to four bytes
|
// align tables to four bytes
|
||||||
if (!output->Pad((4 - (end_offset & 3)) % 4)) {
|
if (!output->Pad((4 - (end_offset & 3)) % 4)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error writing output");
|
||||||
}
|
}
|
||||||
out.chksum = output->chksum();
|
out.chksum = output->chksum();
|
||||||
out_tables.push_back(out);
|
out_tables.push_back(out);
|
||||||
@ -642,7 +660,7 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
|
|||||||
// Need to sort the output tables for inclusion in the file
|
// Need to sort the output tables for inclusion in the file
|
||||||
std::sort(out_tables.begin(), out_tables.end(), OutputTable::SortByTag);
|
std::sort(out_tables.begin(), out_tables.end(), OutputTable::SortByTag);
|
||||||
if (!output->Seek(table_record_offset)) {
|
if (!output->Seek(table_record_offset)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error writing output");
|
||||||
}
|
}
|
||||||
|
|
||||||
output->ResetChecksum();
|
output->ResetChecksum();
|
||||||
@ -652,7 +670,7 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
|
|||||||
!output->WriteU32(out_tables[i].chksum) ||
|
!output->WriteU32(out_tables[i].chksum) ||
|
||||||
!output->WriteU32(out_tables[i].offset) ||
|
!output->WriteU32(out_tables[i].offset) ||
|
||||||
!output->WriteU32(out_tables[i].length)) {
|
!output->WriteU32(out_tables[i].length)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error writing output");
|
||||||
}
|
}
|
||||||
tables_chksum += out_tables[i].chksum;
|
tables_chksum += out_tables[i].chksum;
|
||||||
}
|
}
|
||||||
@ -665,17 +683,17 @@ bool ProcessGeneric(ots::OpenTypeFile *header, ots::OTSStream *output,
|
|||||||
|
|
||||||
// seek into the 'head' table and write in the checksum magic value
|
// seek into the 'head' table and write in the checksum magic value
|
||||||
if (!head_table_offset) {
|
if (!head_table_offset) {
|
||||||
return OTS_FAILURE(); // not reached.
|
return OTS_FAILURE_MSG_HDR("internal error!");
|
||||||
}
|
}
|
||||||
if (!output->Seek(head_table_offset + 8)) {
|
if (!output->Seek(head_table_offset + 8)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error writing output");
|
||||||
}
|
}
|
||||||
if (!output->WriteU32(chksum_magic)) {
|
if (!output->WriteU32(chksum_magic)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error writing output");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!output->Seek(end_of_file)) {
|
if (!output->Seek(end_of_file)) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_HDR("error writing output");
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -689,11 +707,20 @@ void DisableDebugOutput() {
|
|||||||
g_debug_output = false;
|
g_debug_output = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Process(OTSStream *output, const uint8_t *data, size_t length,
|
bool OTS_API Process(OTSStream *output, const uint8_t *data, size_t length,
|
||||||
bool preserveGraphite) {
|
#ifdef MOZ_OTS_REPORT_ERRORS
|
||||||
|
MessageFunc message_func, void *user_data,
|
||||||
|
#endif
|
||||||
|
bool preserveGraphite) {
|
||||||
OpenTypeFile header;
|
OpenTypeFile header;
|
||||||
|
|
||||||
|
#ifdef MOZ_OTS_REPORT_ERRORS
|
||||||
|
header.message_func = message_func;
|
||||||
|
header.user_data = user_data;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (length < 4) {
|
if (length < 4) {
|
||||||
return OTS_FAILURE();
|
return OTS_FAILURE_MSG_(&header, "file less than 4 bytes");
|
||||||
}
|
}
|
||||||
|
|
||||||
header.preserve_graphite = preserveGraphite;
|
header.preserve_graphite = preserveGraphite;
|
||||||
|
@ -38,6 +38,40 @@ void Warning(const char *f, int l, const char *format, ...)
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef MOZ_OTS_REPORT_ERRORS
|
||||||
|
|
||||||
|
// All OTS_FAILURE_* macros ultimately evaluate to 'false', just like the original
|
||||||
|
// message-less OTS_FAILURE(), so that the current parser will return 'false' as
|
||||||
|
// its result (indicating a failure).
|
||||||
|
// If the message-callback feature is enabled, and a message_func pointer has been
|
||||||
|
// provided, this will be called before returning the 'false' status.
|
||||||
|
|
||||||
|
// Generate a simple message
|
||||||
|
#define OTS_FAILURE_MSG_(otf_,msg_) \
|
||||||
|
((otf_)->message_func && \
|
||||||
|
(*(otf_)->message_func)((otf_)->user_data, "%s", msg_) && \
|
||||||
|
false)
|
||||||
|
|
||||||
|
// Generate a message with an associated table tag
|
||||||
|
#define OTS_FAILURE_MSG_TAG_(otf_,msg_,tag_) \
|
||||||
|
((otf_)->message_func && \
|
||||||
|
(*(otf_)->message_func)((otf_)->user_data, "table '%4.4s': %s", tag_, msg_) && \
|
||||||
|
false)
|
||||||
|
|
||||||
|
// Convenience macro for use in files that only handle a single table tag,
|
||||||
|
// defined as TABLE_NAME at the top of the file; the 'file' variable is
|
||||||
|
// expected to be the current OpenTypeFile pointer.
|
||||||
|
#define OTS_FAILURE_MSG(msg_) OTS_FAILURE_MSG_TAG_(file, msg_, TABLE_NAME)
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// If the message-callback feature is not enabled, error messages are just dropped.
|
||||||
|
#define OTS_FAILURE_MSG_(otf_,msg_) OTS_FAILURE()
|
||||||
|
#define OTS_FAILURE_MSG_TAG_(otf_,msg_,tag_) OTS_FAILURE()
|
||||||
|
#define OTS_FAILURE_MSG(msg_) OTS_FAILURE()
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
// Define OTS_NO_TRANSCODE_HINTS (i.e., g++ -DOTS_NO_TRANSCODE_HINTS) if you
|
// Define OTS_NO_TRANSCODE_HINTS (i.e., g++ -DOTS_NO_TRANSCODE_HINTS) if you
|
||||||
// want to omit TrueType hinting instructions and variables in glyf, fpgm, prep,
|
// want to omit TrueType hinting instructions and variables in glyf, fpgm, prep,
|
||||||
// and cvt tables.
|
// and cvt tables.
|
||||||
@ -207,6 +241,11 @@ struct OpenTypeFile {
|
|||||||
uint16_t entry_selector;
|
uint16_t entry_selector;
|
||||||
uint16_t range_shift;
|
uint16_t range_shift;
|
||||||
|
|
||||||
|
#ifdef MOZ_OTS_REPORT_ERRORS
|
||||||
|
MessageFunc message_func;
|
||||||
|
void *user_data;
|
||||||
|
#endif
|
||||||
|
|
||||||
// This is used to tell the relevant parsers whether to preserve the
|
// This is used to tell the relevant parsers whether to preserve the
|
||||||
// Graphite layout tables (currently _without_ any checking)
|
// Graphite layout tables (currently _without_ any checking)
|
||||||
bool preserve_graphite;
|
bool preserve_graphite;
|
||||||
|
Loading…
Reference in New Issue
Block a user