From c817c969bfa315d5cf718c793b1b122d16ac791b Mon Sep 17 00:00:00 2001 From: Rod Sheeter Date: Wed, 20 Jan 2016 14:10:02 -0800 Subject: [PATCH] hmtx optimization decoding. never encodes hmtx optimized as yet; need a cli param. --- src/font.cc | 1 + src/font.h | 2 + src/glyph.cc | 10 +- src/table_tags.h | 3 + src/transform.cc | 127 ++++++++++++++++++ src/transform.h | 3 + src/woff2_common.h | 3 +- src/woff2_dec.cc | 314 ++++++++++++++++++++++++++++++++++++++++----- src/woff2_enc.cc | 39 +++--- 9 files changed, 447 insertions(+), 55 deletions(-) diff --git a/src/font.cc b/src/font.cc index 5b4bbe6..7cc5688 100644 --- a/src/font.cc +++ b/src/font.cc @@ -79,6 +79,7 @@ bool ReadTrueTypeFont(Buffer* file, const uint8_t* data, size_t len, std::map 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) || diff --git a/src/font.h b/src/font.h index ff7f85f..601c4c8 100644 --- a/src/font.h +++ b/src/font.h @@ -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; }; diff --git a/src/glyph.cc b/src/glyph.cc index 4cef0d9..17bef38 100644 --- a/src/glyph.cc +++ b/src/glyph.cc @@ -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); diff --git a/src/table_tags.h b/src/table_tags.h index c9b09bb..daa5d92 100644 --- a/src/table_tags.h +++ b/src/table_tags.h @@ -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[]; diff --git a/src/transform.cc b/src/transform.cc index 23cecd6..b7b0cf0 100644 --- a/src/transform.cc +++ b/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 advance_widths; + std::vector proportional_lsbs; + std::vector 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* 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 diff --git a/src/transform.h b/src/transform.h index 15dc73e..e9d1b0d 100644 --- a/src/transform.h +++ b/src/transform.h @@ -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_ diff --git a/src/woff2_common.h b/src/woff2_common.h index 1d3d03d..a8c45af 100644 --- a/src/woff2_common.h +++ b/src/woff2_common.h @@ -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; diff --git a/src/woff2_dec.cc b/src/woff2_dec.cc index 6e50d72..fff4aaa 100644 --- a/src/woff2_dec.cc +++ b/src/woff2_dec.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include #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& 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(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(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(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 advance_widths; + std::vector 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(glyf_table->dst_offset + + glyf_table->dst_length) > dst_length)) { + return FONT_COMPRESSION_FAILURE(); + } + if (PREDICT_FALSE(static_cast(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(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
& 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
& 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
* 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
& tables, + std::map* 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 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))) { - 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(); - } + // round 2, reconstruct transformed tables + if (PREDICT_FALSE(header_version)) { + // Rebuild collection font by font. + std::vector
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(); } } - transform_buf += transform_length; - if (PREDICT_FALSE( - transform_buf > &uncompressed_buf[0] + uncompressed_buf.size())) { + } else { + if (PREDICT_FALSE(!ReconstructTransformedFont(tables, &src_by_dest, result, + result_length))) { return FONT_COMPRESSION_FAILURE(); } } diff --git a/src/woff2_enc.cc b/src/woff2_enc.cc index 215fb0c..3729ae0 100644 --- a/src/woff2_enc.cc +++ b/src/woff2_enc.cc @@ -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
& tables, std::map 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 = Round4(size); - } + 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); - offset = Round4(offset); - } + + StoreBytes(&compression_buf[0], total_compressed_length, &offset, result); + offset = Round4(offset); + StoreBytes(compressed_metadata_buf.data(), compressed_metadata_buf_length, &offset, result);