Skip to content

Commit

Permalink
Merge pull request #149 from garretrieger/overlap_bit
Browse files Browse the repository at this point in the history
Add woff2 encoder/decoder support for overlapSimpleBitmap.
  • Loading branch information
rsheeter authored Mar 30, 2022
2 parents a0d0ed7 + 62ae7a4 commit 4721483
Show file tree
Hide file tree
Showing 12 changed files with 105 additions and 59 deletions.
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

0 comments on commit 4721483

Please sign in to comment.