Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add woff2 encoder/decoder support for overlapSimpleBitmap. #149

Merged
merged 1 commit into from
Mar 30, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions include/woff2/encode.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions include/woff2/output.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,15 +51,15 @@ 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;
size_t Size() override { return offset_; }
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_;
};
Expand Down
14 changes: 5 additions & 9 deletions src/file.h
Original file line number Diff line number Diff line change
Expand Up @@ -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<char>(ifs.rdbuf()),
std::istreambuf_iterator<char>());
return std::string(std::istreambuf_iterator<char>(ifs.rdbuf()),
std::istreambuf_iterator<char>());
}

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<char>(ofs));
}
Expand Down
15 changes: 12 additions & 3 deletions src/glyph.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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;
Expand All @@ -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) {
Expand All @@ -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 {
Expand All @@ -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) {
Expand Down
12 changes: 10 additions & 2 deletions src/glyph.h
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,10 @@
#ifndef WOFF2_GLYPH_H_
#define WOFF2_GLYPH_H_

#include <stddef.h>
#include <inttypes.h>
#include <stddef.h>

#include <cstdint>
#include <vector>

namespace woff2 {
Expand All @@ -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;
Expand All @@ -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;
Expand Down
20 changes: 19 additions & 1 deletion src/transform.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint8_t>* out, const uint8_t* data, size_t len) {
if (len == 0) return;
Expand Down Expand Up @@ -69,7 +70,10 @@ class GlyfEncoder {
}

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, 0); // index_format, will be set later
WriteLong(result, n_contour_stream_.size());
Expand All @@ -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:
Expand Down Expand Up @@ -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)) {
Expand Down Expand Up @@ -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_points_stream_;
std::vector<uint8_t> flag_byte_stream_;
Expand All @@ -222,6 +239,7 @@ class GlyfEncoder {
std::vector<uint8_t> bbox_stream_;
std::vector<uint8_t> glyph_stream_;
std::vector<uint8_t> instruction_stream_;
std::vector<uint8_t> overlap_bitmap_;
int n_glyphs_;
};

Expand Down
10 changes: 4 additions & 6 deletions src/woff2_compress.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<const uint8_t*>(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<uint8_t*>(&output[0]);

woff2::WOFF2Params params;
Expand Down
47 changes: 37 additions & 10 deletions src/woff2_dec.cc
Original file line number Diff line number Diff line change
Expand Up @@ -32,17 +32,14 @@ namespace woff2 {

namespace {

using std::string;
using std::vector;


// simple glyph flags
const int kGlyfOnCurve = 1 << 0;
const int kGlyfXShort = 1 << 1;
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
Expand All @@ -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_INSTRUCTIONS = 1 << 8;

// glyf flags
const int FLAG_OVERLAP_SIMPLE_BITMAP = 1 << 0;

const size_t kCheckSumAdjustmentOffset = 8;

const size_t kEndPtsOfContoursOffset = 10;
Expand Down Expand Up @@ -191,8 +191,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 +
Expand All @@ -207,6 +208,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) {
Expand Down Expand Up @@ -404,13 +409,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<std::pair<const uint8_t*, size_t> > 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();
Expand Down Expand Up @@ -448,6 +460,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<uint32_t> loca_values(info->num_glyphs + 1);
std::vector<unsigned int> n_points_vec;
std::unique_ptr<Point[]> points;
Expand Down Expand Up @@ -601,8 +624,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 {
Expand Down
14 changes: 7 additions & 7 deletions src/woff2_decompress.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<const uint8_t*>(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);
Expand Down
8 changes: 2 additions & 6 deletions src/woff2_enc.cc
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,8 @@

namespace woff2 {

namespace {


using std::string;
using std::vector;

namespace {

const size_t kWoff2HeaderSize = 48;
const size_t kWoff2EntrySize = 20;
Expand Down Expand Up @@ -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
Expand Down
Loading