Index: third_party/woff2/src/transform.cc |
diff --git a/third_party/woff2/src/transform.cc b/third_party/woff2/src/transform.cc |
index 23cecd6dfb45e2be113c5a3ad81e6eb17a38e0e9..b7b0cf0337ef7dae5f7ae2d10e56cc28976fb765 100644 |
--- a/third_party/woff2/src/transform.cc |
+++ b/third_party/woff2/src/transform.cc |
@@ -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 |