hmtx optimization decoding. never encodes hmtx optimized as yet; need a cli param.

This commit is contained in:
Rod Sheeter
2016-01-20 14:10:02 -08:00
parent db0448815a
commit c817c969bf
9 changed files with 447 additions and 55 deletions
+127
View File
@@ -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