author | Jonathan Kew <jkew@mozilla.com> |
Thu, 19 Mar 2015 11:11:30 +0000 | |
changeset 234431 | eb6141f8f3a342096e162c17381dec1e22426372 |
parent 234430 | 46f600c2366bba5e02566ee85df2a9756d5bf335 |
child 234443 | 661d70e3289993ed2f6477cd5847ab99d9998177 |
push id | 57135 |
push user | jkew@mozilla.com |
push date | Thu, 19 Mar 2015 11:12:13 +0000 |
treeherder | mozilla-inbound@eb6141f8f3a3 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | jdaggett |
bugs | 1142952 |
milestone | 39.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/gfx/ots/README.mozilla +++ b/gfx/ots/README.mozilla @@ -1,12 +1,11 @@ This is the Sanitiser for OpenType project, from http://code.google.com/p/ots/. Our reference repository is https://github.com/khaledhosny/ots/. -Current revision: 7091a232a10a741591ff25158c98ca3f0c319cf9 +Current revision: e779d45e7a96d3b97ed3d2b76db7478cb86fdd8b Upstream files included: LICENSE, src/, include/ Additional files: README.mozilla, src/moz.build Additional patch: ots-visibility.patch (bug 711079). -Additional patch: ots-brotli-path.patch (bug 1064737).
--- a/gfx/ots/include/opentype-sanitiser.h +++ b/gfx/ots/include/opentype-sanitiser.h @@ -218,37 +218,26 @@ class OTS_API OTSContext { virtual ~OTSContext() {} // Process a given OpenType file and write out a sanitised version // output: a pointer to an object implementing the OTSStream interface. The // sanitisied output will be written to this. In the even of a failure, // partial output may have been written. // input: the OpenType file // length: the size, in bytes, of |input| - // context: optional context that holds various OTS settings like user callbacks bool Process(OTSStream *output, const uint8_t *input, size_t length); // This function will be called when OTS is reporting an error. // level: the severity of the generated message: // 0: error messages in case OTS fails to sanitize the font. // 1: warning messages about issue OTS fixed in the sanitized font. virtual void Message(int level, const char *format, ...) MSGFUNC_FMT_ATTR {} // This function will be called when OTS needs to decide what to do for a // font table. // tag: table tag as an integer in big-endian byte order, independent of // platform endianness virtual TableAction GetTableAction(uint32_t tag) { return ots::TABLE_ACTION_DEFAULT; } }; -// For backward compatibility - remove once Chrome switches over to the new API. -bool Process(OTSStream *output, const uint8_t *input, size_t length); - -// Force to disable debug output even when the library is compiled with -// -DOTS_DEBUG. -void DisableDebugOutput(); - -// Enable WOFF2 support(experimental). -void OTS_API EnableWOFF2(); - } // namespace ots #endif // OPENTYPE_SANITISER_H_
--- a/gfx/ots/ots-visibility.patch +++ b/gfx/ots/ots-visibility.patch @@ -51,23 +51,8 @@ diff --git a/gfx/ots/include/opentype-sa public: OTSContext() {} virtual ~OTSContext() {} // Process a given OpenType file and write out a sanitised version // output: a pointer to an object implementing the OTSStream interface. The // sanitisied output will be written to this. In the even of a failure, // partial output may have been written. -@@ -222,13 +242,13 @@ class OTSContext { - // For backward compatibility - remove once Chrome switches over to the new API. - bool Process(OTSStream *output, const uint8_t *input, size_t length); - - // Force to disable debug output even when the library is compiled with - // -DOTS_DEBUG. - void DisableDebugOutput(); - - // Enable WOFF2 support(experimental). --void EnableWOFF2(); -+void OTS_API EnableWOFF2(); - - } // namespace ots - - #endif // OPENTYPE_SANITISER_H_
--- a/gfx/ots/src/cmap.cc +++ b/gfx/ots/src/cmap.cc @@ -679,21 +679,21 @@ bool ots_cmap_parse(OpenTypeFile *file, // check if the table is sorted first by platform ID, then by encoding ID. uint32_t last_id = 0; for (unsigned i = 0; i < num_tables; ++i) { uint32_t current_id = (subtable_headers[i].platform << 24) + (subtable_headers[i].encoding << 16) + subtable_headers[i].language; if ((i != 0) && (last_id >= current_id)) { - return OTS_FAILURE_MSG("subtable %d with platform ID %d, encoding ID %d, language ID %d " - "following subtable with platform ID %d, encoding ID %d, language ID %d", - i, - (uint8_t)(current_id >> 24), (uint8_t)(current_id >> 16), (uint8_t)(current_id), - (uint8_t)(last_id >> 24), (uint8_t)(last_id >> 16), (uint8_t)(last_id)); + OTS_WARNING("subtable %d with platform ID %d, encoding ID %d, language ID %d " + "following subtable with platform ID %d, encoding ID %d, language ID %d", + i, + (uint8_t)(current_id >> 24), (uint8_t)(current_id >> 16), (uint8_t)(current_id), + (uint8_t)(last_id >> 24), (uint8_t)(last_id >> 16), (uint8_t)(last_id)); } last_id = current_id; } // Now, verify that all the lengths are sane for (unsigned i = 0; i < num_tables; ++i) { if (!subtable_headers[i].length) continue; if (subtable_headers[i].length > 1024 * 1024 * 1024) {
--- a/gfx/ots/src/cvt.cc +++ b/gfx/ots/src/cvt.cc @@ -33,17 +33,17 @@ bool ots_cvt_parse(OpenTypeFile *file, c cvt->length = length; return true; } bool ots_cvt_should_serialise(OpenTypeFile *file) { if (!file->glyf) { return false; // this table is not for CFF fonts. } - return file->cvt; + return file->cvt != NULL; } bool ots_cvt_serialise(OTSStream *out, OpenTypeFile *file) { const OpenTypeCVT *cvt = file->cvt; if (!out->Write(cvt->data, cvt->length)) { return OTS_FAILURE_MSG("Failed to write CVT table"); }
--- a/gfx/ots/src/fpgm.cc +++ b/gfx/ots/src/fpgm.cc @@ -27,17 +27,17 @@ bool ots_fpgm_parse(OpenTypeFile *file, fpgm->data = data; fpgm->length = length; return true; } bool ots_fpgm_should_serialise(OpenTypeFile *file) { if (!file->glyf) return false; // this table is not for CFF fonts. - return file->fpgm; + return file->fpgm != NULL; } bool ots_fpgm_serialise(OTSStream *out, OpenTypeFile *file) { const OpenTypeFPGM *fpgm = file->fpgm; if (!out->Write(fpgm->data, fpgm->length)) { return OTS_FAILURE_MSG("Failed to write fpgm"); }
--- a/gfx/ots/src/os2.cc +++ b/gfx/ots/src/os2.cc @@ -1,14 +1,15 @@ // Copyright (c) 2009 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 <string> + #include "os2.h" - #include "head.h" // OS/2 - OS/2 and Windows Metrics // http://www.microsoft.com/typography/otspec/os2.htm #define TABLE_NAME "OS/2" namespace ots { @@ -30,36 +31,39 @@ bool ots_os2_parse(OpenTypeFile *file, c !table.ReadS16(&os2->subscript_y_offset) || !table.ReadS16(&os2->superscript_x_size) || !table.ReadS16(&os2->superscript_y_size) || !table.ReadS16(&os2->superscript_x_offset) || !table.ReadS16(&os2->superscript_y_offset) || !table.ReadS16(&os2->strikeout_size) || !table.ReadS16(&os2->strikeout_position) || !table.ReadS16(&os2->family_class)) { - return OTS_FAILURE_MSG("Failed toi read basic os2 elements"); + return OTS_FAILURE_MSG("Error reading basic table elements"); } if (os2->version > 4) { - return OTS_FAILURE_MSG("os2 version too high %d", os2->version); + return OTS_FAILURE_MSG("Unsupported table version: %u", os2->version); } - // Some linux fonts (e.g., Kedage-t.ttf and LucidaSansDemiOblique.ttf) have - // weird weight/width classes. Overwrite them with FW_NORMAL/1/9. - if (os2->weight_class < 100 || - os2->weight_class > 900 || - os2->weight_class % 100) { - OTS_WARNING("bad weight: %u", os2->weight_class); - os2->weight_class = 400; // FW_NORMAL + // Follow WPF Font Selection Model's advice. + if (1 <= os2->weight_class && os2->weight_class <= 9) { + OTS_WARNING("Bad usWeightClass: %u, changing it to: %u", os2->weight_class, os2->weight_class * 100); + os2->weight_class *= 100; } + // Ditto. + if (os2->weight_class > 999) { + OTS_WARNING("Bad usWeightClass: %u, changing it to: %d", os2->weight_class, 999); + os2->weight_class = 999; + } + if (os2->width_class < 1) { - OTS_WARNING("bad width: %u", os2->width_class); + OTS_WARNING("Bad usWidthClass: %u, changing it to: %d", os2->width_class, 1); os2->width_class = 1; } else if (os2->width_class > 9) { - OTS_WARNING("bad width: %u", os2->width_class); + OTS_WARNING("Bad usWidthClass: %u, changing it to: %d", os2->width_class, 9); os2->width_class = 9; } // lowest 3 bits of fsType are exclusive. if (os2->type & 0x2) { // mask bits 2 & 3. os2->type &= 0xfff3u; } else if (os2->type & 0x4) { @@ -68,68 +72,72 @@ bool ots_os2_parse(OpenTypeFile *file, c } else if (os2->type & 0x8) { // mask bits 1 & 2. os2->type &= 0xfff9u; } // mask reserved bits. use only 0..3, 8, 9 bits. os2->type &= 0x30f; - if (os2->subscript_x_size < 0) { - OTS_WARNING("bad subscript_x_size: %d", os2->subscript_x_size); - os2->subscript_x_size = 0; - } - if (os2->subscript_y_size < 0) { - OTS_WARNING("bad subscript_y_size: %d", os2->subscript_y_size); - os2->subscript_y_size = 0; - } - if (os2->superscript_x_size < 0) { - OTS_WARNING("bad superscript_x_size: %d", os2->superscript_x_size); - os2->superscript_x_size = 0; - } - if (os2->superscript_y_size < 0) { - OTS_WARNING("bad superscript_y_size: %d", os2->superscript_y_size); - os2->superscript_y_size = 0; - } - if (os2->strikeout_size < 0) { - OTS_WARNING("bad strikeout_size: %d", os2->strikeout_size); - os2->strikeout_size = 0; +#define SET_TO_ZERO(a, b) \ + if (os2->b < 0) { \ + OTS_WARNING("Bad " a ": %d, setting it to zero", os2->b); \ + os2->b = 0; \ } + SET_TO_ZERO("ySubscriptXSize", subscript_x_size); + SET_TO_ZERO("ySubscriptYSize", subscript_y_size); + SET_TO_ZERO("ySuperscriptXSize", superscript_x_size); + SET_TO_ZERO("ySuperscriptYSize", superscript_y_size); + SET_TO_ZERO("yStrikeoutSize", strikeout_size); +#undef SET_TO_ZERO + + static std::string panose_strings[10] = { + "bFamilyType", + "bSerifStyle", + "bWeight", + "bProportion", + "bContrast", + "bStrokeVariation", + "bArmStyle", + "bLetterform", + "bMidline", + "bXHeight", + }; for (unsigned i = 0; i < 10; ++i) { if (!table.ReadU8(&os2->panose[i])) { - return OTS_FAILURE_MSG("Failed to read panose in os2 table"); + return OTS_FAILURE_MSG("Error reading PANOSE %s", panose_strings[i].c_str()); } } if (!table.ReadU32(&os2->unicode_range_1) || !table.ReadU32(&os2->unicode_range_2) || !table.ReadU32(&os2->unicode_range_3) || !table.ReadU32(&os2->unicode_range_4) || !table.ReadU32(&os2->vendor_id) || !table.ReadU16(&os2->selection) || !table.ReadU16(&os2->first_char_index) || !table.ReadU16(&os2->last_char_index) || !table.ReadS16(&os2->typo_ascender) || !table.ReadS16(&os2->typo_descender) || !table.ReadS16(&os2->typo_linegap) || !table.ReadU16(&os2->win_ascent) || !table.ReadU16(&os2->win_descent)) { - return OTS_FAILURE_MSG("Failed to read more basic os2 fields"); + return OTS_FAILURE_MSG("Error reading more basic table fields"); } // If bit 6 is set, then bits 0 and 5 must be clear. if (os2->selection & 0x40) { os2->selection &= 0xffdeu; } // the settings of bits 0 and 1 must be reflected in the macStyle bits // in the 'head' table. if (!file->head) { - return OTS_FAILURE_MSG("Head table missing from font as needed by os2 table"); + return OTS_FAILURE_MSG("Needed head table is missing from the font"); } if ((os2->selection & 0x1) && !(file->head->mac_style & 0x2)) { OTS_WARNING("adjusting Mac style (italic)"); file->head->mac_style |= 0x2; } if ((os2->selection & 0x2) && !(file->head->mac_style & 0x4)) {
--- a/gfx/ots/src/ots.cc +++ b/gfx/ots/src/ots.cc @@ -9,31 +9,27 @@ #include <algorithm> #include <cstdlib> #include <cstring> #include <limits> #include <map> #include <vector> -#ifndef OTS_DISABLE_WOFF2 #include "woff2.h" -#endif // The OpenType Font File // http://www.microsoft.com/typography/otspec/cmap.htm namespace { -bool g_debug_output = true; -bool g_enable_woff2 = false; - // 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_) +#define OTS_WARNING_MSG_HDR(msg_) OTS_WARNING_MSG_(header, msg_) struct OpenTypeTable { uint32_t tag; uint32_t chksum; uint32_t offset; uint32_t length; uint32_t uncompressed_length; @@ -284,44 +280,44 @@ bool ProcessWOFF(ots::OpenTypeFile *head uint32_t reported_total_sfnt_size; if (!file.ReadU32(&reported_total_sfnt_size)) { return OTS_FAILURE_MSG_HDR("error reading total sfnt size"); } // We don't care about these fields of the header: // uint16_t major_version, minor_version if (!file.Skip(2 * 2)) { - return OTS_FAILURE_MSG_HDR("error skipping WOFF header fields"); + return OTS_FAILURE_MSG_HDR("Failed to read 'majorVersion' or 'minorVersion'"); } // Checks metadata block size. uint32_t meta_offset; uint32_t meta_length; uint32_t meta_length_orig; if (!file.ReadU32(&meta_offset) || !file.ReadU32(&meta_length) || !file.ReadU32(&meta_length_orig)) { - return OTS_FAILURE_MSG_HDR("error reading WOFF header fields"); + return OTS_FAILURE_MSG_HDR("Failed to read header metadata block fields"); } if (meta_offset) { if (meta_offset >= length || length - meta_offset < meta_length) { - return OTS_FAILURE_MSG_HDR("invalid metadata block location/size"); + return OTS_FAILURE_MSG_HDR("Invalid metadata block offset or length"); } } // Checks private data block size. uint32_t priv_offset; uint32_t priv_length; if (!file.ReadU32(&priv_offset) || !file.ReadU32(&priv_length)) { - return OTS_FAILURE_MSG_HDR("error reading WOFF header fields"); + return OTS_FAILURE_MSG_HDR("Failed to read header private block fields"); } if (priv_offset) { if (priv_offset >= length || length - priv_offset < priv_length) { - return OTS_FAILURE_MSG_HDR("invalid private block location/size"); + return OTS_FAILURE_MSG_HDR("Invalid private block offset or length"); } } // Next up is the list of tables. std::vector<OpenTypeTable> tables; uint32_t first_index = 0; uint32_t last_index = 0; @@ -366,61 +362,59 @@ bool ProcessWOFF(ots::OpenTypeFile *head uint64_t block_end = ots::Round4( static_cast<uint64_t>(tables[last_index].offset) + static_cast<uint64_t>(tables[last_index].length)); if (block_end > std::numeric_limits<uint32_t>::max()) { return OTS_FAILURE_MSG_HDR("invalid table location/size"); } if (meta_offset) { if (block_end != meta_offset) { - return OTS_FAILURE_MSG_HDR("invalid metadata block location"); + return OTS_FAILURE_MSG_HDR("Invalid metadata block offset"); } block_end = ots::Round4(static_cast<uint64_t>(meta_offset) + static_cast<uint64_t>(meta_length)); if (block_end > std::numeric_limits<uint32_t>::max()) { - return OTS_FAILURE_MSG_HDR("invalid metadata block size"); + return OTS_FAILURE_MSG_HDR("Invalid metadata block length"); } } if (priv_offset) { if (block_end != priv_offset) { - return OTS_FAILURE_MSG_HDR("invalid private block location"); + return OTS_FAILURE_MSG_HDR("Invalid private block offset"); } block_end = ots::Round4(static_cast<uint64_t>(priv_offset) + static_cast<uint64_t>(priv_length)); if (block_end > std::numeric_limits<uint32_t>::max()) { - return OTS_FAILURE_MSG_HDR("invalid private block size"); + return OTS_FAILURE_MSG_HDR("Invalid private block length"); } } if (block_end != ots::Round4(length)) { - return OTS_FAILURE_MSG_HDR("file length mismatch (trailing junk?)"); + return OTS_FAILURE_MSG_HDR("File length mismatch (trailing junk?)"); } return ProcessGeneric(header, woff_tag, output, data, length, tables, file); } -#ifndef OTS_DISABLE_WOFF2 bool ProcessWOFF2(ots::OpenTypeFile *header, ots::OTSStream *output, const uint8_t *data, size_t length) { size_t decompressed_size = ots::ComputeWOFF2FinalSize(data, length); if (decompressed_size == 0) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 is set to 0"); } // decompressed font must be <= 30MB if (decompressed_size > 30 * 1024 * 1024) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG_HDR("Size of decompressed WOFF 2.0 font exceeds 30MB"); } std::vector<uint8_t> decompressed_buffer(decompressed_size); - if (!ots::ConvertWOFF2ToTTF(header, &decompressed_buffer[0], decompressed_size, - data, length)) { - return OTS_FAILURE(); + if (!ots::ConvertWOFF2ToSFNT(header, &decompressed_buffer[0], decompressed_size, + data, length)) { + return OTS_FAILURE_MSG_HDR("Failed to convert WOFF 2.0 font to SFNT"); } return ProcessTTF(header, output, &decompressed_buffer[0], decompressed_size); } -#endif ots::TableAction GetTableAction(ots::OpenTypeFile *header, uint32_t tag) { ots::TableAction action = ots::TABLE_ACTION_DEFAULT; action = header->context->GetTableAction(htonl(tag)); if (action == ots::TABLE_ACTION_DEFAULT) { action = ots::TABLE_ACTION_DROP; @@ -474,17 +468,17 @@ bool ProcessGeneric(ots::OpenTypeFile *h for (unsigned i = 0; i < header->num_tables; ++i) { // the tables must be sorted by tag (when taken as big-endian numbers). // This also remove the possibility of duplicate tables. if (i) { const uint32_t this_tag = ntohl(tables[i].tag); const uint32_t prev_tag = ntohl(tables[i - 1].tag); if (this_tag <= prev_tag) { - return OTS_FAILURE_MSG_HDR("table directory not correctly ordered"); + OTS_WARNING_MSG_HDR("Table directory is not correctly ordered"); } } // all tag names must be built from printable ASCII characters if (!CheckTag(tables[i].tag)) { return OTS_FAILURE_MSG_TAG("invalid table tag", &tables[i].tag); } @@ -601,18 +595,24 @@ bool ProcessGeneric(ots::OpenTypeFile *h } if (header->glyf || header->loca) { // mixing outline formats is not recommended return OTS_FAILURE_MSG_HDR("font contains both PS and TT glyphs"); } } else { if (!header->glyf || !header->loca) { // No TrueType glyph found. - // Note: bitmap-only fonts are not supported. - return OTS_FAILURE_MSG_HDR("neither PS nor TT glyphs present"); +#define PASSTHRU_TABLE(TAG) (table_map.find(Tag(TAG)) != table_map.end() && \ + GetTableAction(header, Tag(TAG)) == ots::TABLE_ACTION_PASSTHRU) + // We don't sanitise bitmap table, but don't reject bitmap-only fonts if + // we keep the tables. + if (!PASSTHRU_TABLE("CBDT") || !PASSTHRU_TABLE("CBLC")) { + return OTS_FAILURE_MSG_HDR("no supported glyph shapes table(s) present"); + } +#undef PASSTHRU_TABLE } } uint16_t num_output_tables = 0; for (unsigned i = 0; ; ++i) { if (table_parsers[i].parse == NULL) { break; } @@ -787,64 +787,36 @@ bool IsValidVersionTag(uint32_t tag) { return tag == Tag("\x00\x01\x00\x00") || // OpenType fonts with CFF data have 'OTTO' tag. tag == Tag("OTTO") || // Older Mac fonts might have 'true' or 'typ1' tag. tag == Tag("true") || tag == Tag("typ1"); } -void DisableDebugOutput() { - g_debug_output = false; -} - -void EnableWOFF2() { - g_enable_woff2 = true; -} - bool OTSContext::Process(OTSStream *output, const uint8_t *data, size_t length) { OpenTypeFile header; header.context = this; if (length < 4) { return OTS_FAILURE_MSG_(&header, "file less than 4 bytes"); } bool result; if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') { result = ProcessWOFF(&header, output, data, length); -#ifndef OTS_DISABLE_WOFF2 - } else if (g_enable_woff2 && - data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && - data[3] == '2') { + } else if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == '2') { result = ProcessWOFF2(&header, output, data, length); -#endif } else { result = ProcessTTF(&header, output, data, length); } for (unsigned i = 0; ; ++i) { if (table_parsers[i].parse == NULL) break; table_parsers[i].free(&header); } return result; } -// For backward compatibility -bool Process(OTSStream *output, const uint8_t *data, size_t length) { - static OTSContext context; - return context.Process(output, data, length); -} - -#if !defined(_MSC_VER) && defined(OTS_DEBUG) -bool Failure(const char *f, int l, const char *fn) { - if (g_debug_output) { - std::fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn); - std::fflush(stderr); - } - return false; -} -#endif - } // namespace ots
--- a/gfx/ots/src/ots.h +++ b/gfx/ots/src/ots.h @@ -17,28 +17,32 @@ // arraysize borrowed from base/basictypes.h template <typename T, size_t N> char (&ArraySizeHelper(T (&array)[N]))[N]; #define arraysize(array) (sizeof(ArraySizeHelper(array))) namespace ots { -#if defined(_MSC_VER) || !defined(OTS_DEBUG) +#if !defined(OTS_DEBUG) #define OTS_FAILURE() false #else -#define OTS_FAILURE() ots::Failure(__FILE__, __LINE__, __PRETTY_FUNCTION__) -bool Failure(const char *f, int l, const char *fn); +#define OTS_FAILURE() \ + (\ + std::fprintf(stderr, "ERROR at %s:%d (%s)\n", \ + __FILE__, __LINE__, __FUNCTION__) \ + && false\ + ) #endif // 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 defined(_MSC_VER) || !defined(OTS_DEBUG) +#if !defined(OTS_DEBUG) #define OTS_MESSAGE_(level,otf_,...) \ (otf_)->context->Message(level,__VA_ARGS__) #else #define OTS_MESSAGE_(level,otf_,...) \ OTS_FAILURE(), \ (otf_)->context->Message(level,__VA_ARGS__) #endif
--- a/gfx/ots/src/prep.cc +++ b/gfx/ots/src/prep.cc @@ -27,17 +27,17 @@ bool ots_prep_parse(OpenTypeFile *file, prep->data = data; prep->length = length; return true; } bool ots_prep_should_serialise(OpenTypeFile *file) { if (!file->glyf) return false; // this table is not for CFF fonts. - return file->prep; + return file->prep != NULL; } bool ots_prep_serialise(OTSStream *out, OpenTypeFile *file) { const OpenTypePREP *prep = file->prep; if (!out->Write(prep->data, prep->length)) { return OTS_FAILURE_MSG("Failed to write table length"); }
--- a/gfx/ots/src/woff2.cc +++ b/gfx/ots/src/woff2.cc @@ -40,16 +40,17 @@ const size_t kSfntHeaderSize = 12; const size_t kSfntEntrySize = 16; const size_t kCheckSumAdjustmentOffset = 8; const size_t kEndPtsOfContoursOffset = 10; const size_t kCompositeGlyphBegin = 10; // Note that the byte order is big-endian, not the same as ots.cc #define TAG(a, b, c, d) ((a << 24) | (b << 16) | (c << 8) | d) +#define CHR(t) (t >> 24), (t >> 16), (t >> 8), (t >> 0) const unsigned int kWoff2FlagsTransform = 1 << 5; const uint32_t kKnownTags[] = { TAG('c', 'm', 'a', 'p'), // 0 TAG('h', 'e', 'a', 'd'), // 1 TAG('h', 'h', 'e', 'a'), // 2 TAG('h', 'm', 't', 'x'), // 3 @@ -130,16 +131,20 @@ struct Table { uint32_t dst_length; Table() : tag(0), flags(0), transform_length(0), dst_offset(0), dst_length(0) {} + + bool operator<(const Table& other) const { + return tag < other.tag; + } }; // Based on section 6.1.1 of MicroType Express draft spec bool Read255UShort(ots::Buffer* buf, uint16_t* value) { static const uint8_t kWordCode = 253; static const uint8_t kOneMoreByteCode2 = 254; static const uint8_t kOneMoreByteCode1 = 255; static const uint8_t kLowestUCode = 253; @@ -177,17 +182,17 @@ bool Read255UShort(ots::Buffer* buf, uin bool ReadBase128(ots::Buffer* buf, uint32_t* value) { uint32_t result = 0; for (size_t i = 0; i < 5; ++i) { uint8_t code = 0; if (!buf->ReadU8(&code)) { return OTS_FAILURE(); } // If any of the top seven bits are set then we're about to overflow. - if (result & 0xe0000000U) { + if (result & 0xfe000000U) { return OTS_FAILURE(); } result = (result << 7) | (code & 0x7f); if ((code & 0x80) == 0) { *value = result; return true; } } @@ -504,45 +509,48 @@ bool StoreLoca(const std::vector<uint32_ } else { offset = StoreU16(dst, offset, static_cast<uint16_t>(value >> 1)); } } return true; } // Reconstruct entire glyf table based on transformed original -bool ReconstructGlyf(const uint8_t* data, size_t data_size, +bool ReconstructGlyf(ots::OpenTypeFile* file, + const uint8_t* data, size_t data_size, uint8_t* dst, size_t dst_size, uint8_t* loca_buf, size_t loca_size) { static const int kNumSubStreams = 7; - ots::Buffer file(data, data_size); + ots::Buffer buffer(data, data_size); uint32_t version; std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams); - if (!file.ReadU32(&version)) { - return OTS_FAILURE(); + if (!buffer.ReadU32(&version)) { + return OTS_FAILURE_MSG("Failed to read 'version' of transformed 'glyf' table"); } uint16_t num_glyphs; + if (!buffer.ReadU16(&num_glyphs)) { + return OTS_FAILURE_MSG("Failed to read 'numGlyphs' from transformed 'glyf' table"); + } uint16_t index_format; - if (!file.ReadU16(&num_glyphs) || - !file.ReadU16(&index_format)) { - return OTS_FAILURE(); + if (!buffer.ReadU16(&index_format)) { + return OTS_FAILURE_MSG("Failed to read 'indexFormat' from transformed 'glyf' table"); } unsigned int offset = (2 + kNumSubStreams) * 4; if (offset > data_size) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Size of transformed 'glyf' table is too small to fit its data"); } // Invariant from here on: data_size >= offset for (int i = 0; i < kNumSubStreams; ++i) { uint32_t substream_size; - if (!file.ReadU32(&substream_size)) { - return OTS_FAILURE(); + if (!buffer.ReadU32(&substream_size)) { + return OTS_FAILURE_MSG("Failed to read substream size %d of transformed 'glyf' table", i); } if (substream_size > data_size - offset) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Size of substream %d of transformed 'glyf' table does not fit in table size"); } substreams.at(i) = std::make_pair(data + offset, substream_size); offset += substream_size; } ots::Buffer n_contour_stream(substreams.at(0).first, substreams.at(0).second); ots::Buffer n_points_stream(substreams.at(1).first, substreams.at(1).second); ots::Buffer flag_stream(substreams.at(2).first, substreams.at(2).second); ots::Buffer glyph_stream(substreams.at(3).first, substreams.at(3).second); @@ -555,56 +563,56 @@ bool ReconstructGlyf(const uint8_t* data loca_values.reserve(num_glyphs + 1); std::vector<uint16_t> n_points_vec; std::vector<Point> points; uint32_t loca_offset = 0; for (unsigned int i = 0; i < num_glyphs; ++i) { size_t glyph_size = 0; uint16_t n_contours = 0; if (!n_contour_stream.ReadU16(&n_contours)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Filed to read 'numberOfContours' of glyph %d from transformed 'glyf' table", i); } uint8_t* glyf_dst = dst + loca_offset; size_t glyf_dst_size = dst_size - loca_offset; if (n_contours == 0xffff) { // composite glyph bool have_instructions = false; uint16_t instruction_size = 0; if (!ProcessComposite(&composite_stream, glyf_dst, glyf_dst_size, &glyph_size, &have_instructions)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Filed to process composite glyph %d from transformed 'glyf' table", i); } if (have_instructions) { if (!Read255UShort(&glyph_stream, &instruction_size)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to read 'instructionLength' of glyph %d from transformed 'glyf' table", i); } // No integer overflow here (instruction_size < 2^16). if (instruction_size + 2U > glyf_dst_size - glyph_size) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("'instructionLength' of glyph %d from transformed 'glyf' table does not fit in the destination glyph size", i); } StoreU16(glyf_dst, glyph_size, instruction_size); if (!instruction_stream.Read(glyf_dst + glyph_size + 2, instruction_size)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Filed to read instructions of glyph %d from transformed 'glyf' table", i); } glyph_size += instruction_size + 2; } } else if (n_contours > 0) { // simple glyph n_points_vec.clear(); points.clear(); uint32_t total_n_points = 0; uint16_t n_points_contour; for (uint32_t j = 0; j < n_contours; ++j) { if (!Read255UShort(&n_points_stream, &n_points_contour)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Filed to read number of points of contour %d of glyph %d from transformed 'glyf' table", j, i); } n_points_vec.push_back(n_points_contour); if (total_n_points + n_points_contour < total_n_points) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Negative number of points of contour %d of glyph %d from transformed 'glyf' table", j, i); } total_n_points += n_points_contour; } uint32_t flag_size = total_n_points; if (flag_size > flag_stream.length() - flag_stream.offset()) { return OTS_FAILURE(); } const uint8_t* flags_buf = flag_stream.buffer() + flag_stream.offset(); @@ -649,17 +657,17 @@ bool ReconstructGlyf(const uint8_t* data } uint8_t* instruction_dst = glyf_dst + header_and_endpts_contours_size; StoreU16(instruction_dst, 0, instruction_size); if (!instruction_stream.Read(instruction_dst + 2, instruction_size)) { return OTS_FAILURE(); } if (!StorePoints(points, n_contours, instruction_size, glyf_dst, glyf_dst_size, &glyph_size)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to store points of glyph %d from the transformed 'glyf' table", i); } } else { glyph_size = 0; } loca_values.push_back(loca_offset); if (glyph_size + 3 < glyph_size) { return OTS_FAILURE(); } @@ -670,17 +678,17 @@ bool ReconstructGlyf(const uint8_t* data return OTS_FAILURE(); } loca_offset += glyph_size; } loca_values.push_back(loca_offset); assert(loca_values.size() == static_cast<size_t>(num_glyphs + 1)); if (!ProcessBboxStream(&bbox_stream, num_glyphs, loca_values, dst, dst_size)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Filed to process 'bboxStream' from the transformed 'glyf' table"); } return StoreLoca(loca_values, index_format, loca_buf, loca_size); } // This is linear search, but could be changed to binary because we // do have a guarantee that the tables are sorted by tag. But the total // cpu time is expected to be very small in any case. const Table* FindTable(const std::vector<Table>& tables, uint32_t tag) { @@ -688,34 +696,35 @@ const Table* FindTable(const std::vector for (size_t i = 0; i < n_tables; ++i) { if (tables.at(i).tag == tag) { return &tables.at(i); } } return NULL; } -bool ReconstructTransformed(const std::vector<Table>& tables, uint32_t tag, +bool ReconstructTransformed(ots::OpenTypeFile* file, + const std::vector<Table>& tables, uint32_t tag, const uint8_t* transformed_buf, size_t transformed_size, uint8_t* dst, size_t dst_length) { if (tag == TAG('g', 'l', 'y', 'f')) { const Table* glyf_table = FindTable(tables, tag); const Table* loca_table = FindTable(tables, TAG('l', 'o', 'c', 'a')); if (glyf_table == NULL || loca_table == NULL) { return OTS_FAILURE(); } if (static_cast<uint64_t>(glyf_table->dst_offset) + glyf_table->dst_length > dst_length) { return OTS_FAILURE(); } if (static_cast<uint64_t>(loca_table->dst_offset) + loca_table->dst_length > dst_length) { return OTS_FAILURE(); } - return ReconstructGlyf(transformed_buf, transformed_size, + return ReconstructGlyf(file, transformed_buf, transformed_size, dst + glyf_table->dst_offset, glyf_table->dst_length, dst + loca_table->dst_offset, loca_table->dst_length); } else if (tag == TAG('l', 'o', 'c', 'a')) { // processing was already done by glyf table, but validate if (!FindTable(tables, TAG('g', 'l', 'y', 'f'))) { return OTS_FAILURE(); } } else { @@ -769,17 +778,17 @@ bool Woff2Uncompress(uint8_t* dst_buf, s int ok = BrotliDecompressBuffer(src_size, src_buf, &uncompressed_size, dst_buf); if (!ok || uncompressed_size != dst_size) { return OTS_FAILURE(); } return true; } -bool ReadShortDirectory(ots::OpenTypeFile* file, +bool ReadTableDirectory(ots::OpenTypeFile* file, ots::Buffer* buffer, std::vector<Table>* tables, size_t num_tables) { for (size_t i = 0; i < num_tables; ++i) { Table* table = &tables->at(i); uint8_t flag_byte; if (!buffer->ReadU8(&flag_byte)) { return OTS_FAILURE_MSG("Failed to read the flags of table directory entry %d", i); } @@ -798,28 +807,28 @@ bool ReadShortDirectory(ots::OpenTypeFil uint32_t flags = 0; // Always transform the glyf and loca tables if (tag == TAG('g', 'l', 'y', 'f') || tag == TAG('l', 'o', 'c', 'a')) { flags |= kWoff2FlagsTransform; } uint32_t dst_length; if (!ReadBase128(buffer, &dst_length)) { - return OTS_FAILURE_MSG("Failed to read \"origLength\" for table %4.4s", (char*)&tag); + return OTS_FAILURE_MSG("Failed to read 'origLength' for table '%c%c%c%c'", CHR(tag)); } uint32_t transform_length = dst_length; if ((flags & kWoff2FlagsTransform) != 0) { if (!ReadBase128(buffer, &transform_length)) { - return OTS_FAILURE_MSG("Failed to read \"transformLength\" for table %4.4s", (char*)&tag); + return OTS_FAILURE_MSG("Failed to read 'transformLength' for table '%c%c%c%c'", CHR(tag)); } } // Disallow huge numbers (> 1GB) for sanity. if (transform_length > 1024 * 1024 * 1024 || dst_length > 1024 * 1024 * 1024) { - return OTS_FAILURE_MSG("\"origLength\" or \"transformLength\" > 1GB"); + return OTS_FAILURE_MSG("'origLength' or 'transformLength' > 1GB"); } table->tag = tag; table->flags = flags; table->transform_length = transform_length; table->dst_length = dst_length; } return true; } @@ -834,64 +843,96 @@ size_t ComputeWOFF2FinalSize(const uint8 if (!file.Skip(16) || !file.ReadU32(&total_length)) { return 0; } return total_length; } -bool ConvertWOFF2ToTTF(ots::OpenTypeFile* file, - uint8_t* result, size_t result_length, - const uint8_t* data, size_t length) { +bool ConvertWOFF2ToSFNT(ots::OpenTypeFile* file, + uint8_t* result, size_t result_length, + const uint8_t* data, size_t length) { static const uint32_t kWoff2Signature = 0x774f4632; // "wOF2" ots::Buffer buffer(data, length); uint32_t signature; uint32_t flavor = 0; if (!buffer.ReadU32(&signature) || signature != kWoff2Signature || !buffer.ReadU32(&flavor)) { - return OTS_FAILURE_MSG("Failed to read \"signature\" or \"flavor\", or not WOFF2 signature"); + return OTS_FAILURE_MSG("Failed to read 'signature' or 'flavor', or not WOFF2 signature"); } if (!IsValidVersionTag(ntohl(flavor))) { - return OTS_FAILURE_MSG("Invalid \"flavor\""); + return OTS_FAILURE_MSG("Invalid 'flavor'"); } uint32_t reported_length; if (!buffer.ReadU32(&reported_length) || length != reported_length) { - return OTS_FAILURE_MSG("Failed to read \"length\" or it does not match the actual file size"); + return OTS_FAILURE_MSG("Failed to read 'length' or it does not match the actual file size"); } uint16_t num_tables; if (!buffer.ReadU16(&num_tables) || !num_tables) { - return OTS_FAILURE_MSG("Failed to read \"numTables\""); + return OTS_FAILURE_MSG("Failed to read 'numTables'"); } + + uint16_t reserved_value; + if (!buffer.ReadU16(&reserved_value)) { + return OTS_FAILURE_MSG("Failed to read 'reserved' field"); + } + // We don't care about these fields of the header: - // uint16_t reserved - // uint32_t total_sfnt_size - if (!buffer.Skip(6)) { - return OTS_FAILURE_MSG("Failed to read \"reserve\" or \"totalSfntSize\""); + // uint32_t total_sfnt_size, the caller already passes it as result_length + if (!buffer.Skip(4)) { + return OTS_FAILURE_MSG("Failed to read 'totalSfntSize'"); } uint32_t compressed_length; if (!buffer.ReadU32(&compressed_length)) { - return OTS_FAILURE_MSG("Failed to read \"totalCompressedSize\""); + return OTS_FAILURE_MSG("Failed to read 'totalCompressedSize'"); } if (compressed_length > std::numeric_limits<uint32_t>::max()) { return OTS_FAILURE(); } // We don't care about these fields of the header: // uint16_t major_version, minor_version - // uint32_t meta_offset, meta_length, meta_orig_length - // uint32_t priv_offset, priv_length - if (!buffer.Skip(24)) { - return OTS_FAILURE(); + if (!buffer.Skip(2 * 2)) { + return OTS_FAILURE_MSG("Failed to read 'majorVersion' or 'minorVersion'"); + } + + // Checks metadata block size. + uint32_t meta_offset; + uint32_t meta_length; + uint32_t meta_length_orig; + if (!buffer.ReadU32(&meta_offset) || + !buffer.ReadU32(&meta_length) || + !buffer.ReadU32(&meta_length_orig)) { + return OTS_FAILURE_MSG("Failed to read header metadata block fields"); } + if (meta_offset) { + if (meta_offset >= length || length - meta_offset < meta_length) { + return OTS_FAILURE_MSG("Invalid metadata block offset or length"); + } + } + + // Checks private data block size. + uint32_t priv_offset; + uint32_t priv_length; + if (!buffer.ReadU32(&priv_offset) || + !buffer.ReadU32(&priv_length)) { + return OTS_FAILURE_MSG("Failed to read header private block fields"); + } + if (priv_offset) { + if (priv_offset >= length || length - priv_offset < priv_length) { + return OTS_FAILURE_MSG("Invalid private block offset or length"); + } + } + std::vector<Table> tables(num_tables); - if (!ReadShortDirectory(file, &buffer, &tables, num_tables)) { + if (!ReadTableDirectory(file, &buffer, &tables, num_tables)) { return OTS_FAILURE_MSG("Failed to read table directory"); } uint64_t compressed_offset = buffer.offset(); if (compressed_offset > std::numeric_limits<uint32_t>::max()) { return OTS_FAILURE(); } uint64_t dst_offset = kSfntHeaderSize + kSfntEntrySize * static_cast<uint64_t>(num_tables); @@ -899,39 +940,72 @@ bool ConvertWOFF2ToTTF(ots::OpenTypeFile Table* table = &tables.at(i); table->dst_offset = static_cast<uint32_t>(dst_offset); dst_offset += table->dst_length; if (dst_offset > std::numeric_limits<uint32_t>::max()) { return OTS_FAILURE(); } dst_offset = ots::Round4(dst_offset); } - if (ots::Round4(compressed_offset + compressed_length) > length || dst_offset > result_length) { - return OTS_FAILURE(); + + uint64_t block_end = ots::Round4(compressed_offset + compressed_length); + if (block_end > length || dst_offset != result_length) { + return OTS_FAILURE_MSG("Uncompressed sfnt size mismatch"); } const uint32_t sfnt_header_and_table_directory_size = 12 + 16 * num_tables; if (sfnt_header_and_table_directory_size > result_length) { return OTS_FAILURE(); } + if (meta_offset) { + if (block_end != meta_offset) { + return OTS_FAILURE_MSG("Invalid metadata block offset"); + } + block_end = ots::Round4(static_cast<uint64_t>(meta_offset) + + static_cast<uint64_t>(meta_length)); + if (block_end > std::numeric_limits<uint32_t>::max()) { + return OTS_FAILURE_MSG("Invalid metadata block length"); + } + } + + if (priv_offset) { + if (block_end != priv_offset) { + return OTS_FAILURE_MSG("Invalid private block offset"); + } + block_end = ots::Round4(static_cast<uint64_t>(priv_offset) + + static_cast<uint64_t>(priv_length)); + if (block_end > std::numeric_limits<uint32_t>::max()) { + return OTS_FAILURE_MSG("Invalid private block length"); + } + } + + if (block_end != ots::Round4(length)) { + return OTS_FAILURE_MSG("File length mismatch (trailing junk?)"); + } + // Start building the font size_t offset = 0; offset = StoreU32(result, offset, flavor); offset = StoreU16(result, offset, num_tables); uint8_t max_pow2 = 0; while (1u << (max_pow2 + 1) <= num_tables) { max_pow2++; } const uint16_t output_search_range = (1u << max_pow2) << 4; offset = StoreU16(result, offset, output_search_range); offset = StoreU16(result, offset, max_pow2); offset = StoreU16(result, offset, (num_tables << 4) - output_search_range); + + // sort tags in the table directory in ascending alphabetical order + std::vector<Table> sorted_tables(tables); + std::sort(sorted_tables.begin(), sorted_tables.end()); + for (uint16_t i = 0; i < num_tables; ++i) { - const Table* table = &tables.at(i); + const Table* table = &sorted_tables.at(i); offset = StoreU32(result, offset, table->tag); offset = StoreU32(result, offset, 0); // checksum, to fill in later offset = StoreU32(result, offset, table->dst_offset); offset = StoreU32(result, offset, table->dst_length); } std::vector<uint8_t> uncompressed_buf; const uint8_t* transform_buf = NULL; uint64_t total_size = 0; @@ -946,17 +1020,17 @@ bool ConvertWOFF2ToTTF(ots::OpenTypeFile if (total_size > 30 * 1024 * 1024) { return OTS_FAILURE(); } const size_t total_size_size_t = static_cast<size_t>(total_size); uncompressed_buf.resize(total_size_size_t); const uint8_t* src_buf = data + compressed_offset; if (!Woff2Uncompress(&uncompressed_buf[0], total_size_size_t, src_buf, compressed_length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to uncompress font data"); } transform_buf = &uncompressed_buf[0]; for (uint16_t i = 0; i < num_tables; ++i) { const Table* table = &tables.at(i); uint32_t flags = table->flags; size_t transform_length = table->transform_length; @@ -966,26 +1040,26 @@ bool ConvertWOFF2ToTTF(ots::OpenTypeFile } if (static_cast<uint64_t>(table->dst_offset) + transform_length > result_length) { return OTS_FAILURE(); } std::memcpy(result + table->dst_offset, transform_buf, transform_length); } else { - if (!ReconstructTransformed(tables, table->tag, + if (!ReconstructTransformed(file, tables, table->tag, transform_buf, transform_length, result, result_length)) { - return OTS_FAILURE(); + return OTS_FAILURE_MSG("Failed to reconstruct '%c%c%c%c' table", CHR(table->tag)); } } transform_buf += transform_length; if (transform_buf > &uncompressed_buf[0] + uncompressed_buf.size()) { return OTS_FAILURE(); } } - return FixChecksums(tables, result); + return FixChecksums(sorted_tables, result); } } // namespace ots #undef TABLE_NAME
--- a/gfx/ots/src/woff2.h +++ b/gfx/ots/src/woff2.h @@ -8,13 +8,13 @@ namespace ots { // Compute the size of the final uncompressed font, or 0 on error. size_t ComputeWOFF2FinalSize(const uint8_t *data, size_t length); // Decompresses the font into the target buffer. The result_length should // be the same as determined by ComputeFinalSize(). Returns true on successful // decompression. -bool ConvertWOFF2ToTTF(OpenTypeFile *file, uint8_t *result, size_t result_length, - const uint8_t *data, size_t length); +bool ConvertWOFF2ToSFNT(OpenTypeFile *file, uint8_t *result, size_t result_length, + const uint8_t *data, size_t length); } #endif // OTS_WOFF2_H_
--- a/gfx/thebes/gfxUserFontSet.cpp +++ b/gfx/thebes/gfxUserFontSet.cpp @@ -735,21 +735,16 @@ gfxUserFontEntry::FontDataDownloadComple gfxUserFontSet::gfxUserFontSet() : mFontFamilies(4), mLocalRulesUsed(false) { IncrementGeneration(true); gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList(); if (fp) { fp->AddUserFontSet(this); } - - // This is a one-time global switch for OTS. However, as long as we use - // a preference to control the availability of WOFF2 support, we will - // not actually pass any WOFF2 data to OTS unless the pref is on. - ots::EnableWOFF2(); } gfxUserFontSet::~gfxUserFontSet() { gfxPlatformFontList* fp = gfxPlatformFontList::PlatformFontList(); if (fp) { fp->RemoveUserFontSet(this); }