Merge pull request #42 from google/hmtx
hmtx optimization decoding. never encodes hmtx optimized as yet
This commit is contained in:
@@ -79,6 +79,7 @@ bool ReadTrueTypeFont(Buffer* file, const uint8_t* data, size_t len,
|
||||
std::map<uint32_t, uint32_t> intervals;
|
||||
for (uint16_t i = 0; i < font->num_tables; ++i) {
|
||||
Font::Table table;
|
||||
table.flag_byte = 0;
|
||||
table.reuse_of = NULL;
|
||||
if (!file->ReadU32(&table.tag) ||
|
||||
!file->ReadU32(&table.checksum) ||
|
||||
|
||||
@@ -47,6 +47,8 @@ struct Font {
|
||||
// Intended use is to bypass re-processing tables
|
||||
Font::Table* reuse_of;
|
||||
|
||||
uint8_t flag_byte;
|
||||
|
||||
// Is this table reused by a TTC
|
||||
bool IsReused() const;
|
||||
};
|
||||
|
||||
+5
-5
@@ -78,11 +78,6 @@ bool ReadGlyph(const uint8_t* data, size_t len, Glyph* glyph) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
if (num_contours == 0) {
|
||||
// Empty glyph.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read the bounding box.
|
||||
if (!buffer.ReadS16(&glyph->x_min) ||
|
||||
!buffer.ReadS16(&glyph->y_min) ||
|
||||
@@ -91,6 +86,11 @@ bool ReadGlyph(const uint8_t* data, size_t len, Glyph* glyph) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
if (num_contours == 0) {
|
||||
// Empty glyph.
|
||||
return true;
|
||||
}
|
||||
|
||||
if (num_contours > 0) {
|
||||
// Simple glyph.
|
||||
glyph->contours.resize(num_contours);
|
||||
|
||||
@@ -27,6 +27,9 @@ static const uint32_t kHeadTableTag = 0x68656164;
|
||||
static const uint32_t kLocaTableTag = 0x6c6f6361;
|
||||
static const uint32_t kDsigTableTag = 0x44534947;
|
||||
static const uint32_t kCffTableTag = 0x43464620;
|
||||
static const uint32_t kHmtxTableTag = 0x686d7478;
|
||||
static const uint32_t kHheaTableTag = 0x68686561;
|
||||
static const uint32_t kMaxpTableTag = 0x6d617870;
|
||||
|
||||
extern const uint32_t kKnownTags[];
|
||||
|
||||
|
||||
@@ -290,4 +290,131 @@ bool TransformGlyfAndLocaTables(Font* font) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// See https://www.microsoft.com/typography/otspec/hmtx.htm
|
||||
// See WOFF2 spec, 5.4. Transformed hmtx table format
|
||||
bool TransformHmtxTable(Font* font) {
|
||||
const Font::Table* glyf_table = font->FindTable(kGlyfTableTag);
|
||||
const Font::Table* hmtx_table = font->FindTable(kHmtxTableTag);
|
||||
const Font::Table* hhea_table = font->FindTable(kHheaTableTag);
|
||||
|
||||
// If you don't have hmtx or a glyf not much is going to happen here
|
||||
if (hmtx_table == NULL || glyf_table == NULL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// hmtx without hhea doesn't make sense
|
||||
if (hhea_table == NULL) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
// Skip 34 to reach 'hhea' numberOfHMetrics
|
||||
Buffer hhea_buf(hhea_table->data, hhea_table->length);
|
||||
uint16_t num_hmetrics;
|
||||
if (!hhea_buf.Skip(34) || !hhea_buf.ReadU16(&num_hmetrics)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
// Must have at least one hMetric
|
||||
if (num_hmetrics < 1) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
int num_glyphs = NumGlyphs(*font);
|
||||
|
||||
// Most fonts can be transformed; assume it's a go until proven otherwise
|
||||
std::vector<uint16_t> advance_widths;
|
||||
std::vector<int16_t> proportional_lsbs;
|
||||
std::vector<int16_t> monospace_lsbs;
|
||||
|
||||
bool remove_proportional_lsb = true;
|
||||
bool remove_monospace_lsb = (num_glyphs - num_hmetrics) > 0;
|
||||
|
||||
Buffer hmtx_buf(hmtx_table->data, hmtx_table->length);
|
||||
for (int i = 0; i < num_glyphs; i++) {
|
||||
Glyph glyph;
|
||||
const uint8_t* glyph_data;
|
||||
size_t glyph_size;
|
||||
if (!GetGlyphData(*font, i, &glyph_data, &glyph_size) ||
|
||||
(glyph_size > 0 && !ReadGlyph(glyph_data, glyph_size, &glyph))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
uint16_t advance_width = 0;
|
||||
int16_t lsb = 0;
|
||||
|
||||
if (i < num_hmetrics) {
|
||||
// [0, num_hmetrics) are proportional hMetrics
|
||||
if (!hmtx_buf.ReadU16(&advance_width)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
if (!hmtx_buf.ReadS16(&lsb)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
if (glyph_size > 0 && glyph.x_min != lsb) {
|
||||
remove_proportional_lsb = false;
|
||||
}
|
||||
|
||||
advance_widths.push_back(advance_width);
|
||||
proportional_lsbs.push_back(lsb);
|
||||
} else {
|
||||
// [num_hmetrics, num_glyphs) are monospace leftSideBearing's
|
||||
if (!hmtx_buf.ReadS16(&lsb)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
if (glyph_size > 0 && glyph.x_min != lsb) {
|
||||
remove_monospace_lsb = false;
|
||||
}
|
||||
monospace_lsbs.push_back(lsb);
|
||||
}
|
||||
|
||||
// If we know we can't optimize, bail out completely
|
||||
if (!remove_proportional_lsb && !remove_monospace_lsb) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Font::Table* transformed_hmtx = &font->tables[kHmtxTableTag ^ 0x80808080];
|
||||
|
||||
uint8_t flags = 0;
|
||||
size_t transformed_size = 1 + 2 * advance_widths.size();
|
||||
if (remove_proportional_lsb) {
|
||||
flags |= 1;
|
||||
} else {
|
||||
transformed_size += 2 * proportional_lsbs.size();
|
||||
}
|
||||
if (remove_monospace_lsb) {
|
||||
flags |= 1 << 1;
|
||||
} else {
|
||||
transformed_size += 2 * monospace_lsbs.size();
|
||||
}
|
||||
|
||||
transformed_hmtx->buffer.reserve(transformed_size);
|
||||
std::vector<uint8_t>* out = &transformed_hmtx->buffer;
|
||||
WriteBytes(out, &flags, 1);
|
||||
for (uint16_t advance_width : advance_widths) {
|
||||
WriteUShort(out, advance_width);
|
||||
}
|
||||
|
||||
if (!remove_proportional_lsb) {
|
||||
for (int16_t lsb : proportional_lsbs) {
|
||||
WriteUShort(out, lsb);
|
||||
}
|
||||
}
|
||||
if (!remove_monospace_lsb) {
|
||||
for (int16_t lsb : monospace_lsbs) {
|
||||
WriteUShort(out, lsb);
|
||||
}
|
||||
}
|
||||
|
||||
transformed_hmtx->tag = kHmtxTableTag ^ 0x80808080;
|
||||
transformed_hmtx->flag_byte = 1 << 6;
|
||||
transformed_hmtx->length = transformed_hmtx->buffer.size();
|
||||
transformed_hmtx->data = transformed_hmtx->buffer.data();
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace woff2
|
||||
|
||||
@@ -26,6 +26,9 @@ namespace woff2 {
|
||||
// derived from the original tag by flipping the MSBs of every byte.
|
||||
bool TransformGlyfAndLocaTables(Font* font);
|
||||
|
||||
// Apply transformation to hmtx table if applicable for this font.
|
||||
bool TransformHmtxTable(Font* font);
|
||||
|
||||
} // namespace woff2
|
||||
|
||||
#endif // WOFF2_TRANSFORM_H_
|
||||
|
||||
+2
-1
@@ -26,7 +26,8 @@ namespace woff2 {
|
||||
|
||||
static const uint32_t kWoff2Signature = 0x774f4632; // "wOF2"
|
||||
|
||||
const unsigned int kWoff2FlagsTransform = 1 << 5;
|
||||
// Leave the first byte open to store flag_byte
|
||||
const unsigned int kWoff2FlagsTransform = 1 << 8;
|
||||
|
||||
// TrueType Collection ID string: 'ttcf'
|
||||
static const uint32_t kTtcFontFlavor = 0x74746366;
|
||||
|
||||
+282
-28
@@ -24,6 +24,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <unordered_set>
|
||||
#include <memory>
|
||||
|
||||
#include "./buffer.h"
|
||||
@@ -401,6 +402,7 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size,
|
||||
}
|
||||
uint8_t* glyf_dst = dst + loca_offset;
|
||||
size_t glyf_dst_size = dst_size - loca_offset;
|
||||
|
||||
if (n_contours == 0xffff) {
|
||||
// composite glyph
|
||||
bool have_instructions = false;
|
||||
@@ -542,6 +544,200 @@ const Table* FindTable(const std::vector<Table>& tables, uint32_t tag) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// https://www.microsoft.com/typography/otspec/maxp.htm
|
||||
bool ReadNumGlyphs(const Table* maxp_table,
|
||||
uint8_t* dst, size_t dst_length, uint16_t* num_glyphs) {
|
||||
if (PREDICT_FALSE(static_cast<uint64_t>(maxp_table->dst_offset +
|
||||
maxp_table->dst_length) > dst_length)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
Buffer buffer(dst + maxp_table->dst_offset, maxp_table->dst_length);
|
||||
// Skip 4 to reach 'maxp' numGlyphs
|
||||
if (PREDICT_FALSE(!buffer.Skip(4) || !buffer.ReadU16(num_glyphs))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get numberOfHMetrics, https://www.microsoft.com/typography/otspec/hhea.htm
|
||||
bool ReadNumHMetrics(const Table* hhea_table,
|
||||
uint8_t* dst, size_t dst_length, uint16_t* num_hmetrics) {
|
||||
if (PREDICT_FALSE(static_cast<uint64_t>(hhea_table->dst_offset +
|
||||
hhea_table->dst_length) > dst_length)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
// Skip 34 to reach 'hhea' numberOfHMetrics
|
||||
Buffer buffer(dst + hhea_table->dst_offset, hhea_table->dst_length);
|
||||
if (PREDICT_FALSE(!buffer.Skip(34) || !buffer.ReadU16(num_hmetrics))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// x_min for glyph; https://www.microsoft.com/typography/otspec/glyf.htm
|
||||
bool ReadGlyphXMin(Buffer* glyf_buff, Buffer* loca_buff, int16_t loca_format,
|
||||
uint16_t index, int16_t* x_min) {
|
||||
uint32_t offset1, offset2;
|
||||
loca_buff->set_offset((loca_format == 0 ? 2 : 4) * index);
|
||||
if (loca_format == 0) {
|
||||
uint16_t tmp1, tmp2;
|
||||
if (PREDICT_FALSE(!loca_buff->ReadU16(&tmp1) ||
|
||||
!loca_buff->ReadU16(&tmp2))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
// https://www.microsoft.com/typography/otspec/loca.htm
|
||||
// "The actual local offset divided by 2 is stored."
|
||||
offset1 = tmp1 * 2;
|
||||
offset2 = tmp2 * 2;
|
||||
} else if (PREDICT_FALSE(!loca_buff->ReadU32(&offset1) ||
|
||||
!loca_buff->ReadU32(&offset2))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
if (offset1 != offset2) {
|
||||
glyf_buff->set_offset(offset1 + 2);
|
||||
if (!glyf_buff->ReadS16(x_min)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
} else {
|
||||
*x_min = 0;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// http://dev.w3.org/webfonts/WOFF2/spec/Overview.html#hmtx_table_format
|
||||
bool ReconstructTransformedHmtx(const uint8_t* transformed_buf,
|
||||
size_t transformed_size,
|
||||
const Table* glyf_table,
|
||||
const Table* hhea_table,
|
||||
const Table* hmtx_table,
|
||||
const Table* loca_table,
|
||||
const Table* maxp_table,
|
||||
uint8_t* dst, size_t dst_length) {
|
||||
if (PREDICT_FALSE(!glyf_table)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
if (PREDICT_FALSE(!hhea_table)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
if (PREDICT_FALSE(!hmtx_table)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
if (PREDICT_FALSE(!loca_table)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
if (PREDICT_FALSE(!maxp_table)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
uint16_t num_glyphs, num_hmetrics;
|
||||
if (!ReadNumGlyphs(maxp_table, dst, dst_length, &num_glyphs)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
if (!ReadNumHMetrics(hhea_table, dst, dst_length, &num_hmetrics)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
if (PREDICT_FALSE(static_cast<uint64_t>(hmtx_table->dst_offset +
|
||||
hmtx_table->dst_length) > dst_length)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
Buffer hmtx_buff_in(transformed_buf, transformed_size);
|
||||
|
||||
uint8_t hmtx_flags;
|
||||
if (PREDICT_FALSE(!hmtx_buff_in.ReadU8(&hmtx_flags))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
std::vector<uint16_t> advance_widths;
|
||||
std::vector<int16_t> lsbs;
|
||||
bool has_proportional_lsbs = (hmtx_flags & 1) == 0;
|
||||
bool has_monospace_lsbs = (hmtx_flags & 2) == 0;
|
||||
|
||||
// you say you transformed but there is little evidence of it
|
||||
if (has_proportional_lsbs && has_monospace_lsbs) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
// glyf/loca are done already so we can use them to recover x_min's
|
||||
if (PREDICT_FALSE(static_cast<uint64_t>(glyf_table->dst_offset +
|
||||
glyf_table->dst_length) > dst_length)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
if (PREDICT_FALSE(static_cast<uint64_t>(loca_table->dst_offset +
|
||||
loca_table->dst_length) > dst_length)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
Buffer glyf_buff(dst + glyf_table->dst_offset, glyf_table->dst_length);
|
||||
Buffer loca_buff(dst + loca_table->dst_offset, loca_table->dst_length);
|
||||
int16_t loca_format;
|
||||
if (loca_table->dst_length == 2 * (num_glyphs + 1)) {
|
||||
loca_format = 0;
|
||||
} else if (loca_table->dst_length == 4 * (num_glyphs + 1)) {
|
||||
loca_format = 1;
|
||||
} else {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < num_hmetrics; i++) {
|
||||
uint16_t advance_width;
|
||||
if (PREDICT_FALSE(!hmtx_buff_in.ReadU16(&advance_width))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
advance_widths.push_back(advance_width);
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < num_hmetrics; i++) {
|
||||
int16_t lsb;
|
||||
if (has_proportional_lsbs) {
|
||||
if (PREDICT_FALSE(!hmtx_buff_in.ReadS16(&lsb))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
} else {
|
||||
if (PREDICT_FALSE(!ReadGlyphXMin(&glyf_buff, &loca_buff, loca_format, i,
|
||||
&lsb))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
}
|
||||
lsbs.push_back(lsb);
|
||||
}
|
||||
|
||||
for (uint16_t i = num_hmetrics; i < num_glyphs; i++) {
|
||||
int16_t lsb;
|
||||
if (has_monospace_lsbs) {
|
||||
if (PREDICT_FALSE(!hmtx_buff_in.ReadS16(&lsb))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
} else {
|
||||
if (PREDICT_FALSE(!ReadGlyphXMin(&glyf_buff, &loca_buff, loca_format, i,
|
||||
&lsb))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
}
|
||||
lsbs.push_back(lsb);
|
||||
}
|
||||
|
||||
// bake me a shiny new hmtx table
|
||||
uint32_t hmtx_output_size = 2 * num_glyphs + 2 * num_hmetrics;
|
||||
if (hmtx_output_size > hmtx_table->dst_length) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
if (PREDICT_FALSE(static_cast<uint64_t>(hmtx_table->dst_offset +
|
||||
hmtx_table->dst_length) > dst_length)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
size_t dst_offset = hmtx_table->dst_offset;
|
||||
for (uint32_t i = 0; i < num_glyphs; i++) {
|
||||
if (i < num_hmetrics) {
|
||||
Store16(advance_widths[i], &dst_offset, dst);
|
||||
}
|
||||
Store16(lsbs[i], &dst_offset, dst);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReconstructTransformedGlyf(const uint8_t* transformed_buf,
|
||||
size_t transformed_size, const Table* glyf_table, const Table* loca_table,
|
||||
uint8_t* dst, size_t dst_length) {
|
||||
@@ -561,9 +757,11 @@ bool ReconstructTransformedGlyf(const uint8_t* transformed_buf,
|
||||
dst + loca_table->dst_offset, loca_table->dst_length);
|
||||
}
|
||||
|
||||
// Reconstructs a single font. tables must not contain duplicates.
|
||||
bool ReconstructTransformed(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 == kGlyfTableTag) {
|
||||
const Table* glyf_table = FindTable(tables, tag);
|
||||
const Table* loca_table = FindTable(tables, kLocaTableTag);
|
||||
@@ -574,6 +772,20 @@ bool ReconstructTransformed(const std::vector<Table>& tables, uint32_t tag,
|
||||
if (PREDICT_FALSE(!FindTable(tables, kGlyfTableTag))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
} else if (tag == kHmtxTableTag) {
|
||||
// Tables are sorted for a non-collection.
|
||||
// glyf is before hmtx and includes loca so all our accesses are safe.
|
||||
const Table* glyf_table = FindTable(tables, kGlyfTableTag);
|
||||
const Table* hhea_table = FindTable(tables, kHheaTableTag);
|
||||
const Table* hmtx_table = FindTable(tables, kHmtxTableTag);
|
||||
const Table* loca_table = FindTable(tables, kLocaTableTag);
|
||||
const Table* maxp_table = FindTable(tables, kMaxpTableTag);
|
||||
|
||||
if (PREDICT_FALSE(!ReconstructTransformedHmtx(
|
||||
transformed_buf, transformed_size, glyf_table, hhea_table,
|
||||
hmtx_table, loca_table, maxp_table, dst, dst_length))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
} else {
|
||||
// transform for the tag is not known
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
@@ -696,15 +908,19 @@ bool ReadTableDirectory(Buffer* file, std::vector<Table>* tables,
|
||||
} else {
|
||||
tag = kKnownTags[flag_byte & 0x3f];
|
||||
}
|
||||
// Bits 6 and 7 are reserved and must be 0.
|
||||
if (PREDICT_FALSE((flag_byte & 0xC0)) != 0) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
uint32_t flags = 0;
|
||||
// Always transform the glyf and loca tables
|
||||
uint8_t xform_version = (flag_byte >> 6) & 0x03;
|
||||
|
||||
// 0 means xform for glyph/loca, non-0 for others
|
||||
if (tag == kGlyfTableTag || tag == kLocaTableTag) {
|
||||
if (xform_version == 0) {
|
||||
flags |= kWoff2FlagsTransform;
|
||||
}
|
||||
} else if (xform_version != 0) {
|
||||
flags |= kWoff2FlagsTransform;
|
||||
}
|
||||
flags |= xform_version;
|
||||
|
||||
uint32_t dst_length;
|
||||
if (PREDICT_FALSE(!ReadBase128(file, &dst_length))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
@@ -781,6 +997,29 @@ uint64_t ComputeOffsetToFirstTable(const uint32_t header_version,
|
||||
return offset;
|
||||
}
|
||||
|
||||
bool ReconstructTransformedFont(const std::vector<Table>& tables,
|
||||
std::map<uint32_t, const uint8_t*>* src_by_dest,
|
||||
uint8_t* result, size_t result_length) {
|
||||
for (const auto& table : tables) {
|
||||
if ((table.flags & kWoff2FlagsTransform) != kWoff2FlagsTransform) {
|
||||
continue;
|
||||
}
|
||||
const auto it = src_by_dest->find(table.dst_offset);
|
||||
if (it == src_by_dest->end()) {
|
||||
continue;
|
||||
}
|
||||
const uint8_t* transform_buf = (*it).second;
|
||||
src_by_dest->erase(table.dst_offset);
|
||||
|
||||
size_t transform_length = table.transform_length;
|
||||
if (PREDICT_FALSE(!ReconstructTransformed(tables, table.tag,
|
||||
transform_buf, transform_length, result, result_length))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
|
||||
const uint8_t* data, size_t length) {
|
||||
Buffer file(data, length);
|
||||
@@ -1046,14 +1285,31 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
|
||||
src_buf, compressed_length))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
// round 1, copy across all the unmodified tables
|
||||
transform_buf = &uncompressed_buf[0];
|
||||
const uint8_t* const transform_buf_end = &uncompressed_buf[0]
|
||||
+ uncompressed_buf.size();
|
||||
|
||||
// We wipe out the values as they get written into the final result to dedup
|
||||
std::map<uint32_t, const uint8_t*> src_by_dest;
|
||||
for (uint16_t i = 0; i < num_tables; ++i) {
|
||||
const Table* table = &tables[i];
|
||||
size_t transform_length = table->transform_length;
|
||||
|
||||
src_by_dest[table->dst_offset] = transform_buf;
|
||||
|
||||
if (PREDICT_FALSE(transform_buf + transform_length > transform_buf_end)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
transform_buf += transform_length;
|
||||
}
|
||||
|
||||
for (uint16_t i = 0; i < num_tables; ++i) {
|
||||
const Table* table = &tables[i];
|
||||
uint32_t flags = table->flags;
|
||||
size_t transform_length = table->transform_length;
|
||||
const size_t transform_length = table->transform_length;
|
||||
|
||||
if ((flags & kWoff2FlagsTransform) == 0) {
|
||||
if ((table->flags & kWoff2FlagsTransform) != kWoff2FlagsTransform) {
|
||||
if (PREDICT_FALSE(transform_length != table->dst_length)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
@@ -1061,31 +1317,29 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
|
||||
transform_length) > result_length)) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
transform_buf = src_by_dest.at(table->dst_offset);
|
||||
src_by_dest.erase(table->dst_offset);
|
||||
std::memcpy(result + table->dst_offset, transform_buf, transform_length);
|
||||
}
|
||||
}
|
||||
|
||||
std::memcpy(result + table->dst_offset, transform_buf,
|
||||
transform_length);
|
||||
} else {
|
||||
if (header_version) {
|
||||
if (table->tag == kGlyfTableTag) {
|
||||
const Table* loca_table = loca_by_glyf[table];
|
||||
if (PREDICT_FALSE(!ReconstructTransformedGlyf(transform_buf,
|
||||
transform_length, table, loca_table, result, result_length))) {
|
||||
// round 2, reconstruct transformed tables
|
||||
if (PREDICT_FALSE(header_version)) {
|
||||
// Rebuild collection font by font.
|
||||
std::vector<Table> font_tables;
|
||||
for (const auto& ttc_font : ttc_fonts) {
|
||||
font_tables.resize(ttc_font.table_indices.size());
|
||||
for (auto i = 0; i < ttc_font.table_indices.size(); i++) {
|
||||
font_tables[i] = tables[ttc_font.table_indices[i]];
|
||||
}
|
||||
if (PREDICT_FALSE(!ReconstructTransformedFont(font_tables, &src_by_dest,
|
||||
result, result_length))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
} else if (PREDICT_FALSE(table->tag != kLocaTableTag)) {
|
||||
// transform for this tag not known
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
} else {
|
||||
if (PREDICT_FALSE(!ReconstructTransformed(tables, table->tag,
|
||||
transform_buf, transform_length, result, result_length))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
}
|
||||
}
|
||||
transform_buf += transform_length;
|
||||
if (PREDICT_FALSE(
|
||||
transform_buf > &uncompressed_buf[0] + uncompressed_buf.size())) {
|
||||
if (PREDICT_FALSE(!ReconstructTransformedFont(tables, &src_by_dest, result,
|
||||
result_length))) {
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
}
|
||||
|
||||
+18
-17
@@ -34,10 +34,12 @@
|
||||
#include "./variable_length.h"
|
||||
#include "./woff2_common.h"
|
||||
|
||||
|
||||
namespace woff2 {
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
using std::string;
|
||||
using std::vector;
|
||||
|
||||
@@ -82,7 +84,7 @@ int KnownTableIndex(uint32_t tag) {
|
||||
}
|
||||
|
||||
void StoreTableEntry(const Table& table, size_t* offset, uint8_t* dst) {
|
||||
uint8_t flag_byte = KnownTableIndex(table.tag);
|
||||
uint8_t flag_byte = (table.flags & 0xC0) | KnownTableIndex(table.tag);
|
||||
dst[(*offset)++] = flag_byte;
|
||||
// The index here is treated as a set of flag bytes because
|
||||
// bits 6 and 7 of the byte are reserved for future use as flags.
|
||||
@@ -109,6 +111,7 @@ size_t TableEntrySize(const Table& table) {
|
||||
size_t ComputeWoff2Length(const FontCollection& font_collection,
|
||||
const std::vector<Table>& tables,
|
||||
std::map<uint32_t, uint16_t> index_by_offset,
|
||||
size_t compressed_data_length,
|
||||
size_t extended_metadata_length) {
|
||||
size_t size = kWoff2HeaderSize;
|
||||
|
||||
@@ -137,10 +140,8 @@ size_t ComputeWoff2Length(const FontCollection& font_collection,
|
||||
}
|
||||
|
||||
// compressed data
|
||||
for (const auto& table : tables) {
|
||||
size += table.dst_length;
|
||||
size += compressed_data_length;
|
||||
size = Round4(size);
|
||||
}
|
||||
|
||||
size += extended_metadata_length;
|
||||
return size;
|
||||
@@ -217,7 +218,7 @@ bool TransformFontCollection(FontCollection* font_collection) {
|
||||
for (auto& font : font_collection->fonts) {
|
||||
if (!TransformGlyfAndLocaTables(&font)) {
|
||||
#ifdef FONT_COMPRESSION_BIN
|
||||
fprintf(stderr, "Font transformation failed.\n");
|
||||
fprintf(stderr, "glyf/loca transformation failed.\n");
|
||||
#endif
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
@@ -291,6 +292,11 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
|
||||
return FONT_COMPRESSION_FAILURE();
|
||||
}
|
||||
|
||||
#ifdef FONT_COMPRESSION_BIN
|
||||
fprintf(stderr, "Compressed %zu to %u.\n", total_transform_length,
|
||||
total_compressed_length);
|
||||
#endif
|
||||
|
||||
// Compress the extended metadata
|
||||
// TODO(user): how does this apply to collections
|
||||
uint32_t compressed_metadata_buf_length =
|
||||
@@ -331,30 +337,25 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
|
||||
|
||||
Table table;
|
||||
table.tag = src_table.tag;
|
||||
table.flags = 0;
|
||||
table.flags = src_table.flag_byte;
|
||||
table.src_length = src_table.length;
|
||||
table.transform_length = src_table.length;
|
||||
const uint8_t* transformed_data = src_table.data;
|
||||
const Font::Table* transformed_table =
|
||||
font.FindTable(src_table.tag ^ 0x80808080);
|
||||
if (transformed_table != NULL) {
|
||||
table.flags = transformed_table->flag_byte;
|
||||
table.flags |= kWoff2FlagsTransform;
|
||||
table.transform_length = transformed_table->length;
|
||||
transformed_data = transformed_table->data;
|
||||
}
|
||||
if (tables.empty()) {
|
||||
table.dst_length = total_compressed_length;
|
||||
table.dst_data = &compression_buf[0];
|
||||
} else {
|
||||
table.dst_length = 0;
|
||||
table.dst_data = NULL;
|
||||
|
||||
}
|
||||
tables.push_back(table);
|
||||
}
|
||||
}
|
||||
|
||||
size_t woff2_length = ComputeWoff2Length(font_collection, tables,
|
||||
index_by_offset, compressed_metadata_buf_length);
|
||||
index_by_offset, total_compressed_length, compressed_metadata_buf_length);
|
||||
if (woff2_length > *result_length) {
|
||||
#ifdef FONT_COMPRESSION_BIN
|
||||
fprintf(stderr, "Result allocation was too small (%zd vs %zd bytes).\n",
|
||||
@@ -444,10 +445,10 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
|
||||
}
|
||||
|
||||
// compressed data format (http://www.w3.org/TR/WOFF2/#table_format)
|
||||
for (const auto& table : tables) {
|
||||
StoreBytes(table.dst_data, table.dst_length, &offset, result);
|
||||
|
||||
StoreBytes(&compression_buf[0], total_compressed_length, &offset, result);
|
||||
offset = Round4(offset);
|
||||
}
|
||||
|
||||
StoreBytes(compressed_metadata_buf.data(), compressed_metadata_buf_length,
|
||||
&offset, result);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user