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 |