Merge pull request #42 from google/hmtx

hmtx optimization decoding. never encodes hmtx optimized as yet
This commit is contained in:
rsheeter
2016-01-20 14:24:57 -08:00
9 changed files with 447 additions and 55 deletions
+1
View File
@@ -79,6 +79,7 @@ bool ReadTrueTypeFont(Buffer* file, const uint8_t* data, size_t len,
std::map<uint32_t, uint32_t> intervals; std::map<uint32_t, uint32_t> intervals;
for (uint16_t i = 0; i < font->num_tables; ++i) { for (uint16_t i = 0; i < font->num_tables; ++i) {
Font::Table table; Font::Table table;
table.flag_byte = 0;
table.reuse_of = NULL; table.reuse_of = NULL;
if (!file->ReadU32(&table.tag) || if (!file->ReadU32(&table.tag) ||
!file->ReadU32(&table.checksum) || !file->ReadU32(&table.checksum) ||
+2
View File
@@ -47,6 +47,8 @@ struct Font {
// Intended use is to bypass re-processing tables // Intended use is to bypass re-processing tables
Font::Table* reuse_of; Font::Table* reuse_of;
uint8_t flag_byte;
// Is this table reused by a TTC // Is this table reused by a TTC
bool IsReused() const; bool IsReused() const;
}; };
+5 -5
View File
@@ -78,11 +78,6 @@ bool ReadGlyph(const uint8_t* data, size_t len, Glyph* glyph) {
return FONT_COMPRESSION_FAILURE(); return FONT_COMPRESSION_FAILURE();
} }
if (num_contours == 0) {
// Empty glyph.
return true;
}
// Read the bounding box. // Read the bounding box.
if (!buffer.ReadS16(&glyph->x_min) || if (!buffer.ReadS16(&glyph->x_min) ||
!buffer.ReadS16(&glyph->y_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(); return FONT_COMPRESSION_FAILURE();
} }
if (num_contours == 0) {
// Empty glyph.
return true;
}
if (num_contours > 0) { if (num_contours > 0) {
// Simple glyph. // Simple glyph.
glyph->contours.resize(num_contours); glyph->contours.resize(num_contours);
+3
View File
@@ -27,6 +27,9 @@ static const uint32_t kHeadTableTag = 0x68656164;
static const uint32_t kLocaTableTag = 0x6c6f6361; static const uint32_t kLocaTableTag = 0x6c6f6361;
static const uint32_t kDsigTableTag = 0x44534947; static const uint32_t kDsigTableTag = 0x44534947;
static const uint32_t kCffTableTag = 0x43464620; 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[]; extern const uint32_t kKnownTags[];
+127
View File
@@ -290,4 +290,131 @@ bool TransformGlyfAndLocaTables(Font* font) {
return true; 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 } // namespace woff2
+3
View File
@@ -26,6 +26,9 @@ namespace woff2 {
// derived from the original tag by flipping the MSBs of every byte. // derived from the original tag by flipping the MSBs of every byte.
bool TransformGlyfAndLocaTables(Font* font); bool TransformGlyfAndLocaTables(Font* font);
// Apply transformation to hmtx table if applicable for this font.
bool TransformHmtxTable(Font* font);
} // namespace woff2 } // namespace woff2
#endif // WOFF2_TRANSFORM_H_ #endif // WOFF2_TRANSFORM_H_
+2 -1
View File
@@ -26,7 +26,8 @@ namespace woff2 {
static const uint32_t kWoff2Signature = 0x774f4632; // "wOF2" 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' // TrueType Collection ID string: 'ttcf'
static const uint32_t kTtcFontFlavor = 0x74746366; static const uint32_t kTtcFontFlavor = 0x74746366;
+284 -30
View File
@@ -24,6 +24,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <map> #include <map>
#include <unordered_set>
#include <memory> #include <memory>
#include "./buffer.h" #include "./buffer.h"
@@ -401,6 +402,7 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size,
} }
uint8_t* glyf_dst = dst + loca_offset; uint8_t* glyf_dst = dst + loca_offset;
size_t glyf_dst_size = dst_size - loca_offset; size_t glyf_dst_size = dst_size - loca_offset;
if (n_contours == 0xffff) { if (n_contours == 0xffff) {
// composite glyph // composite glyph
bool have_instructions = false; bool have_instructions = false;
@@ -542,6 +544,200 @@ const Table* FindTable(const std::vector<Table>& tables, uint32_t tag) {
return NULL; 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, bool ReconstructTransformedGlyf(const uint8_t* transformed_buf,
size_t transformed_size, const Table* glyf_table, const Table* loca_table, size_t transformed_size, const Table* glyf_table, const Table* loca_table,
uint8_t* dst, size_t dst_length) { 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); 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, bool ReconstructTransformed(const std::vector<Table>& tables, uint32_t tag,
const uint8_t* transformed_buf, size_t transformed_size, const uint8_t* transformed_buf, size_t transformed_size,
uint8_t* dst, size_t dst_length) { uint8_t* dst, size_t dst_length) {
if (tag == kGlyfTableTag) { if (tag == kGlyfTableTag) {
const Table* glyf_table = FindTable(tables, tag); const Table* glyf_table = FindTable(tables, tag);
const Table* loca_table = FindTable(tables, kLocaTableTag); 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))) { if (PREDICT_FALSE(!FindTable(tables, kGlyfTableTag))) {
return FONT_COMPRESSION_FAILURE(); 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 { } else {
// transform for the tag is not known // transform for the tag is not known
return FONT_COMPRESSION_FAILURE(); return FONT_COMPRESSION_FAILURE();
@@ -696,15 +908,19 @@ bool ReadTableDirectory(Buffer* file, std::vector<Table>* tables,
} else { } else {
tag = kKnownTags[flag_byte & 0x3f]; 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; 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 (tag == kGlyfTableTag || tag == kLocaTableTag) {
if (xform_version == 0) {
flags |= kWoff2FlagsTransform;
}
} else if (xform_version != 0) {
flags |= kWoff2FlagsTransform; flags |= kWoff2FlagsTransform;
} }
flags |= xform_version;
uint32_t dst_length; uint32_t dst_length;
if (PREDICT_FALSE(!ReadBase128(file, &dst_length))) { if (PREDICT_FALSE(!ReadBase128(file, &dst_length))) {
return FONT_COMPRESSION_FAILURE(); return FONT_COMPRESSION_FAILURE();
@@ -781,6 +997,29 @@ uint64_t ComputeOffsetToFirstTable(const uint32_t header_version,
return offset; 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, bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
const uint8_t* data, size_t length) { const uint8_t* data, size_t length) {
Buffer file(data, length); Buffer file(data, length);
@@ -1046,14 +1285,31 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
src_buf, compressed_length))) { src_buf, compressed_length))) {
return FONT_COMPRESSION_FAILURE(); return FONT_COMPRESSION_FAILURE();
} }
// round 1, copy across all the unmodified tables
transform_buf = &uncompressed_buf[0]; 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) { for (uint16_t i = 0; i < num_tables; ++i) {
const Table* table = &tables[i]; const Table* table = &tables[i];
uint32_t flags = table->flags; const size_t transform_length = table->transform_length;
size_t transform_length = table->transform_length;
if ((flags & kWoff2FlagsTransform) == 0) { if ((table->flags & kWoff2FlagsTransform) != kWoff2FlagsTransform) {
if (PREDICT_FALSE(transform_length != table->dst_length)) { if (PREDICT_FALSE(transform_length != table->dst_length)) {
return FONT_COMPRESSION_FAILURE(); return FONT_COMPRESSION_FAILURE();
} }
@@ -1061,31 +1317,29 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
transform_length) > result_length)) { transform_length) > result_length)) {
return FONT_COMPRESSION_FAILURE(); 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, // round 2, reconstruct transformed tables
transform_length); if (PREDICT_FALSE(header_version)) {
} else { // Rebuild collection font by font.
if (header_version) { std::vector<Table> font_tables;
if (table->tag == kGlyfTableTag) { for (const auto& ttc_font : ttc_fonts) {
const Table* loca_table = loca_by_glyf[table]; font_tables.resize(ttc_font.table_indices.size());
if (PREDICT_FALSE(!ReconstructTransformedGlyf(transform_buf, for (auto i = 0; i < ttc_font.table_indices.size(); i++) {
transform_length, table, loca_table, result, result_length))) { font_tables[i] = tables[ttc_font.table_indices[i]];
return FONT_COMPRESSION_FAILURE(); }
} if (PREDICT_FALSE(!ReconstructTransformedFont(font_tables, &src_by_dest,
} else if (PREDICT_FALSE(table->tag != kLocaTableTag)) { result, result_length))) {
// transform for this tag not known return FONT_COMPRESSION_FAILURE();
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; } else {
if (PREDICT_FALSE( if (PREDICT_FALSE(!ReconstructTransformedFont(tables, &src_by_dest, result,
transform_buf > &uncompressed_buf[0] + uncompressed_buf.size())) { result_length))) {
return FONT_COMPRESSION_FAILURE(); return FONT_COMPRESSION_FAILURE();
} }
} }
+20 -19
View File
@@ -34,10 +34,12 @@
#include "./variable_length.h" #include "./variable_length.h"
#include "./woff2_common.h" #include "./woff2_common.h"
namespace woff2 { namespace woff2 {
namespace { namespace {
using std::string; using std::string;
using std::vector; using std::vector;
@@ -82,7 +84,7 @@ int KnownTableIndex(uint32_t tag) {
} }
void StoreTableEntry(const Table& table, size_t* offset, uint8_t* dst) { 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; dst[(*offset)++] = flag_byte;
// The index here is treated as a set of flag bytes because // 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. // 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, size_t ComputeWoff2Length(const FontCollection& font_collection,
const std::vector<Table>& tables, const std::vector<Table>& tables,
std::map<uint32_t, uint16_t> index_by_offset, std::map<uint32_t, uint16_t> index_by_offset,
size_t compressed_data_length,
size_t extended_metadata_length) { size_t extended_metadata_length) {
size_t size = kWoff2HeaderSize; size_t size = kWoff2HeaderSize;
@@ -137,10 +140,8 @@ size_t ComputeWoff2Length(const FontCollection& font_collection,
} }
// compressed data // compressed data
for (const auto& table : tables) { size += compressed_data_length;
size += table.dst_length; size = Round4(size);
size = Round4(size);
}
size += extended_metadata_length; size += extended_metadata_length;
return size; return size;
@@ -217,7 +218,7 @@ bool TransformFontCollection(FontCollection* font_collection) {
for (auto& font : font_collection->fonts) { for (auto& font : font_collection->fonts) {
if (!TransformGlyfAndLocaTables(&font)) { if (!TransformGlyfAndLocaTables(&font)) {
#ifdef FONT_COMPRESSION_BIN #ifdef FONT_COMPRESSION_BIN
fprintf(stderr, "Font transformation failed.\n"); fprintf(stderr, "glyf/loca transformation failed.\n");
#endif #endif
return FONT_COMPRESSION_FAILURE(); return FONT_COMPRESSION_FAILURE();
} }
@@ -291,6 +292,11 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
return FONT_COMPRESSION_FAILURE(); 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 // Compress the extended metadata
// TODO(user): how does this apply to collections // TODO(user): how does this apply to collections
uint32_t compressed_metadata_buf_length = uint32_t compressed_metadata_buf_length =
@@ -331,30 +337,25 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
Table table; Table table;
table.tag = src_table.tag; table.tag = src_table.tag;
table.flags = 0; table.flags = src_table.flag_byte;
table.src_length = src_table.length; table.src_length = src_table.length;
table.transform_length = src_table.length; table.transform_length = src_table.length;
const uint8_t* transformed_data = src_table.data; const uint8_t* transformed_data = src_table.data;
const Font::Table* transformed_table = const Font::Table* transformed_table =
font.FindTable(src_table.tag ^ 0x80808080); font.FindTable(src_table.tag ^ 0x80808080);
if (transformed_table != NULL) { if (transformed_table != NULL) {
table.flags = transformed_table->flag_byte;
table.flags |= kWoff2FlagsTransform; table.flags |= kWoff2FlagsTransform;
table.transform_length = transformed_table->length; table.transform_length = transformed_table->length;
transformed_data = transformed_table->data; 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); tables.push_back(table);
} }
} }
size_t woff2_length = ComputeWoff2Length(font_collection, tables, 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) { if (woff2_length > *result_length) {
#ifdef FONT_COMPRESSION_BIN #ifdef FONT_COMPRESSION_BIN
fprintf(stderr, "Result allocation was too small (%zd vs %zd bytes).\n", 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) // 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); offset = Round4(offset);
}
StoreBytes(compressed_metadata_buf.data(), compressed_metadata_buf_length, StoreBytes(compressed_metadata_buf.data(), compressed_metadata_buf_length,
&offset, result); &offset, result);