| Index: third_party/ots/src/woff2.cc
|
| diff --git a/third_party/ots/src/woff2.cc b/third_party/ots/src/woff2.cc
|
| index 54ab6259bbbc6099dc2947303d7321883ba733d7..b244aeca323fe8f866d0cf9d1dfc5c6b998f73bc 100644
|
| --- a/third_party/ots/src/woff2.cc
|
| +++ b/third_party/ots/src/woff2.cc
|
| @@ -45,6 +45,7 @@ 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;
|
|
|
| @@ -135,6 +136,10 @@ struct Table {
|
| 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
|
| @@ -509,35 +514,38 @@ bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format,
|
| }
|
|
|
| // 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;
|
| @@ -560,7 +568,7 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size,
|
| 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;
|
| @@ -570,20 +578,20 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size,
|
| 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;
|
| }
|
| @@ -595,11 +603,11 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size,
|
| 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;
|
| }
|
| @@ -654,7 +662,7 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size,
|
| }
|
| 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;
|
| @@ -675,7 +683,7 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size,
|
| 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);
|
| }
|
| @@ -693,7 +701,8 @@ const Table* FindTable(const std::vector<Table>& tables, uint32_t tag) {
|
| 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')) {
|
| @@ -710,7 +719,7 @@ bool ReconstructTransformed(const std::vector<Table>& tables, uint32_t tag,
|
| 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')) {
|
| @@ -803,18 +812,18 @@ bool ReadTableDirectory(ots::OpenTypeFile* file,
|
| }
|
| 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;
|
| @@ -839,9 +848,9 @@ size_t ComputeWOFF2FinalSize(const uint8_t* data, size_t length) {
|
| 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);
|
|
|
| @@ -849,30 +858,35 @@ bool ConvertWOFF2ToTTF(ots::OpenTypeFile* file,
|
| 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();
|
| @@ -880,11 +894,38 @@ bool ConvertWOFF2ToTTF(ots::OpenTypeFile* file,
|
|
|
| // 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 (!ReadTableDirectory(file, &buffer, &tables, num_tables)) {
|
| return OTS_FAILURE_MSG("Failed to read table directory");
|
| @@ -904,8 +945,10 @@ bool ConvertWOFF2ToTTF(ots::OpenTypeFile* file,
|
| }
|
| 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;
|
| @@ -913,6 +956,32 @@ bool ConvertWOFF2ToTTF(ots::OpenTypeFile* file,
|
| 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);
|
| @@ -925,8 +994,13 @@ bool ConvertWOFF2ToTTF(ots::OpenTypeFile* file,
|
| 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);
|
| @@ -951,7 +1025,7 @@ bool ConvertWOFF2ToTTF(ots::OpenTypeFile* file,
|
| 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];
|
|
|
| @@ -971,9 +1045,9 @@ bool ConvertWOFF2ToTTF(ots::OpenTypeFile* file,
|
| 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));
|
| }
|
| }
|
|
|
| @@ -983,7 +1057,7 @@ bool ConvertWOFF2ToTTF(ots::OpenTypeFile* file,
|
| }
|
| }
|
|
|
| - return FixChecksums(tables, result);
|
| + return FixChecksums(sorted_tables, result);
|
| }
|
|
|
| } // namespace ots
|
|
|