From ab2c050f10cbe03cb00a5ac9ed829c76f2e6f32d Mon Sep 17 00:00:00 2001 From: Jonathan Kew Date: Mon, 12 Dec 2022 15:04:07 +0000 Subject: [PATCH] Bug 1466443 - Update WOFF2 library to upstream tip, to pick up overlapSimpleBitmap support. r=gfx-reviewers,lsalzman Differential Revision: https://phabricator.services.mozilla.com/D164324 --- modules/woff2/README.mozilla | 2 +- modules/woff2/include/woff2/encode.h | 4 +- modules/woff2/include/woff2/output.h | 4 +- modules/woff2/src/file.h | 14 ++---- modules/woff2/src/glyph.cc | 15 ++++-- modules/woff2/src/glyph.h | 12 ++++- modules/woff2/src/transform.cc | 20 +++++++- modules/woff2/src/woff2_compress.cc | 10 ++-- modules/woff2/src/woff2_dec.cc | 66 +++++++++++++++++++++------ modules/woff2/src/woff2_decompress.cc | 14 +++--- modules/woff2/src/woff2_enc.cc | 8 +--- modules/woff2/src/woff2_info.cc | 8 ++-- modules/woff2/src/woff2_out.cc | 8 +--- 13 files changed, 122 insertions(+), 63 deletions(-) diff --git a/modules/woff2/README.mozilla b/modules/woff2/README.mozilla index bd35dc45209e..eaa5a0ae7f63 100644 --- a/modules/woff2/README.mozilla +++ b/modules/woff2/README.mozilla @@ -11,6 +11,6 @@ The in-tree copy is updated by running sh update.sh from within the modules/woff2 directory. -Current version: [commit 1bccf208bca986e53a647dfe4811322adb06ecf8]. +Current version: [commit 4721483ad780ee2b63cb787bfee4aa64b61a0446]. Additional patch: woff2-rlbox.patch (bug 1732201). diff --git a/modules/woff2/include/woff2/encode.h b/modules/woff2/include/woff2/encode.h index 34b772297495..2ac563032d1d 100644 --- a/modules/woff2/include/woff2/encode.h +++ b/modules/woff2/include/woff2/encode.h @@ -26,8 +26,8 @@ struct WOFF2Params { // Returns an upper bound on the size of the compressed file. size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length); -size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length, - const std::string& extended_metadata); +size_t MaxWOFF2CompressedSize(const uint8_t *data, size_t length, + const std::string &extended_metadata); // Compresses the font into the target buffer. *result_length should be at least // the value returned by MaxWOFF2CompressedSize(), upon return, it is set to the diff --git a/modules/woff2/include/woff2/output.h b/modules/woff2/include/woff2/output.h index c325f67be728..dc78ccf82679 100644 --- a/modules/woff2/include/woff2/output.h +++ b/modules/woff2/include/woff2/output.h @@ -51,7 +51,7 @@ class WOFF2StringOut : public WOFF2Out { // Create a writer that writes its data to buf. // buf->size() will grow to at most max_size // buf may be sized (e.g. using EstimateWOFF2FinalSize) or empty. - explicit WOFF2StringOut(std::string* buf); + explicit WOFF2StringOut(std::string *buf); bool Write(const void *buf, size_t n) override; bool Write(const void *buf, size_t offset, size_t n) override; @@ -59,7 +59,7 @@ class WOFF2StringOut : public WOFF2Out { size_t MaxSize() { return max_size_; } void SetMaxSize(size_t max_size); private: - std::string* buf_; + std::string *buf_; size_t max_size_; size_t offset_; }; diff --git a/modules/woff2/src/file.h b/modules/woff2/src/file.h index 61318776be3b..70ea7a7fb1b9 100644 --- a/modules/woff2/src/file.h +++ b/modules/woff2/src/file.h @@ -14,18 +14,14 @@ namespace woff2 { -using std::string; - - -inline string GetFileContent(string filename) { +inline std::string GetFileContent(std::string filename) { std::ifstream ifs(filename.c_str(), std::ios::binary); - return string( - std::istreambuf_iterator(ifs.rdbuf()), - std::istreambuf_iterator()); + return std::string(std::istreambuf_iterator(ifs.rdbuf()), + std::istreambuf_iterator()); } -inline void SetFileContents(string filename, string::iterator start, - string::iterator end) { +inline void SetFileContents(std::string filename, std::string::iterator start, + std::string::iterator end) { std::ofstream ofs(filename.c_str(), std::ios::binary); std::copy(start, end, std::ostream_iterator(ofs)); } diff --git a/modules/woff2/src/glyph.cc b/modules/woff2/src/glyph.cc index 057174de2193..5b4948679c76 100644 --- a/modules/woff2/src/glyph.cc +++ b/modules/woff2/src/glyph.cc @@ -21,6 +21,7 @@ static const int32_t kFLAG_YSHORT = 1 << 2; static const int32_t kFLAG_REPEAT = 1 << 3; static const int32_t kFLAG_XREPEATSIGN = 1 << 4; static const int32_t kFLAG_YREPEATSIGN = 1 << 5; +static const int32_t kFLAG_OVERLAP_SIMPLE = 1 << 6; static const int32_t kFLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0; static const int32_t kFLAG_WE_HAVE_A_SCALE = 1 << 3; static const int32_t kFLAG_MORE_COMPONENTS = 1 << 5; @@ -134,6 +135,10 @@ bool ReadGlyph(const uint8_t* data, size_t len, Glyph* glyph) { } } + if (!flags.empty() && !flags[0].empty()) { + glyph->overlap_simple_flag_set = (flags[0][0] & kFLAG_OVERLAP_SIMPLE); + } + // Read the x coordinates. int prev_x = 0; for (int i = 0; i < num_contours; ++i) { @@ -239,7 +244,7 @@ bool StoreEndPtsOfContours(const Glyph& glyph, size_t* offset, uint8_t* dst) { bool StorePoints(const Glyph& glyph, size_t* offset, uint8_t* dst, size_t dst_size) { - int last_flag = -1; + int previous_flag = -1; int repeat_count = 0; int last_x = 0; int last_y = 0; @@ -250,6 +255,10 @@ bool StorePoints(const Glyph& glyph, size_t* offset, for (const auto& contour : glyph.contours) { for (const auto& point : contour) { int flag = point.on_curve ? kFLAG_ONCURVE : 0; + if (previous_flag == -1 && glyph.overlap_simple_flag_set) { + // First flag needs to have overlap simple bit set. + flag = flag | kFLAG_OVERLAP_SIMPLE; + } int dx = point.x - last_x; int dy = point.y - last_y; if (dx == 0) { @@ -268,7 +277,7 @@ bool StorePoints(const Glyph& glyph, size_t* offset, } else { y_bytes += 2; } - if (flag == last_flag && repeat_count != 255) { + if (flag == previous_flag && repeat_count != 255) { dst[*offset - 1] |= kFLAG_REPEAT; repeat_count++; } else { @@ -286,7 +295,7 @@ bool StorePoints(const Glyph& glyph, size_t* offset, } last_x = point.x; last_y = point.y; - last_flag = flag; + previous_flag = flag; } } if (repeat_count != 0) { diff --git a/modules/woff2/src/glyph.h b/modules/woff2/src/glyph.h index f24056f4c8c8..c38eb5f2bc46 100644 --- a/modules/woff2/src/glyph.h +++ b/modules/woff2/src/glyph.h @@ -10,8 +10,10 @@ #ifndef WOFF2_GLYPH_H_ #define WOFF2_GLYPH_H_ -#include #include +#include + +#include #include namespace woff2 { @@ -22,7 +24,10 @@ namespace woff2 { // is around. class Glyph { public: - Glyph() : instructions_size(0), composite_data_size(0) {} + Glyph() + : instructions_size(0), + overlap_simple_flag_set(false), + composite_data_size(0) {} // Bounding box. int16_t x_min; @@ -34,6 +39,9 @@ class Glyph { uint16_t instructions_size; const uint8_t* instructions_data; + // Flags. + bool overlap_simple_flag_set; + // Data model for simple glyphs. struct Point { int x; diff --git a/modules/woff2/src/transform.cc b/modules/woff2/src/transform.cc index 999bef37458e..1016efcd2bb0 100644 --- a/modules/woff2/src/transform.cc +++ b/modules/woff2/src/transform.cc @@ -22,6 +22,7 @@ namespace { const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0; const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; +const int FLAG_OVERLAP_SIMPLE_BITMAP = 1 << 0; void WriteBytes(std::vector* out, const uint8_t* data, size_t len) { if (len == 0) return; @@ -69,7 +70,10 @@ class GlyfEncoder { } void GetTransformedGlyfBytes(std::vector* result) { - WriteLong(result, 0); // version + WriteUShort(result, 0); // Version + WriteUShort(result, overlap_bitmap_.empty() + ? 0x00 + : FLAG_OVERLAP_SIMPLE_BITMAP); // Flags WriteUShort(result, n_glyphs_); WriteUShort(result, 0); // index_format, will be set later WriteLong(result, n_contour_stream_.size()); @@ -87,6 +91,9 @@ class GlyfEncoder { WriteBytes(result, bbox_bitmap_); WriteBytes(result, bbox_stream_); WriteBytes(result, instruction_stream_); + if (!overlap_bitmap_.empty()) { + WriteBytes(result, overlap_bitmap_); + } } private: @@ -127,6 +134,10 @@ class GlyfEncoder { } void WriteSimpleGlyph(int glyph_id, const Glyph& glyph) { + if (glyph.overlap_simple_flag_set) { + EnsureOverlapBitmap(); + overlap_bitmap_[glyph_id >> 3] |= 0x80 >> (glyph_id & 7); + } int num_contours = glyph.contours.size(); WriteUShort(&n_contour_stream_, num_contours); if (ShouldWriteSimpleGlyphBbox(glyph)) { @@ -214,6 +225,12 @@ class GlyfEncoder { } } + void EnsureOverlapBitmap() { + if (overlap_bitmap_.empty()) { + overlap_bitmap_.resize((n_glyphs_ + 7) >> 3); + } + } + std::vector n_contour_stream_; std::vector n_points_stream_; std::vector flag_byte_stream_; @@ -222,6 +239,7 @@ class GlyfEncoder { std::vector bbox_stream_; std::vector glyph_stream_; std::vector instruction_stream_; + std::vector overlap_bitmap_; int n_glyphs_; }; diff --git a/modules/woff2/src/woff2_compress.cc b/modules/woff2/src/woff2_compress.cc index 80e310866764..36e8fcafe9c0 100644 --- a/modules/woff2/src/woff2_compress.cc +++ b/modules/woff2/src/woff2_compress.cc @@ -13,22 +13,20 @@ int main(int argc, char **argv) { - using std::string; - if (argc != 2) { fprintf(stderr, "One argument, the input filename, must be provided.\n"); return 1; } - string filename(argv[1]); - string outfilename = filename.substr(0, filename.find_last_of(".")) + ".woff2"; + std::string filename(argv[1]); + std::string outfilename = filename.substr(0, filename.find_last_of(".")) + ".woff2"; fprintf(stdout, "Processing %s => %s\n", filename.c_str(), outfilename.c_str()); - string input = woff2::GetFileContent(filename); + std::string input = woff2::GetFileContent(filename); const uint8_t* input_data = reinterpret_cast(input.data()); size_t output_size = woff2::MaxWOFF2CompressedSize(input_data, input.size()); - string output(output_size, 0); + std::string output(output_size, 0); uint8_t* output_data = reinterpret_cast(&output[0]); woff2::WOFF2Params params; diff --git a/modules/woff2/src/woff2_dec.cc b/modules/woff2/src/woff2_dec.cc index 50806d7d19cc..f1f98cb51a7f 100644 --- a/modules/woff2/src/woff2_dec.cc +++ b/modules/woff2/src/woff2_dec.cc @@ -33,10 +33,6 @@ namespace woff2 { namespace { -using std::string; -using std::vector; - - // simple glyph flags const int kGlyfOnCurve = 1 << 0; const int kGlyfXShort = 1 << 1; @@ -44,6 +40,7 @@ const int kGlyfYShort = 1 << 2; const int kGlyfRepeat = 1 << 3; const int kGlyfThisXIsSame = 1 << 4; const int kGlyfThisYIsSame = 1 << 5; +const int kOverlapSimple = 1 << 6; // composite glyph flags // See CompositeGlyph.java in sfntly for full definitions @@ -54,6 +51,9 @@ const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6; const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7; const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; +// glyf flags +const int FLAG_OVERLAP_SIMPLE_BITMAP = 1 << 0; + const size_t kCheckSumAdjustmentOffset = 8; const size_t kEndPtsOfContoursOffset = 10; @@ -112,6 +112,16 @@ int WithSign(int flag, int baseval) { return (flag & 1) ? baseval : -baseval; } +bool _SafeIntAddition(int a, int b, int* result) { + if (PREDICT_FALSE( + ((a > 0) && (b > std::numeric_limits::max() - a)) || + ((a < 0) && (b < std::numeric_limits::min() - a)))) { + return false; + } + *result = a + b; + return true; +} + bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, unsigned int n_points, Point* result, size_t* in_bytes_consumed) { int x = 0; @@ -167,9 +177,12 @@ bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, (in[triplet_index + 2] << 8) + in[triplet_index + 3]); } triplet_index += n_data_bytes; - // Possible overflow but coordinate values are not security sensitive - x += dx; - y += dy; + if (!_SafeIntAddition(x, dx, &x)) { + return false; + } + if (!_SafeIntAddition(y, dy, &y)) { + return false; + } *result++ = {x, y, on_curve}; } *in_bytes_consumed = triplet_index; @@ -179,8 +192,9 @@ bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, // This function stores just the point data. On entry, dst points to the // beginning of a simple glyph. Returns true on success. bool StorePoints(unsigned int n_points, const Point* points, - unsigned int n_contours, unsigned int instruction_length, - uint8_t* dst, size_t dst_size, size_t* glyph_size) { + unsigned int n_contours, unsigned int instruction_length, + bool has_overlap_bit, uint8_t* dst, size_t dst_size, + size_t* glyph_size) { // I believe that n_contours < 65536, in which case this is safe. However, a // comment and/or an assert would be good. unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 + @@ -195,6 +209,10 @@ bool StorePoints(unsigned int n_points, const Point* points, for (unsigned int i = 0; i < n_points; ++i) { const Point& point = points[i]; int flag = point.on_curve ? kGlyfOnCurve : 0; + if (has_overlap_bit && i == 0) { + flag |= kOverlapSimple; + } + int dx = point.x - last_x; int dy = point.y - last_y; if (dx == 0) { @@ -392,13 +410,20 @@ bool ReconstructGlyf(const uint8_t* data, Table* glyf_table, WOFF2Out* out) { static const int kNumSubStreams = 7; Buffer file(data, glyf_table->transform_length); - uint32_t version; + uint16_t version; std::vector > substreams(kNumSubStreams); const size_t glyf_start = out->Size(); - if (PREDICT_FALSE(!file.ReadU32(&version))) { + if (PREDICT_FALSE(!file.ReadU16(&version))) { return FONT_COMPRESSION_FAILURE(); } + + uint16_t flags; + if (PREDICT_FALSE(!file.ReadU16(&flags))) { + return FONT_COMPRESSION_FAILURE(); + } + bool has_overlap_bitmap = (flags & FLAG_OVERLAP_SIMPLE_BITMAP); + if (PREDICT_FALSE(!file.ReadU16(&info->num_glyphs) || !file.ReadU16(&info->index_format))) { return FONT_COMPRESSION_FAILURE(); @@ -436,6 +461,17 @@ bool ReconstructGlyf(const uint8_t* data, Table* glyf_table, Buffer bbox_stream(substreams[5].first, substreams[5].second); Buffer instruction_stream(substreams[6].first, substreams[6].second); + const uint8_t* overlap_bitmap = nullptr; + unsigned int overlap_bitmap_length = 0; + if (has_overlap_bitmap) { + overlap_bitmap_length = (info->num_glyphs + 7) >> 3; + overlap_bitmap = data + offset; + if (PREDICT_FALSE(overlap_bitmap_length > + glyf_table->transform_length - offset)) { + return FONT_COMPRESSION_FAILURE(); + } + } + std::vector loca_values(info->num_glyphs + 1); std::vector n_points_vec; std::unique_ptr points; @@ -589,8 +625,12 @@ bool ReconstructGlyf(const uint8_t* data, Table* glyf_table, } glyph_size += instruction_size; - if (PREDICT_FALSE(!StorePoints(total_n_points, points.get(), n_contours, - instruction_size, glyph_buf.get(), glyph_buf_size, &glyph_size))) { + bool has_overlap_bit = + has_overlap_bitmap && overlap_bitmap[i >> 3] & (0x80 >> (i & 7)); + + if (PREDICT_FALSE(!StorePoints( + total_n_points, points.get(), n_contours, instruction_size, + has_overlap_bit, glyph_buf.get(), glyph_buf_size, &glyph_size))) { return FONT_COMPRESSION_FAILURE(); } } else { diff --git a/modules/woff2/src/woff2_decompress.cc b/modules/woff2/src/woff2_decompress.cc index de088b9ec0f8..47394781ad3d 100644 --- a/modules/woff2/src/woff2_decompress.cc +++ b/modules/woff2/src/woff2_decompress.cc @@ -14,21 +14,21 @@ int main(int argc, char **argv) { - using std::string; - if (argc != 2) { fprintf(stderr, "One argument, the input filename, must be provided.\n"); return 1; } - string filename(argv[1]); - string outfilename = filename.substr(0, filename.find_last_of(".")) + ".ttf"; + std::string filename(argv[1]); + std::string outfilename = filename.substr(0, filename.find_last_of(".")) + ".ttf"; // Note: update woff2_dec_fuzzer_new_entry.cc if this pattern changes. - string input = woff2::GetFileContent(filename); + std::string input = woff2::GetFileContent(filename); const uint8_t* raw_input = reinterpret_cast(input.data()); - string output(std::min(woff2::ComputeWOFF2FinalSize(raw_input, input.size()), - woff2::kDefaultMaxSize), 0); + std::string output( + std::min(woff2::ComputeWOFF2FinalSize(raw_input, input.size()), + woff2::kDefaultMaxSize), + 0); woff2::WOFF2StringOut out(&output); const bool ok = woff2::ConvertWOFF2ToTTF(raw_input, input.size(), &out); diff --git a/modules/woff2/src/woff2_enc.cc b/modules/woff2/src/woff2_enc.cc index ec00878bcd64..f3f46e5759eb 100644 --- a/modules/woff2/src/woff2_enc.cc +++ b/modules/woff2/src/woff2_enc.cc @@ -28,13 +28,9 @@ namespace woff2 { + namespace { - -using std::string; -using std::vector; - - const size_t kWoff2HeaderSize = 48; const size_t kWoff2EntrySize = 20; @@ -183,7 +179,7 @@ size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length) { } size_t MaxWOFF2CompressedSize(const uint8_t* data, size_t length, - const string& extended_metadata) { + const std::string& extended_metadata) { // Except for the header size, which is 32 bytes larger in woff2 format, // all other parts should be smaller (table header in short format, // transformations and compression). Just to be sure, we will give some diff --git a/modules/woff2/src/woff2_info.cc b/modules/woff2/src/woff2_info.cc index 2b51adcb6221..b13230453700 100644 --- a/modules/woff2/src/woff2_info.cc +++ b/modules/woff2/src/woff2_info.cc @@ -29,18 +29,16 @@ std::string PrintTag(int tag) { } int main(int argc, char **argv) { - using std::string; - if (argc != 2) { fprintf(stderr, "One argument, the input filename, must be provided.\n"); return 1; } - string filename(argv[1]); - string outfilename = filename.substr(0, filename.find_last_of(".")) + ".woff2"; + std::string filename(argv[1]); + std::string outfilename = filename.substr(0, filename.find_last_of(".")) + ".woff2"; fprintf(stdout, "Processing %s => %s\n", filename.c_str(), outfilename.c_str()); - string input = woff2::GetFileContent(filename); + std::string input = woff2::GetFileContent(filename); woff2::Buffer file(reinterpret_cast(input.data()), input.size()); diff --git a/modules/woff2/src/woff2_out.cc b/modules/woff2/src/woff2_out.cc index 8ab32681f1d2..a22d3bf8bed2 100644 --- a/modules/woff2/src/woff2_out.cc +++ b/modules/woff2/src/woff2_out.cc @@ -8,14 +8,10 @@ #include -using std::string; - namespace woff2 { -WOFF2StringOut::WOFF2StringOut(string* buf) - : buf_(buf), - max_size_(kDefaultMaxSize), - offset_(0) {} +WOFF2StringOut::WOFF2StringOut(std::string *buf) + : buf_(buf), max_size_(kDefaultMaxSize), offset_(0) {} bool WOFF2StringOut::Write(const void *buf, size_t n) { return Write(buf, offset_, n);