// 98% of Google Fonts have no glyph above 5k bytes // Largest glyph ever observed was 72k bytes const size_t kDefaultGlyphBuf = 5120;
// Over 14k test fonts the max compression ratio seen to date was ~20. // >100 suggests you wrote a bad uncompressed size. constfloat kMaxPlausibleCompressionRatio = 100.0;
// metadata for a TTC font entry struct TtcFont {
uint32_t flavor;
uint32_t dst_offset;
uint32_t header_checksum;
std::vector<uint16_t> table_indices;
};
struct WOFF2Header {
uint32_t flavor;
uint32_t header_version;
uint16_t num_tables;
uint64_t compressed_offset;
uint32_t compressed_length;
uint32_t uncompressed_size;
std::vector<Table> tables; // num_tables unique tables
std::vector<TtcFont> ttc_fonts; // metadata to help rebuild font
};
/** * Accumulates data we may need to reconstruct a single font. One per font * created for a TTC.
*/ struct WOFF2FontInfo {
uint16_t num_glyphs;
uint16_t index_format;
uint16_t num_hmetrics;
std::vector<int16_t> x_mins;
std::map<uint32_t, uint32_t> table_entry_by_tag;
};
// Accumulates metadata as we rebuild the font struct RebuildMetadata {
uint32_t header_checksum; // set by WriteHeaders
std::vector<WOFF2FontInfo> font_infos; // checksums for tables that have been written. // (tag, src_offset) => checksum. Need both because 0-length loca.
std::map<std::pair<uint32_t, uint32_t>, uint32_t> checksums;
};
bool _SafeIntAddition(int a, int b, int* result) { if (PREDICT_FALSE(
((a > 0) && (b > std::numeric_limits<int>::max() - a)) ||
((a < 0) && (b < std::numeric_limits<int>::min() - a)))) { returnfalse;
}
*result = a + b; returntrue;
}
bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, unsignedint n_points, Point* result, size_t* in_bytes_consumed) { int x = 0; int y = 0;
// This function stores just the point data. On entry, dst points to the // beginning of a simple glyph. Returns true on success. bool StorePoints(unsignedint n_points, const Point* points, unsignedint n_contours, unsignedint instruction_length, bool has_overlap_bit, uint8_t* dst, size_t dst_size,
size_t* glyph_size) { // I believe that n_contours < 65536, in which case this is safe. However, a // comment and/or an assert would be good. unsignedint flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 +
instruction_length; int last_flag = -1; int repeat_count = 0; int last_x = 0; int last_y = 0; unsignedint x_bytes = 0; unsignedint y_bytes = 0;
for (unsignedint i = 0; i < n_points; ++i) { const Point& point = points[i]; int flag = point.on_curve ? kGlyfOnCurve : 0; if (has_overlap_bit && i == 0) {
flag |= kOverlapSimple;
}
int dx = point.x - last_x; int dy = point.y - last_y; if (dx == 0) {
flag |= kGlyfThisXIsSame;
} elseif (dx > -256 && dx < 256) {
flag |= kGlyfXShort | (dx > 0 ? kGlyfThisXIsSame : 0);
x_bytes += 1;
} else {
x_bytes += 2;
} if (dy == 0) {
flag |= kGlyfThisYIsSame;
} elseif (dy > -256 && dy < 256) {
flag |= kGlyfYShort | (dy > 0 ? kGlyfThisYIsSame : 0);
y_bytes += 1;
} else {
y_bytes += 2;
}
int x_offset = flag_offset; int y_offset = flag_offset + x_bytes;
last_x = 0;
last_y = 0; for (unsignedint i = 0; i < n_points; ++i) { int dx = points[i].x - last_x; if (dx == 0) { // pass
} elseif (dx > -256 && dx < 256) {
dst[x_offset++] = std::abs(dx);
} else { // will always fit for valid input, but overflow is harmless
x_offset = Store16(dst, x_offset, dx);
}
last_x += dx; int dy = points[i].y - last_y; if (dy == 0) { // pass
} elseif (dy > -256 && dy < 256) {
dst[y_offset++] = std::abs(dy);
} else {
y_offset = Store16(dst, y_offset, dy);
}
last_y += dy;
}
*glyph_size = y_offset; returntrue;
}
// Compute the bounding box of the coordinates, and store into a glyf buffer. // A precondition is that there are at least 10 bytes available. // dst should point to the beginning of a 'glyf' record. void ComputeBbox(unsignedint n_points, const Point* points, uint8_t* dst) { int x_min = 0; int y_min = 0; int x_max = 0; int y_max = 0;
if (n_points > 0) {
x_min = points[0].x;
x_max = points[0].x;
y_min = points[0].y;
y_max = points[0].y;
} for (unsignedint i = 1; i < n_points; ++i) { int x = points[i].x; int y = points[i].y;
x_min = std::min(x, x_min);
x_max = std::max(x, x_max);
y_min = std::min(y, y_min);
y_max = std::max(y, y_max);
}
size_t offset = 2;
offset = Store16(dst, offset, x_min);
offset = Store16(dst, offset, y_min);
offset = Store16(dst, offset, x_max);
offset = Store16(dst, offset, y_max);
}
// We may need x_min to reconstruct 'hmtx' if (n_contours > 0) {
Buffer x_min_buf(glyph_buf.get() + 2, 2); if (PREDICT_FALSE(!x_min_buf.ReadS16(&info->x_mins[i]))) { return FONT_COMPRESSION_FAILURE();
}
}
}
// glyf_table dst_offset was set by ReconstructFont
glyf_table->dst_length = out->Size() - glyf_table->dst_offset;
loca_table->dst_offset = out->Size(); // loca[n] will be equal the length of the glyph data ('glyf') table
loca_values[info->num_glyphs] = glyf_table->dst_length; if (PREDICT_FALSE(!StoreLoca(loca_values, info->index_format, loca_checksum,
out))) { return FONT_COMPRESSION_FAILURE();
}
loca_table->dst_length = out->Size() - loca_table->dst_offset;
// Bits 2-7 are reserved and MUST be zero. if ((hmtx_flags & 0xFC) != 0) { #ifdef FONT_COMPRESSION_BIN
fprintf(stderr, "Illegal hmtx flags; bits 2-7 must be 0\n"); #endif return FONT_COMPRESSION_FAILURE();
}
// you say you transformed but there is little evidence of it if (has_proportional_lsbs && has_monospace_lsbs) { return FONT_COMPRESSION_FAILURE();
}
assert(x_mins.size() == num_glyphs);
// num_glyphs 0 is OK if there is no 'glyf' but cannot then xform 'hmtx'. if (PREDICT_FALSE(num_hmetrics > num_glyphs)) { 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 {
lsb = x_mins[i];
}
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 {
lsb = x_mins[i];
}
lsbs.push_back(lsb);
}
// bake me a shiny new hmtx table
uint32_t hmtx_output_size = 2 * num_glyphs + 2 * num_hmetrics;
std::vector<uint8_t> hmtx_table(hmtx_output_size);
uint8_t* dst = &hmtx_table[0];
size_t dst_offset = 0; 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);
}
// First table goes after all the headers, table directory, etc
uint64_t ComputeOffsetToFirstTable(const WOFF2Header& hdr) {
uint64_t offset = kSfntHeaderSize +
kSfntEntrySize * static_cast<uint64_t>(hdr.num_tables); if (hdr.header_version) {
offset = CollectionHeaderSize(hdr.header_version, hdr.ttc_fonts.size())
+ kSfntHeaderSize * hdr.ttc_fonts.size(); for (constauto& ttc_font : hdr.ttc_fonts) {
offset += kSfntEntrySize * ttc_font.table_indices.size();
}
} return offset;
}
std::vector<Table*> Tables(WOFF2Header* hdr, size_t font_index) {
std::vector<Table*> tables; if (PREDICT_FALSE(hdr->header_version)) { for (auto index : hdr->ttc_fonts[font_index].table_indices) {
tables.push_back(&hdr->tables[index]);
}
} else { for (auto& table : hdr->tables) {
tables.push_back(&table);
}
} return tables;
}
// Offset tables assumed to have been written in with 0's initially. // WOFF2Header isn't const so we can use [] instead of at() (which upsets FF) bool ReconstructFont(uint8_t* transformed_buf, const uint32_t transformed_buf_size,
RebuildMetadata* metadata,
WOFF2Header* hdr,
size_t font_index,
WOFF2Out* out) {
size_t dest_offset = out->Size();
uint8_t table_entry[12];
WOFF2FontInfo* info = &metadata->font_infos[font_index];
std::vector<Table*> tables = Tables(hdr, font_index);
// 'glyf' without 'loca' doesn't make sense const Table* glyf_table = FindTable(&tables, kGlyfTableTag); const Table* loca_table = FindTable(&tables, kLocaTableTag); if (PREDICT_FALSE(static_cast<bool>(glyf_table) != static_cast<bool>(loca_table))) { #ifdef FONT_COMPRESSION_BIN
fprintf(stderr, "Cannot have just one of glyf/loca\n"); #endif return FONT_COMPRESSION_FAILURE();
}
if (glyf_table != NULL) { if (PREDICT_FALSE((glyf_table->flags & kWoff2FlagsTransform)
!= (loca_table->flags & kWoff2FlagsTransform))) { #ifdef FONT_COMPRESSION_BIN
fprintf(stderr, "Cannot transform just one of glyf/loca\n"); #endif return FONT_COMPRESSION_FAILURE();
}
}
// TODO(user) a collection with optimized hmtx that reused glyf/loca // would fail. We don't optimize hmtx for collections yet. if (PREDICT_FALSE(static_cast<uint64_t>(table.src_offset) + table.src_length
> transformed_buf_size)) { return FONT_COMPRESSION_FAILURE();
}
if (table.tag == kHheaTableTag) { if (!ReadNumHMetrics(transformed_buf + table.src_offset,
table.src_length, &info->num_hmetrics)) { return FONT_COMPRESSION_FAILURE();
}
}
// We don't care about these fields of the header: // uint16_t reserved // uint32_t total_sfnt_size, we don't believe this, will compute later if (PREDICT_FALSE(!file.Skip(6))) { return FONT_COMPRESSION_FAILURE();
} if (PREDICT_FALSE(!file.ReadU32(&hdr->compressed_length))) { return FONT_COMPRESSION_FAILURE();
} // We don't care about these fields of the header: // uint16_t major_version, minor_version if (PREDICT_FALSE(!file.Skip(2 * 2))) { return FONT_COMPRESSION_FAILURE();
}
uint32_t meta_offset;
uint32_t meta_length;
uint32_t meta_length_orig; if (PREDICT_FALSE(!file.ReadU32(&meta_offset) ||
!file.ReadU32(&meta_length) ||
!file.ReadU32(&meta_length_orig))) { return FONT_COMPRESSION_FAILURE();
} if (meta_offset) { if (PREDICT_FALSE(
meta_offset >= length || length - meta_offset < meta_length)) { return FONT_COMPRESSION_FAILURE();
}
}
uint32_t priv_offset;
uint32_t priv_length; if (PREDICT_FALSE(!file.ReadU32(&priv_offset) ||
!file.ReadU32(&priv_length))) { return FONT_COMPRESSION_FAILURE();
} if (priv_offset) { if (PREDICT_FALSE(
priv_offset >= length || length - priv_offset < priv_length)) { return FONT_COMPRESSION_FAILURE();
}
}
hdr->tables.resize(hdr->num_tables); if (PREDICT_FALSE(!ReadTableDirectory(
&file, &hdr->tables, hdr->num_tables))) { return FONT_COMPRESSION_FAILURE();
}
// Before we sort for output the last table end is the uncompressed size.
Table& last_table = hdr->tables.back();
hdr->uncompressed_size = last_table.src_offset + last_table.src_length; if (PREDICT_FALSE(hdr->uncompressed_size < last_table.src_offset)) { return FONT_COMPRESSION_FAILURE();
}
hdr->header_version = 0;
if (hdr->flavor == kTtcFontFlavor) { if (PREDICT_FALSE(!file.ReadU32(&hdr->header_version))) { return FONT_COMPRESSION_FAILURE();
} if (PREDICT_FALSE(hdr->header_version != 0x00010000
&& hdr->header_version != 0x00020000)) { return FONT_COMPRESSION_FAILURE();
}
uint32_t num_fonts; if (PREDICT_FALSE(!Read255UShort(&file, &num_fonts) || !num_fonts)) { return FONT_COMPRESSION_FAILURE();
}
hdr->ttc_fonts.resize(num_fonts);
for (uint32_t i = 0; i < num_fonts; i++) {
TtcFont& ttc_font = hdr->ttc_fonts[i];
uint32_t num_tables; if (PREDICT_FALSE(!Read255UShort(&file, &num_tables) || !num_tables)) { return FONT_COMPRESSION_FAILURE();
} if (PREDICT_FALSE(!file.ReadU32(&ttc_font.flavor))) { return FONT_COMPRESSION_FAILURE();
}
// if we have both glyf and loca make sure they are consecutive // if we have just one we'll reject the font elsewhere if (glyf_idx > 0 || loca_idx > 0) { if (PREDICT_FALSE(glyf_idx > loca_idx || loca_idx - glyf_idx != 1)) { #ifdef FONT_COMPRESSION_BIN
fprintf(stderr, "TTC font %d has non-consecutive glyf/loca\n", i); #endif return FONT_COMPRESSION_FAILURE();
}
}
}
}
if (priv_offset) { if (PREDICT_FALSE(src_offset != priv_offset)) { return FONT_COMPRESSION_FAILURE();
}
src_offset = Round4(priv_offset + priv_length); if (PREDICT_FALSE(src_offset > std::numeric_limits<uint32_t>::max())) { return FONT_COMPRESSION_FAILURE();
}
}
if (PREDICT_FALSE(src_offset != Round4(length))) { return FONT_COMPRESSION_FAILURE();
}
returntrue;
}
// Write everything before the actual table data bool WriteHeaders(const uint8_t* data, size_t length, RebuildMetadata* metadata,
WOFF2Header* hdr, WOFF2Out* out) {
std::vector<uint8_t> output(ComputeOffsetToFirstTable(*hdr), 0);
// Re-order tables in output (OTSpec) order
std::vector<Table> sorted_tables(hdr->tables); if (hdr->header_version) { // collection; we have to sort the table offset vector in each font for (auto& ttc_font : hdr->ttc_fonts) {
std::map<uint32_t, uint16_t> sorted_index_by_tag; for (auto table_index : ttc_font.table_indices) {
sorted_index_by_tag[hdr->tables[table_index].tag] = table_index;
}
uint16_t index = 0; for (auto& i : sorted_index_by_tag) {
ttc_font.table_indices[index++] = i.second;
}
}
} else { // non-collection; we can just sort the tables
std::sort(sorted_tables.begin(), sorted_tables.end());
}
// Start building the font
uint8_t* result = &output[0];
size_t offset = 0; if (hdr->header_version) { // TTC header
offset = StoreU32(result, offset, hdr->flavor); // TAG TTCTag
offset = StoreU32(result, offset, hdr->header_version); // FIXED Version
offset = StoreU32(result, offset, hdr->ttc_fonts.size()); // ULONG numFonts // Space for ULONG OffsetTable[numFonts] (zeroed initially)
size_t offset_table = offset; // keep start of offset table for later for (size_t i = 0; i < hdr->ttc_fonts.size(); i++) {
offset = StoreU32(result, offset, 0); // will fill real values in later
} // space for DSIG fields for header v2 if (hdr->header_version == 0x00020000) {
offset = StoreU32(result, offset, 0); // ULONG ulDsigTag
offset = StoreU32(result, offset, 0); // ULONG ulDsigLength
offset = StoreU32(result, offset, 0); // ULONG ulDsigOffset
}
// write Offset Tables and store the location of each in TTC Header
metadata->font_infos.resize(hdr->ttc_fonts.size()); for (size_t i = 0; i < hdr->ttc_fonts.size(); i++) {
TtcFont& ttc_font = hdr->ttc_fonts[i];
const uint8_t* src_buf = data + hdr.compressed_offset;
std::vector<uint8_t> uncompressed_buf(hdr.uncompressed_size); if (PREDICT_FALSE(hdr.uncompressed_size < 1)) { return FONT_COMPRESSION_FAILURE();
} if (PREDICT_FALSE(!Woff2Uncompress(&uncompressed_buf[0],
hdr.uncompressed_size, src_buf,
hdr.compressed_length))) { return FONT_COMPRESSION_FAILURE();
}
for (size_t i = 0; i < metadata.font_infos.size(); i++) { if (PREDICT_FALSE(!ReconstructFont(&uncompressed_buf[0],
hdr.uncompressed_size,
&metadata, &hdr, i, out))) { return FONT_COMPRESSION_FAILURE();
}
}
returntrue;
}
} // namespace woff2
¤ Dauer der Verarbeitung: 0.37 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.