Merge pull request #149 from garretrieger/overlap_bit

Add woff2 encoder/decoder support for overlapSimpleBitmap.
This commit is contained in:
rsheeter
2022-03-30 08:59:13 -07:00
committed by GitHub
12 changed files with 105 additions and 59 deletions
+2 -2
View File
@@ -26,8 +26,8 @@ struct WOFF2Params {
// Returns an upper bound on the size of the compressed file. // 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);
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); const std::string &extended_metadata);
// Compresses the font into the target buffer. *result_length should be at least // 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 // the value returned by MaxWOFF2CompressedSize(), upon return, it is set to the
+2 -2
View File
@@ -51,7 +51,7 @@ class WOFF2StringOut : public WOFF2Out {
// Create a writer that writes its data to buf. // Create a writer that writes its data to buf.
// buf->size() will grow to at most max_size // buf->size() will grow to at most max_size
// buf may be sized (e.g. using EstimateWOFF2FinalSize) or empty. // 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 n) override;
bool Write(const void *buf, size_t offset, 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_; } size_t MaxSize() { return max_size_; }
void SetMaxSize(size_t max_size); void SetMaxSize(size_t max_size);
private: private:
std::string* buf_; std::string *buf_;
size_t max_size_; size_t max_size_;
size_t offset_; size_t offset_;
}; };
+4 -8
View File
@@ -14,18 +14,14 @@
namespace woff2 { namespace woff2 {
using std::string; inline std::string GetFileContent(std::string filename) {
inline string GetFileContent(string filename) {
std::ifstream ifs(filename.c_str(), std::ios::binary); std::ifstream ifs(filename.c_str(), std::ios::binary);
return string( return std::string(std::istreambuf_iterator<char>(ifs.rdbuf()),
std::istreambuf_iterator<char>(ifs.rdbuf()),
std::istreambuf_iterator<char>()); std::istreambuf_iterator<char>());
} }
inline void SetFileContents(string filename, string::iterator start, inline void SetFileContents(std::string filename, std::string::iterator start,
string::iterator end) { std::string::iterator end) {
std::ofstream ofs(filename.c_str(), std::ios::binary); std::ofstream ofs(filename.c_str(), std::ios::binary);
std::copy(start, end, std::ostream_iterator<char>(ofs)); std::copy(start, end, std::ostream_iterator<char>(ofs));
} }
+12 -3
View File
@@ -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_REPEAT = 1 << 3;
static const int32_t kFLAG_XREPEATSIGN = 1 << 4; static const int32_t kFLAG_XREPEATSIGN = 1 << 4;
static const int32_t kFLAG_YREPEATSIGN = 1 << 5; 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_ARG_1_AND_2_ARE_WORDS = 1 << 0;
static const int32_t kFLAG_WE_HAVE_A_SCALE = 1 << 3; static const int32_t kFLAG_WE_HAVE_A_SCALE = 1 << 3;
static const int32_t kFLAG_MORE_COMPONENTS = 1 << 5; 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. // Read the x coordinates.
int prev_x = 0; int prev_x = 0;
for (int i = 0; i < num_contours; ++i) { 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, bool StorePoints(const Glyph& glyph, size_t* offset,
uint8_t* dst, size_t dst_size) { uint8_t* dst, size_t dst_size) {
int last_flag = -1; int previous_flag = -1;
int repeat_count = 0; int repeat_count = 0;
int last_x = 0; int last_x = 0;
int last_y = 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& contour : glyph.contours) {
for (const auto& point : contour) { for (const auto& point : contour) {
int flag = point.on_curve ? kFLAG_ONCURVE : 0; 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 dx = point.x - last_x;
int dy = point.y - last_y; int dy = point.y - last_y;
if (dx == 0) { if (dx == 0) {
@@ -268,7 +277,7 @@ bool StorePoints(const Glyph& glyph, size_t* offset,
} else { } else {
y_bytes += 2; y_bytes += 2;
} }
if (flag == last_flag && repeat_count != 255) { if (flag == previous_flag && repeat_count != 255) {
dst[*offset - 1] |= kFLAG_REPEAT; dst[*offset - 1] |= kFLAG_REPEAT;
repeat_count++; repeat_count++;
} else { } else {
@@ -286,7 +295,7 @@ bool StorePoints(const Glyph& glyph, size_t* offset,
} }
last_x = point.x; last_x = point.x;
last_y = point.y; last_y = point.y;
last_flag = flag; previous_flag = flag;
} }
} }
if (repeat_count != 0) { if (repeat_count != 0) {
+10 -2
View File
@@ -10,8 +10,10 @@
#ifndef WOFF2_GLYPH_H_ #ifndef WOFF2_GLYPH_H_
#define WOFF2_GLYPH_H_ #define WOFF2_GLYPH_H_
#include <stddef.h>
#include <inttypes.h> #include <inttypes.h>
#include <stddef.h>
#include <cstdint>
#include <vector> #include <vector>
namespace woff2 { namespace woff2 {
@@ -22,7 +24,10 @@ namespace woff2 {
// is around. // is around.
class Glyph { class Glyph {
public: public:
Glyph() : instructions_size(0), composite_data_size(0) {} Glyph()
: instructions_size(0),
overlap_simple_flag_set(false),
composite_data_size(0) {}
// Bounding box. // Bounding box.
int16_t x_min; int16_t x_min;
@@ -34,6 +39,9 @@ class Glyph {
uint16_t instructions_size; uint16_t instructions_size;
const uint8_t* instructions_data; const uint8_t* instructions_data;
// Flags.
bool overlap_simple_flag_set;
// Data model for simple glyphs. // Data model for simple glyphs.
struct Point { struct Point {
int x; int x;
+19 -1
View File
@@ -22,6 +22,7 @@ namespace {
const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0; const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0;
const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
const int FLAG_OVERLAP_SIMPLE_BITMAP = 1 << 0;
void WriteBytes(std::vector<uint8_t>* out, const uint8_t* data, size_t len) { void WriteBytes(std::vector<uint8_t>* out, const uint8_t* data, size_t len) {
if (len == 0) return; if (len == 0) return;
@@ -69,7 +70,10 @@ class GlyfEncoder {
} }
void GetTransformedGlyfBytes(std::vector<uint8_t>* result) { void GetTransformedGlyfBytes(std::vector<uint8_t>* 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, n_glyphs_);
WriteUShort(result, 0); // index_format, will be set later WriteUShort(result, 0); // index_format, will be set later
WriteLong(result, n_contour_stream_.size()); WriteLong(result, n_contour_stream_.size());
@@ -87,6 +91,9 @@ class GlyfEncoder {
WriteBytes(result, bbox_bitmap_); WriteBytes(result, bbox_bitmap_);
WriteBytes(result, bbox_stream_); WriteBytes(result, bbox_stream_);
WriteBytes(result, instruction_stream_); WriteBytes(result, instruction_stream_);
if (!overlap_bitmap_.empty()) {
WriteBytes(result, overlap_bitmap_);
}
} }
private: private:
@@ -127,6 +134,10 @@ class GlyfEncoder {
} }
void WriteSimpleGlyph(int glyph_id, const Glyph& glyph) { 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(); int num_contours = glyph.contours.size();
WriteUShort(&n_contour_stream_, num_contours); WriteUShort(&n_contour_stream_, num_contours);
if (ShouldWriteSimpleGlyphBbox(glyph)) { if (ShouldWriteSimpleGlyphBbox(glyph)) {
@@ -214,6 +225,12 @@ class GlyfEncoder {
} }
} }
void EnsureOverlapBitmap() {
if (overlap_bitmap_.empty()) {
overlap_bitmap_.resize((n_glyphs_ + 7) >> 3);
}
}
std::vector<uint8_t> n_contour_stream_; std::vector<uint8_t> n_contour_stream_;
std::vector<uint8_t> n_points_stream_; std::vector<uint8_t> n_points_stream_;
std::vector<uint8_t> flag_byte_stream_; std::vector<uint8_t> flag_byte_stream_;
@@ -222,6 +239,7 @@ class GlyfEncoder {
std::vector<uint8_t> bbox_stream_; std::vector<uint8_t> bbox_stream_;
std::vector<uint8_t> glyph_stream_; std::vector<uint8_t> glyph_stream_;
std::vector<uint8_t> instruction_stream_; std::vector<uint8_t> instruction_stream_;
std::vector<uint8_t> overlap_bitmap_;
int n_glyphs_; int n_glyphs_;
}; };
+4 -6
View File
@@ -13,22 +13,20 @@
int main(int argc, char **argv) { int main(int argc, char **argv) {
using std::string;
if (argc != 2) { if (argc != 2) {
fprintf(stderr, "One argument, the input filename, must be provided.\n"); fprintf(stderr, "One argument, the input filename, must be provided.\n");
return 1; return 1;
} }
string filename(argv[1]); std::string filename(argv[1]);
string outfilename = filename.substr(0, filename.find_last_of(".")) + ".woff2"; std::string outfilename = filename.substr(0, filename.find_last_of(".")) + ".woff2";
fprintf(stdout, "Processing %s => %s\n", fprintf(stdout, "Processing %s => %s\n",
filename.c_str(), outfilename.c_str()); filename.c_str(), outfilename.c_str());
string input = woff2::GetFileContent(filename); std::string input = woff2::GetFileContent(filename);
const uint8_t* input_data = reinterpret_cast<const uint8_t*>(input.data()); const uint8_t* input_data = reinterpret_cast<const uint8_t*>(input.data());
size_t output_size = woff2::MaxWOFF2CompressedSize(input_data, input.size()); 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<uint8_t*>(&output[0]); uint8_t* output_data = reinterpret_cast<uint8_t*>(&output[0]);
woff2::WOFF2Params params; woff2::WOFF2Params params;
+36 -9
View File
@@ -32,10 +32,6 @@ namespace woff2 {
namespace { namespace {
using std::string;
using std::vector;
// simple glyph flags // simple glyph flags
const int kGlyfOnCurve = 1 << 0; const int kGlyfOnCurve = 1 << 0;
const int kGlyfXShort = 1 << 1; const int kGlyfXShort = 1 << 1;
@@ -43,6 +39,7 @@ const int kGlyfYShort = 1 << 2;
const int kGlyfRepeat = 1 << 3; const int kGlyfRepeat = 1 << 3;
const int kGlyfThisXIsSame = 1 << 4; const int kGlyfThisXIsSame = 1 << 4;
const int kGlyfThisYIsSame = 1 << 5; const int kGlyfThisYIsSame = 1 << 5;
const int kOverlapSimple = 1 << 6;
// composite glyph flags // composite glyph flags
// See CompositeGlyph.java in sfntly for full definitions // See CompositeGlyph.java in sfntly for full definitions
@@ -53,6 +50,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_A_TWO_BY_TWO = 1 << 7;
const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; 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 kCheckSumAdjustmentOffset = 8;
const size_t kEndPtsOfContoursOffset = 10; const size_t kEndPtsOfContoursOffset = 10;
@@ -192,7 +192,8 @@ bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size,
// beginning of a simple glyph. Returns true on success. // beginning of a simple glyph. Returns true on success.
bool StorePoints(unsigned int n_points, const Point* points, bool StorePoints(unsigned int n_points, const Point* points,
unsigned int n_contours, unsigned int instruction_length, unsigned int n_contours, unsigned int instruction_length,
uint8_t* dst, size_t dst_size, size_t* glyph_size) { 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 // I believe that n_contours < 65536, in which case this is safe. However, a
// comment and/or an assert would be good. // comment and/or an assert would be good.
unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 + unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 +
@@ -207,6 +208,10 @@ bool StorePoints(unsigned int n_points, const Point* points,
for (unsigned int i = 0; i < n_points; ++i) { for (unsigned int i = 0; i < n_points; ++i) {
const Point& point = points[i]; const Point& point = points[i];
int flag = point.on_curve ? kGlyfOnCurve : 0; int flag = point.on_curve ? kGlyfOnCurve : 0;
if (has_overlap_bit && i == 0) {
flag |= kOverlapSimple;
}
int dx = point.x - last_x; int dx = point.x - last_x;
int dy = point.y - last_y; int dy = point.y - last_y;
if (dx == 0) { if (dx == 0) {
@@ -404,13 +409,20 @@ bool ReconstructGlyf(const uint8_t* data, Table* glyf_table,
WOFF2Out* out) { WOFF2Out* out) {
static const int kNumSubStreams = 7; static const int kNumSubStreams = 7;
Buffer file(data, glyf_table->transform_length); Buffer file(data, glyf_table->transform_length);
uint32_t version; uint16_t version;
std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams); std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams);
const size_t glyf_start = out->Size(); const size_t glyf_start = out->Size();
if (PREDICT_FALSE(!file.ReadU32(&version))) { if (PREDICT_FALSE(!file.ReadU16(&version))) {
return FONT_COMPRESSION_FAILURE(); 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) || if (PREDICT_FALSE(!file.ReadU16(&info->num_glyphs) ||
!file.ReadU16(&info->index_format))) { !file.ReadU16(&info->index_format))) {
return FONT_COMPRESSION_FAILURE(); return FONT_COMPRESSION_FAILURE();
@@ -448,6 +460,17 @@ bool ReconstructGlyf(const uint8_t* data, Table* glyf_table,
Buffer bbox_stream(substreams[5].first, substreams[5].second); Buffer bbox_stream(substreams[5].first, substreams[5].second);
Buffer instruction_stream(substreams[6].first, substreams[6].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<uint32_t> loca_values(info->num_glyphs + 1); std::vector<uint32_t> loca_values(info->num_glyphs + 1);
std::vector<unsigned int> n_points_vec; std::vector<unsigned int> n_points_vec;
std::unique_ptr<Point[]> points; std::unique_ptr<Point[]> points;
@@ -601,8 +624,12 @@ bool ReconstructGlyf(const uint8_t* data, Table* glyf_table,
} }
glyph_size += instruction_size; glyph_size += instruction_size;
if (PREDICT_FALSE(!StorePoints(total_n_points, points.get(), n_contours, bool has_overlap_bit =
instruction_size, glyph_buf.get(), glyph_buf_size, &glyph_size))) { 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(); return FONT_COMPRESSION_FAILURE();
} }
} else { } else {
+7 -7
View File
@@ -14,21 +14,21 @@
int main(int argc, char **argv) { int main(int argc, char **argv) {
using std::string;
if (argc != 2) { if (argc != 2) {
fprintf(stderr, "One argument, the input filename, must be provided.\n"); fprintf(stderr, "One argument, the input filename, must be provided.\n");
return 1; return 1;
} }
string filename(argv[1]); std::string filename(argv[1]);
string outfilename = filename.substr(0, filename.find_last_of(".")) + ".ttf"; std::string outfilename = filename.substr(0, filename.find_last_of(".")) + ".ttf";
// Note: update woff2_dec_fuzzer_new_entry.cc if this pattern changes. // 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<const uint8_t*>(input.data()); const uint8_t* raw_input = reinterpret_cast<const uint8_t*>(input.data());
string output(std::min(woff2::ComputeWOFF2FinalSize(raw_input, input.size()), std::string output(
woff2::kDefaultMaxSize), 0); std::min(woff2::ComputeWOFF2FinalSize(raw_input, input.size()),
woff2::kDefaultMaxSize),
0);
woff2::WOFF2StringOut out(&output); woff2::WOFF2StringOut out(&output);
const bool ok = woff2::ConvertWOFF2ToTTF(raw_input, input.size(), &out); const bool ok = woff2::ConvertWOFF2ToTTF(raw_input, input.size(), &out);
+2 -6
View File
@@ -28,13 +28,9 @@
namespace woff2 { namespace woff2 {
namespace { namespace {
using std::string;
using std::vector;
const size_t kWoff2HeaderSize = 48; const size_t kWoff2HeaderSize = 48;
const size_t kWoff2EntrySize = 20; 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, 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, // Except for the header size, which is 32 bytes larger in woff2 format,
// all other parts should be smaller (table header in short format, // all other parts should be smaller (table header in short format,
// transformations and compression). Just to be sure, we will give some // transformations and compression). Just to be sure, we will give some
+3 -5
View File
@@ -29,18 +29,16 @@ std::string PrintTag(int tag) {
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
using std::string;
if (argc != 2) { if (argc != 2) {
fprintf(stderr, "One argument, the input filename, must be provided.\n"); fprintf(stderr, "One argument, the input filename, must be provided.\n");
return 1; return 1;
} }
string filename(argv[1]); std::string filename(argv[1]);
string outfilename = filename.substr(0, filename.find_last_of(".")) + ".woff2"; std::string outfilename = filename.substr(0, filename.find_last_of(".")) + ".woff2";
fprintf(stdout, "Processing %s => %s\n", fprintf(stdout, "Processing %s => %s\n",
filename.c_str(), outfilename.c_str()); filename.c_str(), outfilename.c_str());
string input = woff2::GetFileContent(filename); std::string input = woff2::GetFileContent(filename);
woff2::Buffer file(reinterpret_cast<const uint8_t*>(input.data()), woff2::Buffer file(reinterpret_cast<const uint8_t*>(input.data()),
input.size()); input.size());
+2 -6
View File
@@ -8,14 +8,10 @@
#include <woff2/output.h> #include <woff2/output.h>
using std::string;
namespace woff2 { namespace woff2 {
WOFF2StringOut::WOFF2StringOut(string* buf) WOFF2StringOut::WOFF2StringOut(std::string *buf)
: buf_(buf), : buf_(buf), max_size_(kDefaultMaxSize), offset_(0) {}
max_size_(kDefaultMaxSize),
offset_(0) {}
bool WOFF2StringOut::Write(const void *buf, size_t n) { bool WOFF2StringOut::Write(const void *buf, size_t n) {
return Write(buf, offset_, n); return Write(buf, offset_, n);