Merge update to Makefile
This commit is contained in:
@@ -16,7 +16,7 @@ endif
|
|||||||
SRCDIR = src
|
SRCDIR = src
|
||||||
|
|
||||||
OUROBJ = font.o glyph.o normalize.o table_tags.o transform.o \
|
OUROBJ = font.o glyph.o normalize.o table_tags.o transform.o \
|
||||||
woff2_dec.o woff2_enc.o
|
woff2_dec.o woff2_enc.o woff2_common.o variable_length.o
|
||||||
|
|
||||||
BROTLI = brotli
|
BROTLI = brotli
|
||||||
ENCOBJ = $(BROTLI)/enc/*.o
|
ENCOBJ = $(BROTLI)/enc/*.o
|
||||||
|
|||||||
+221
-29
@@ -22,6 +22,7 @@
|
|||||||
#include "./port.h"
|
#include "./port.h"
|
||||||
#include "./store_bytes.h"
|
#include "./store_bytes.h"
|
||||||
#include "./table_tags.h"
|
#include "./table_tags.h"
|
||||||
|
#include "./woff2_common.h"
|
||||||
|
|
||||||
namespace woff2 {
|
namespace woff2 {
|
||||||
|
|
||||||
@@ -35,24 +36,51 @@ const Font::Table* Font::FindTable(uint32_t tag) const {
|
|||||||
return it == tables.end() ? 0 : &it->second;
|
return it == tables.end() ? 0 : &it->second;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadFont(const uint8_t* data, size_t len, Font* font) {
|
std::vector<uint32_t> Font::OutputOrderedTags() const {
|
||||||
Buffer file(data, len);
|
std::vector<uint32_t> output_order;
|
||||||
|
|
||||||
|
for (const auto& i : tables) {
|
||||||
|
const Font::Table& table = i.second;
|
||||||
|
// This is a transformed table, we will write it together with the
|
||||||
|
// original version.
|
||||||
|
if (table.tag & 0x80808080) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
output_order.push_back(table.tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// alphabetize (not required), then put loca immediately after glyf (required)
|
||||||
|
std::sort(output_order.begin(), output_order.end());
|
||||||
|
auto glyf_loc = std::find(output_order.begin(), output_order.end(),
|
||||||
|
kGlyfTableTag);
|
||||||
|
auto loca_loc = std::find(output_order.begin(), output_order.end(),
|
||||||
|
kLocaTableTag);
|
||||||
|
if (glyf_loc != output_order.end() && loca_loc != output_order.end()) {
|
||||||
|
output_order.erase(loca_loc);
|
||||||
|
output_order.insert(std::find(output_order.begin(), output_order.end(),
|
||||||
|
kGlyfTableTag) + 1, kLocaTableTag);
|
||||||
|
}
|
||||||
|
|
||||||
|
return output_order;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadTrueTypeFont(Buffer* file, const uint8_t* data, size_t len,
|
||||||
|
Font* font) {
|
||||||
// We don't care about the search_range, entry_selector and range_shift
|
// We don't care about the search_range, entry_selector and range_shift
|
||||||
// fields, they will always be computed upon writing the font.
|
// fields, they will always be computed upon writing the font.
|
||||||
if (!file.ReadU32(&font->flavor) ||
|
if (!file->ReadU16(&font->num_tables) ||
|
||||||
!file.ReadU16(&font->num_tables) ||
|
!file->Skip(6)) {
|
||||||
!file.Skip(6)) {
|
|
||||||
return FONT_COMPRESSION_FAILURE();
|
return FONT_COMPRESSION_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
if (!file.ReadU32(&table.tag) ||
|
table.reuse_of = NULL;
|
||||||
!file.ReadU32(&table.checksum) ||
|
if (!file->ReadU32(&table.tag) ||
|
||||||
!file.ReadU32(&table.offset) ||
|
!file->ReadU32(&table.checksum) ||
|
||||||
!file.ReadU32(&table.length)) {
|
!file->ReadU32(&table.offset) ||
|
||||||
|
!file->ReadU32(&table.length)) {
|
||||||
return FONT_COMPRESSION_FAILURE();
|
return FONT_COMPRESSION_FAILURE();
|
||||||
}
|
}
|
||||||
if ((table.offset & 3) != 0 ||
|
if ((table.offset & 3) != 0 ||
|
||||||
@@ -76,9 +104,97 @@ bool ReadFont(const uint8_t* data, size_t len, Font* font) {
|
|||||||
}
|
}
|
||||||
last_offset = i.first + i.second;
|
last_offset = i.first + i.second;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ReadCollectionFont(Buffer* file, const uint8_t* data, size_t len,
|
||||||
|
Font* font,
|
||||||
|
std::map<uint32_t, Font::Table*>* all_tables) {
|
||||||
|
if (!file->ReadU32(&font->flavor)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
if (!ReadTrueTypeFont(file, data, len, font)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto& entry : font->tables) {
|
||||||
|
Font::Table& table = entry.second;
|
||||||
|
|
||||||
|
if (all_tables->find(table.offset) == all_tables->end()) {
|
||||||
|
(*all_tables)[table.offset] = font->FindTable(table.tag);
|
||||||
|
} else {
|
||||||
|
table.reuse_of = (*all_tables)[table.offset];
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadTrueTypeCollection(Buffer* file, const uint8_t* data, size_t len,
|
||||||
|
FontCollection* font_collection) {
|
||||||
|
uint32_t num_fonts;
|
||||||
|
|
||||||
|
if (!file->ReadU32(&font_collection->header_version) ||
|
||||||
|
!file->ReadU32(&num_fonts)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint32_t> offsets;
|
||||||
|
for (auto i = 0; i < num_fonts; i++) {
|
||||||
|
uint32_t offset;
|
||||||
|
if (!file->ReadU32(&offset)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
offsets.push_back(offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
font_collection->fonts.resize(offsets.size());
|
||||||
|
std::vector<Font>::iterator font_it = font_collection->fonts.begin();
|
||||||
|
|
||||||
|
std::map<uint32_t, Font::Table*> all_tables;
|
||||||
|
for (const auto offset : offsets) {
|
||||||
|
file->set_offset(offset);
|
||||||
|
Font& font = *font_it++;
|
||||||
|
if (!ReadCollectionFont(file, data, len, &font, &all_tables)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadFont(const uint8_t* data, size_t len, Font* font) {
|
||||||
|
Buffer file(data, len);
|
||||||
|
|
||||||
|
if (!file.ReadU32(&font->flavor)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font->flavor == kTtcFontFlavor) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
return ReadTrueTypeFont(&file, data, len, font);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadFontCollection(const uint8_t* data, size_t len,
|
||||||
|
FontCollection* font_collection) {
|
||||||
|
Buffer file(data, len);
|
||||||
|
|
||||||
|
uint32_t flavor;
|
||||||
|
if (!file.ReadU32(&flavor)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flavor != kTtcFontFlavor) {
|
||||||
|
font_collection->fonts.resize(1);
|
||||||
|
Font& font = font_collection->fonts[0];
|
||||||
|
font.flavor = flavor;
|
||||||
|
return ReadTrueTypeFont(&file, data, len, &font);
|
||||||
|
}
|
||||||
|
return ReadTrueTypeCollection(&file, data, len, font_collection);
|
||||||
|
}
|
||||||
|
|
||||||
size_t FontFileSize(const Font& font) {
|
size_t FontFileSize(const Font& font) {
|
||||||
size_t max_offset = 12ULL + 16ULL * font.num_tables;
|
size_t max_offset = 12ULL + 16ULL * font.num_tables;
|
||||||
for (const auto& i : font.tables) {
|
for (const auto& i : font.tables) {
|
||||||
@@ -90,29 +206,34 @@ size_t FontFileSize(const Font& font) {
|
|||||||
return max_offset;
|
return max_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t FontCollectionFileSize(const FontCollection& font_collection) {
|
||||||
|
size_t max_offset = 0;
|
||||||
|
for (auto& font : font_collection.fonts) {
|
||||||
|
// font file size actually just finds max offset
|
||||||
|
max_offset = std::max(max_offset, FontFileSize(font));
|
||||||
|
}
|
||||||
|
return max_offset;
|
||||||
|
}
|
||||||
|
|
||||||
bool WriteFont(const Font& font, uint8_t* dst, size_t dst_size) {
|
bool WriteFont(const Font& font, uint8_t* dst, size_t dst_size) {
|
||||||
if (dst_size < 12ULL + 16ULL * font.num_tables) {
|
size_t offset = 0;
|
||||||
|
return WriteFont(font, &offset, dst, dst_size, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WriteTable(const Font::Table& table, size_t* offset, uint8_t* dst,
|
||||||
|
size_t dst_size) {
|
||||||
|
StoreU32(table.tag, offset, dst);
|
||||||
|
StoreU32(table.checksum, offset, dst);
|
||||||
|
StoreU32(table.offset, offset, dst);
|
||||||
|
StoreU32(table.length, offset, dst);
|
||||||
|
|
||||||
|
if (table.offset + table.length < table.offset ||
|
||||||
|
dst_size < table.offset + table.length) {
|
||||||
return FONT_COMPRESSION_FAILURE();
|
return FONT_COMPRESSION_FAILURE();
|
||||||
}
|
}
|
||||||
size_t offset = 0;
|
|
||||||
StoreU32(font.flavor, &offset, dst);
|
// Write the actual table data if it's the first time we've seen it
|
||||||
Store16(font.num_tables, &offset, dst);
|
if (!table.IsReused()) {
|
||||||
uint16_t max_pow2 = font.num_tables ? Log2Floor(font.num_tables) : 0;
|
|
||||||
uint16_t search_range = max_pow2 ? 1 << (max_pow2 + 4) : 0;
|
|
||||||
uint16_t range_shift = (font.num_tables << 4) - search_range;
|
|
||||||
Store16(search_range, &offset, dst);
|
|
||||||
Store16(max_pow2, &offset, dst);
|
|
||||||
Store16(range_shift, &offset, dst);
|
|
||||||
for (const auto& i : font.tables) {
|
|
||||||
const Font::Table& table = i.second;
|
|
||||||
StoreU32(table.tag, &offset, dst);
|
|
||||||
StoreU32(table.checksum, &offset, dst);
|
|
||||||
StoreU32(table.offset, &offset, dst);
|
|
||||||
StoreU32(table.length, &offset, dst);
|
|
||||||
if (table.offset + table.length < table.offset ||
|
|
||||||
dst_size < table.offset + table.length) {
|
|
||||||
return FONT_COMPRESSION_FAILURE();
|
|
||||||
}
|
|
||||||
memcpy(dst + table.offset, table.data, table.length);
|
memcpy(dst + table.offset, table.data, table.length);
|
||||||
size_t padding_size = (4 - (table.length & 3)) & 3;
|
size_t padding_size = (4 - (table.length & 3)) & 3;
|
||||||
if (table.offset + table.length + padding_size < padding_size ||
|
if (table.offset + table.length + padding_size < padding_size ||
|
||||||
@@ -124,6 +245,73 @@ bool WriteFont(const Font& font, uint8_t* dst, size_t dst_size) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool WriteFont(const Font& font, size_t* offset, uint8_t* dst,
|
||||||
|
size_t dst_size, bool reorder_tables) {
|
||||||
|
if (dst_size < 12ULL + 16ULL * font.num_tables) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
StoreU32(font.flavor, offset, dst);
|
||||||
|
Store16(font.num_tables, offset, dst);
|
||||||
|
uint16_t max_pow2 = font.num_tables ? Log2Floor(font.num_tables) : 0;
|
||||||
|
uint16_t search_range = max_pow2 ? 1 << (max_pow2 + 4) : 0;
|
||||||
|
uint16_t range_shift = (font.num_tables << 4) - search_range;
|
||||||
|
Store16(search_range, offset, dst);
|
||||||
|
Store16(max_pow2, offset, dst);
|
||||||
|
Store16(range_shift, offset, dst);
|
||||||
|
if (reorder_tables) {
|
||||||
|
for (const auto tag : font.OutputOrderedTags()) {
|
||||||
|
if (!WriteTable(font.tables.at(tag), offset, dst, dst_size)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const auto& i : font.tables) {
|
||||||
|
if (!WriteTable(i.second, offset, dst, dst_size)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WriteFontCollection(const FontCollection& font_collection, uint8_t* dst,
|
||||||
|
size_t dst_size) {
|
||||||
|
size_t offset = 0;
|
||||||
|
|
||||||
|
// It's simpler if this just a simple sfnt
|
||||||
|
if (font_collection.fonts.size() == 1) {
|
||||||
|
return WriteFont(font_collection.fonts[0], &offset, dst, dst_size, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write TTC header
|
||||||
|
StoreU32(kTtcFontFlavor, &offset, dst);
|
||||||
|
StoreU32(font_collection.header_version, &offset, dst);
|
||||||
|
StoreU32(font_collection.fonts.size(), &offset, dst);
|
||||||
|
|
||||||
|
// Offset Table, zeroed for now
|
||||||
|
size_t offset_table = offset; // where to write offsets later
|
||||||
|
for (int i = 0; i < font_collection.fonts.size(); i++) {
|
||||||
|
StoreU32(0, &offset, dst);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (font_collection.header_version == 0x00020000) {
|
||||||
|
StoreU32(0, &offset, dst); // ulDsigTag
|
||||||
|
StoreU32(0, &offset, dst); // ulDsigLength
|
||||||
|
StoreU32(0, &offset, dst); // ulDsigOffset
|
||||||
|
}
|
||||||
|
|
||||||
|
// Write fonts and their offsets.
|
||||||
|
for (int i = 0; i < font_collection.fonts.size(); i++) {
|
||||||
|
const auto& font = font_collection.fonts[i];
|
||||||
|
StoreU32(offset, &offset_table, dst);
|
||||||
|
if (!WriteFont(font, &offset, dst, dst_size, false)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int NumGlyphs(const Font& font) {
|
int NumGlyphs(const Font& font) {
|
||||||
const Font::Table* head_table = font.FindTable(kHeadTableTag);
|
const Font::Table* head_table = font.FindTable(kHeadTableTag);
|
||||||
const Font::Table* loca_table = font.FindTable(kLocaTableTag);
|
const Font::Table* loca_table = font.FindTable(kLocaTableTag);
|
||||||
@@ -143,6 +331,10 @@ int IndexFormat(const Font& font) {
|
|||||||
return head_table->data[51];
|
return head_table->data[51];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool Font::Table::IsReused() const {
|
||||||
|
return this->reuse_of != NULL;
|
||||||
|
}
|
||||||
|
|
||||||
bool GetGlyphData(const Font& font, int glyph_index,
|
bool GetGlyphData(const Font& font, int glyph_index,
|
||||||
const uint8_t** glyph_data, size_t* glyph_size) {
|
const uint8_t** glyph_data, size_t* glyph_size) {
|
||||||
if (glyph_index < 0) {
|
if (glyph_index < 0) {
|
||||||
|
|||||||
+30
-1
@@ -41,25 +41,54 @@ struct Font {
|
|||||||
|
|
||||||
// Buffer used to mutate the data before writing out.
|
// Buffer used to mutate the data before writing out.
|
||||||
std::vector<uint8_t> buffer;
|
std::vector<uint8_t> buffer;
|
||||||
|
|
||||||
|
// If we've seen this tag/offset before, pointer to the first time we saw it
|
||||||
|
// If this is the first time we've seen this table, NULL
|
||||||
|
// Intended use is to bypass re-processing tables
|
||||||
|
Font::Table* reuse_of;
|
||||||
|
|
||||||
|
// Is this table reused by a TTC
|
||||||
|
bool IsReused() const;
|
||||||
};
|
};
|
||||||
std::map<uint32_t, Table> tables;
|
std::map<uint32_t, Table> tables;
|
||||||
|
std::vector<uint32_t> OutputOrderedTags() const;
|
||||||
|
|
||||||
Table* FindTable(uint32_t tag);
|
Table* FindTable(uint32_t tag);
|
||||||
const Table* FindTable(uint32_t tag) const;
|
const Table* FindTable(uint32_t tag) const;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Accomodates both singular (OTF, TTF) and collection (TTC) fonts
|
||||||
|
struct FontCollection {
|
||||||
|
uint32_t header_version;
|
||||||
|
// (offset, first use of table*) pairs
|
||||||
|
std::map<uint32_t, Font::Table*> tables;
|
||||||
|
std::vector<Font> fonts;
|
||||||
|
};
|
||||||
|
|
||||||
// Parses the font from the given data. Returns false on parsing failure or
|
// Parses the font from the given data. Returns false on parsing failure or
|
||||||
// buffer overflow. The font is valid only so long the input data pointer is
|
// buffer overflow. The font is valid only so long the input data pointer is
|
||||||
// valid.
|
// valid. Does NOT support collections.
|
||||||
bool ReadFont(const uint8_t* data, size_t len, Font* font);
|
bool ReadFont(const uint8_t* data, size_t len, Font* font);
|
||||||
|
|
||||||
|
// Parses the font from the given data. Returns false on parsing failure or
|
||||||
|
// buffer overflow. The font is valid only so long the input data pointer is
|
||||||
|
// valid. Supports collections.
|
||||||
|
bool ReadFontCollection(const uint8_t* data, size_t len, FontCollection* fonts);
|
||||||
|
|
||||||
// Returns the file size of the font.
|
// Returns the file size of the font.
|
||||||
size_t FontFileSize(const Font& font);
|
size_t FontFileSize(const Font& font);
|
||||||
|
size_t FontCollectionFileSize(const FontCollection& font);
|
||||||
|
|
||||||
// Writes the font into the specified dst buffer. The dst_size should be the
|
// Writes the font into the specified dst buffer. The dst_size should be the
|
||||||
// same as returned by FontFileSize(). Returns false upon buffer overflow (which
|
// same as returned by FontFileSize(). Returns false upon buffer overflow (which
|
||||||
// should not happen if dst_size was computed by FontFileSize()).
|
// should not happen if dst_size was computed by FontFileSize()).
|
||||||
bool WriteFont(const Font& font, uint8_t* dst, size_t dst_size);
|
bool WriteFont(const Font& font, uint8_t* dst, size_t dst_size);
|
||||||
|
// Write the font at a specific offset, optionally reordering tables
|
||||||
|
bool WriteFont(const Font& font, size_t* offset, uint8_t* dst, size_t dst_size,
|
||||||
|
bool reorder_tables);
|
||||||
|
|
||||||
|
bool WriteFontCollection(const FontCollection& font_collection, uint8_t* dst,
|
||||||
|
size_t dst_size);
|
||||||
|
|
||||||
// Returns the number of glyphs in the font.
|
// Returns the number of glyphs in the font.
|
||||||
// NOTE: Currently this works only for TrueType-flavored fonts, will return
|
// NOTE: Currently this works only for TrueType-flavored fonts, will return
|
||||||
|
|||||||
+70
-17
@@ -26,6 +26,7 @@
|
|||||||
#include "./round.h"
|
#include "./round.h"
|
||||||
#include "./store_bytes.h"
|
#include "./store_bytes.h"
|
||||||
#include "./table_tags.h"
|
#include "./table_tags.h"
|
||||||
|
#include "./woff2_common.h"
|
||||||
|
|
||||||
namespace woff2 {
|
namespace woff2 {
|
||||||
|
|
||||||
@@ -123,6 +124,9 @@ bool MakeEditableBuffer(Font* font, int tableTag) {
|
|||||||
if (table == NULL) {
|
if (table == NULL) {
|
||||||
return FONT_COMPRESSION_FAILURE();
|
return FONT_COMPRESSION_FAILURE();
|
||||||
}
|
}
|
||||||
|
if (table->IsReused()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
int sz = Round4(table->length);
|
int sz = Round4(table->length);
|
||||||
table->buffer.resize(sz);
|
table->buffer.resize(sz);
|
||||||
uint8_t* buf = &table->buffer[0];
|
uint8_t* buf = &table->buffer[0];
|
||||||
@@ -148,6 +152,15 @@ bool NormalizeGlyphs(Font* font) {
|
|||||||
if (loca_table == NULL || glyf_table == NULL) {
|
if (loca_table == NULL || glyf_table == NULL) {
|
||||||
return FONT_COMPRESSION_FAILURE();
|
return FONT_COMPRESSION_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Must share neither or both loca & glyf
|
||||||
|
if (loca_table->IsReused() != glyf_table->IsReused()) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
if (loca_table->IsReused()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
int index_fmt = head_table->data[51];
|
int index_fmt = head_table->data[51];
|
||||||
int num_glyphs = NumGlyphs(*font);
|
int num_glyphs = NumGlyphs(*font);
|
||||||
|
|
||||||
@@ -184,26 +197,16 @@ bool NormalizeGlyphs(Font* font) {
|
|||||||
|
|
||||||
bool NormalizeOffsets(Font* font) {
|
bool NormalizeOffsets(Font* font) {
|
||||||
uint32_t offset = 12 + 16 * font->num_tables;
|
uint32_t offset = 12 + 16 * font->num_tables;
|
||||||
for (auto& i : font->tables) {
|
for (auto tag : font->OutputOrderedTags()) {
|
||||||
i.second.offset = offset;
|
auto& table = font->tables[tag];
|
||||||
offset += Round4(i.second.length);
|
table.offset = offset;
|
||||||
|
offset += Round4(table.length);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
uint32_t ComputeChecksum(const uint8_t* buf, size_t size) {
|
|
||||||
uint32_t checksum = 0;
|
|
||||||
for (size_t i = 0; i < size; i += 4) {
|
|
||||||
checksum += ((buf[i] << 24) |
|
|
||||||
(buf[i + 1] << 16) |
|
|
||||||
(buf[i + 2] << 8) |
|
|
||||||
buf[i + 3]);
|
|
||||||
}
|
|
||||||
return checksum;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t ComputeHeaderChecksum(const Font& font) {
|
uint32_t ComputeHeaderChecksum(const Font& font) {
|
||||||
uint32_t checksum = font.flavor;
|
uint32_t checksum = font.flavor;
|
||||||
uint16_t max_pow2 = font.num_tables ? Log2Floor(font.num_tables) : 0;
|
uint16_t max_pow2 = font.num_tables ? Log2Floor(font.num_tables) : 0;
|
||||||
@@ -224,6 +227,9 @@ uint32_t ComputeHeaderChecksum(const Font& font) {
|
|||||||
|
|
||||||
bool FixChecksums(Font* font) {
|
bool FixChecksums(Font* font) {
|
||||||
Font::Table* head_table = font->FindTable(kHeadTableTag);
|
Font::Table* head_table = font->FindTable(kHeadTableTag);
|
||||||
|
if (head_table->reuse_of != NULL) {
|
||||||
|
head_table = head_table->reuse_of;
|
||||||
|
}
|
||||||
if (head_table == NULL || head_table->length < 12) {
|
if (head_table == NULL || head_table->length < 12) {
|
||||||
return FONT_COMPRESSION_FAILURE();
|
return FONT_COMPRESSION_FAILURE();
|
||||||
}
|
}
|
||||||
@@ -233,21 +239,68 @@ bool FixChecksums(Font* font) {
|
|||||||
uint32_t file_checksum = 0;
|
uint32_t file_checksum = 0;
|
||||||
for (auto& i : font->tables) {
|
for (auto& i : font->tables) {
|
||||||
Font::Table* table = &i.second;
|
Font::Table* table = &i.second;
|
||||||
table->checksum = ComputeChecksum(table->data, table->length);
|
if (table->IsReused()) {
|
||||||
|
table = table->reuse_of;
|
||||||
|
}
|
||||||
|
table->checksum = ComputeULongSum(table->data, table->length);
|
||||||
file_checksum += table->checksum;
|
file_checksum += table->checksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
file_checksum += ComputeHeaderChecksum(*font);
|
file_checksum += ComputeHeaderChecksum(*font);
|
||||||
offset = 8;
|
offset = 8;
|
||||||
StoreU32(0xb1b0afba - file_checksum, &offset, head_buf);
|
StoreU32(0xb1b0afba - file_checksum, &offset, head_buf);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool NormalizeFont(Font* font) {
|
bool NormalizeWithoutFixingChecksums(Font* font) {
|
||||||
return (MakeEditableBuffer(font, kHeadTableTag) &&
|
return (MakeEditableBuffer(font, kHeadTableTag) &&
|
||||||
RemoveDigitalSignature(font) &&
|
RemoveDigitalSignature(font) &&
|
||||||
NormalizeGlyphs(font) &&
|
NormalizeGlyphs(font) &&
|
||||||
NormalizeOffsets(font) &&
|
NormalizeOffsets(font));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NormalizeFont(Font* font) {
|
||||||
|
return (NormalizeWithoutFixingChecksums(font) &&
|
||||||
FixChecksums(font));
|
FixChecksums(font));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool NormalizeFontCollection(FontCollection* font_collection) {
|
||||||
|
if (font_collection->fonts.size() == 1) {
|
||||||
|
return NormalizeFont(&font_collection->fonts[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t offset = CollectionHeaderSize(font_collection->header_version,
|
||||||
|
font_collection->fonts.size());
|
||||||
|
for (auto& font : font_collection->fonts) {
|
||||||
|
if (!NormalizeWithoutFixingChecksums(&font)) {
|
||||||
|
fprintf(stderr, "Font normalization failed.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
offset += kSfntHeaderSize + kSfntEntrySize * font.num_tables;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start table offsets after TTC Header and Sfnt Headers
|
||||||
|
for (auto& font : font_collection->fonts) {
|
||||||
|
for (auto tag : font.OutputOrderedTags()) {
|
||||||
|
Font::Table& table = font.tables[tag];
|
||||||
|
if (table.IsReused()) {
|
||||||
|
table.offset = table.reuse_of->offset;
|
||||||
|
} else {
|
||||||
|
table.offset = offset;
|
||||||
|
offset += Round4(table.length);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now we can fix the checksums
|
||||||
|
for (auto& font : font_collection->fonts) {
|
||||||
|
if (!FixChecksums(&font)) {
|
||||||
|
fprintf(stderr, "Failed to fix checksums\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace woff2
|
} // namespace woff2
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
namespace woff2 {
|
namespace woff2 {
|
||||||
|
|
||||||
struct Font;
|
struct Font;
|
||||||
|
struct FontCollection;
|
||||||
|
|
||||||
// Changes the offset fields of the table headers so that the data for the
|
// Changes the offset fields of the table headers so that the data for the
|
||||||
// tables will be written in order of increasing tag values, without any gaps
|
// tables will be written in order of increasing tag values, without any gaps
|
||||||
@@ -39,6 +40,7 @@ bool NormalizeGlyphs(Font* font);
|
|||||||
|
|
||||||
// Performs all of the normalization steps above.
|
// Performs all of the normalization steps above.
|
||||||
bool NormalizeFont(Font* font);
|
bool NormalizeFont(Font* font);
|
||||||
|
bool NormalizeFontCollection(FontCollection* font_collection);
|
||||||
|
|
||||||
} // namespace woff2
|
} // namespace woff2
|
||||||
|
|
||||||
|
|||||||
+12
-18
@@ -22,6 +22,7 @@
|
|||||||
#include "./font.h"
|
#include "./font.h"
|
||||||
#include "./glyph.h"
|
#include "./glyph.h"
|
||||||
#include "./table_tags.h"
|
#include "./table_tags.h"
|
||||||
|
#include "./variable_length.h"
|
||||||
|
|
||||||
namespace woff2 {
|
namespace woff2 {
|
||||||
|
|
||||||
@@ -55,22 +56,6 @@ void WriteLong(std::vector<uint8_t>* out, int value) {
|
|||||||
out->push_back(value & 255);
|
out->push_back(value & 255);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Write255UShort(std::vector<uint8_t>* out, int value) {
|
|
||||||
if (value < 253) {
|
|
||||||
out->push_back(value);
|
|
||||||
} else if (value < 506) {
|
|
||||||
out->push_back(255);
|
|
||||||
out->push_back(value - 253);
|
|
||||||
} else if (value < 762) {
|
|
||||||
out->push_back(254);
|
|
||||||
out->push_back(value - 506);
|
|
||||||
} else {
|
|
||||||
out->push_back(253);
|
|
||||||
out->push_back(value >> 8);
|
|
||||||
out->push_back(value & 0xff);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Glyf table preprocessing, based on
|
// Glyf table preprocessing, based on
|
||||||
// GlyfEncoder.java
|
// GlyfEncoder.java
|
||||||
// but only the "sbbox" and "cbbox" options are supported.
|
// but only the "sbbox" and "cbbox" options are supported.
|
||||||
@@ -228,9 +213,18 @@ class GlyfEncoder {
|
|||||||
|
|
||||||
bool TransformGlyfAndLocaTables(Font* font) {
|
bool TransformGlyfAndLocaTables(Font* font) {
|
||||||
// no transform for CFF
|
// no transform for CFF
|
||||||
|
const Font::Table* glyf_table = font->FindTable(kGlyfTableTag);
|
||||||
|
const Font::Table* loca_table = font->FindTable(kLocaTableTag);
|
||||||
if (font->FindTable(kCffTableTag) != NULL
|
if (font->FindTable(kCffTableTag) != NULL
|
||||||
&& font->FindTable(kGlyfTableTag) == NULL
|
&& glyf_table == NULL
|
||||||
&& font->FindTable(kLocaTableTag) == NULL) {
|
&& loca_table == NULL) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Must share neither or both loca/glyf
|
||||||
|
if (glyf_table->IsReused() != loca_table->IsReused()) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
if (glyf_table->IsReused()) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Font::Table* transformed_glyf = &font->tables[kGlyfTableTag ^ 0x80808080];
|
Font::Table* transformed_glyf = &font->tables[kGlyfTableTag ^ 0x80808080];
|
||||||
|
|||||||
@@ -0,0 +1,133 @@
|
|||||||
|
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
// Helper functions for woff2 variable length types: 255UInt16 and UIntBase128
|
||||||
|
|
||||||
|
#include "./variable_length.h"
|
||||||
|
|
||||||
|
namespace woff2 {
|
||||||
|
|
||||||
|
size_t Size255UShort(uint16_t value) {
|
||||||
|
size_t result = 3;
|
||||||
|
if (value < 253) {
|
||||||
|
result = 1;
|
||||||
|
} else if (value < 762) {
|
||||||
|
result = 2;
|
||||||
|
} else {
|
||||||
|
result = 3;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Write255UShort(std::vector<uint8_t>* out, int value) {
|
||||||
|
if (value < 253) {
|
||||||
|
out->push_back(value);
|
||||||
|
} else if (value < 506) {
|
||||||
|
out->push_back(255);
|
||||||
|
out->push_back(value - 253);
|
||||||
|
} else if (value < 762) {
|
||||||
|
out->push_back(254);
|
||||||
|
out->push_back(value - 506);
|
||||||
|
} else {
|
||||||
|
out->push_back(253);
|
||||||
|
out->push_back(value >> 8);
|
||||||
|
out->push_back(value & 0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Store255UShort(int val, size_t* offset, uint8_t* dst) {
|
||||||
|
std::vector<uint8_t> packed;
|
||||||
|
Write255UShort(&packed, val);
|
||||||
|
for (uint8_t val : packed) {
|
||||||
|
dst[(*offset)++] = val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on section 6.1.1 of MicroType Express draft spec
|
||||||
|
bool Read255UShort(Buffer* buf, unsigned int* value) {
|
||||||
|
static const int kWordCode = 253;
|
||||||
|
static const int kOneMoreByteCode2 = 254;
|
||||||
|
static const int kOneMoreByteCode1 = 255;
|
||||||
|
static const int kLowestUCode = 253;
|
||||||
|
uint8_t code = 0;
|
||||||
|
if (!buf->ReadU8(&code)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
if (code == kWordCode) {
|
||||||
|
uint16_t result = 0;
|
||||||
|
if (!buf->ReadU16(&result)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
*value = result;
|
||||||
|
return true;
|
||||||
|
} else if (code == kOneMoreByteCode1) {
|
||||||
|
uint8_t result = 0;
|
||||||
|
if (!buf->ReadU8(&result)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
*value = result + kLowestUCode;
|
||||||
|
return true;
|
||||||
|
} else if (code == kOneMoreByteCode2) {
|
||||||
|
uint8_t result = 0;
|
||||||
|
if (!buf->ReadU8(&result)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
*value = result + kLowestUCode * 2;
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
*value = code;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ReadBase128(Buffer* buf, uint32_t* value) {
|
||||||
|
uint32_t result = 0;
|
||||||
|
for (size_t i = 0; i < 5; ++i) {
|
||||||
|
uint8_t code = 0;
|
||||||
|
if (!buf->ReadU8(&code)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
// If any of the top seven bits are set then we're about to overflow.
|
||||||
|
if (result & 0xfe000000) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
result = (result << 7) | (code & 0x7f);
|
||||||
|
if ((code & 0x80) == 0) {
|
||||||
|
*value = result;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Make sure not to exceed the size bound
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t Base128Size(size_t n) {
|
||||||
|
size_t size = 1;
|
||||||
|
for (; n >= 128; n >>= 7) ++size;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StoreBase128(size_t len, size_t* offset, uint8_t* dst) {
|
||||||
|
size_t size = Base128Size(len);
|
||||||
|
for (int i = 0; i < size; ++i) {
|
||||||
|
int b = static_cast<int>((len >> (7 * (size - i - 1))) & 0x7f);
|
||||||
|
if (i < size - 1) {
|
||||||
|
b |= 0x80;
|
||||||
|
}
|
||||||
|
dst[(*offset)++] = b;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace woff2
|
||||||
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2015 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
// Helper functions for woff2 variable length types: 255UInt16 and UIntBase128
|
||||||
|
|
||||||
|
#ifndef WOFF2_VARIABLE_LENGTH_H_
|
||||||
|
#define WOFF2_VARIABLE_LENGTH_H_
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
#include <vector>
|
||||||
|
#include "./buffer.h"
|
||||||
|
|
||||||
|
namespace woff2 {
|
||||||
|
|
||||||
|
size_t Size255UShort(uint16_t value);
|
||||||
|
bool Read255UShort(Buffer* buf, unsigned int* value);
|
||||||
|
void Write255UShort(std::vector<uint8_t>* out, int value);
|
||||||
|
void Store255UShort(int val, size_t* offset, uint8_t* dst);
|
||||||
|
|
||||||
|
size_t Base128Size(size_t n);
|
||||||
|
bool ReadBase128(Buffer* buf, uint32_t* value);
|
||||||
|
void StoreBase128(size_t len, size_t* offset, uint8_t* dst);
|
||||||
|
|
||||||
|
} // namespace woff2
|
||||||
|
|
||||||
|
#endif // WOFF2_VARIABLE_LENGTH_H_
|
||||||
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2013 Google Inc. All Rights Reserved.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
//
|
||||||
|
// Helpers common across multiple parts of woff2
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "./woff2_common.h"
|
||||||
|
|
||||||
|
namespace woff2 {
|
||||||
|
|
||||||
|
|
||||||
|
uint32_t ComputeULongSum(const uint8_t* buf, size_t size) {
|
||||||
|
uint32_t checksum = 0;
|
||||||
|
for (size_t i = 0; i < size; i += 4) {
|
||||||
|
// We assume the addition is mod 2^32, which is valid because unsigned
|
||||||
|
checksum += (buf[i] << 24) | (buf[i + 1] << 16) |
|
||||||
|
(buf[i + 2] << 8) | buf[i + 3];
|
||||||
|
}
|
||||||
|
return checksum;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t CollectionHeaderSize(uint32_t header_version, uint32_t num_fonts) {
|
||||||
|
size_t size = 0;
|
||||||
|
if (header_version == 0x00020000) {
|
||||||
|
size += 12; // ulDsig{Tag,Length,Offset}
|
||||||
|
}
|
||||||
|
if (header_version == 0x00010000 || header_version == 0x00020000) {
|
||||||
|
size += 12 // TTCTag, Version, numFonts
|
||||||
|
+ 4 * num_fonts; // OffsetTable[numFonts]
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace woff2
|
||||||
+18
-4
@@ -17,8 +17,11 @@
|
|||||||
#ifndef WOFF2_WOFF2_COMMON_H_
|
#ifndef WOFF2_WOFF2_COMMON_H_
|
||||||
#define WOFF2_WOFF2_COMMON_H_
|
#define WOFF2_WOFF2_COMMON_H_
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace woff2 {
|
namespace woff2 {
|
||||||
|
|
||||||
static const uint32_t kWoff2Signature = 0x774f4632; // "wOF2"
|
static const uint32_t kWoff2Signature = 0x774f4632; // "wOF2"
|
||||||
@@ -26,6 +29,12 @@ static const uint32_t kWoff2Signature = 0x774f4632; // "wOF2"
|
|||||||
const unsigned int kWoff2FlagsContinueStream = 1 << 4;
|
const unsigned int kWoff2FlagsContinueStream = 1 << 4;
|
||||||
const unsigned int kWoff2FlagsTransform = 1 << 5;
|
const unsigned int kWoff2FlagsTransform = 1 << 5;
|
||||||
|
|
||||||
|
// TrueType Collection ID string: 'ttcf'
|
||||||
|
static const uint32_t kTtcFontFlavor = 0x74746366;
|
||||||
|
|
||||||
|
static const size_t kSfntHeaderSize = 12;
|
||||||
|
static const size_t kSfntEntrySize = 16;
|
||||||
|
|
||||||
struct Point {
|
struct Point {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
@@ -43,12 +52,17 @@ struct Table {
|
|||||||
uint32_t dst_offset;
|
uint32_t dst_offset;
|
||||||
uint32_t dst_length;
|
uint32_t dst_length;
|
||||||
const uint8_t* dst_data;
|
const uint8_t* dst_data;
|
||||||
|
|
||||||
bool operator<(const Table& other) const {
|
|
||||||
return tag < other.tag;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// Size of the collection header. 0 if version indicates this isn't a
|
||||||
|
// collection. Ref http://www.microsoft.com/typography/otspec/otff.htm,
|
||||||
|
// True Type Collections
|
||||||
|
size_t CollectionHeaderSize(uint32_t header_version, uint32_t num_fonts);
|
||||||
|
|
||||||
|
// Compute checksum over size bytes of buf
|
||||||
|
uint32_t ComputeULongSum(const uint8_t* buf, size_t size);
|
||||||
|
|
||||||
} // namespace woff2
|
} // namespace woff2
|
||||||
|
|
||||||
#endif // WOFF2_WOFF2_COMMON_H_
|
#endif // WOFF2_WOFF2_COMMON_H_
|
||||||
|
|||||||
+285
-124
@@ -21,14 +21,15 @@
|
|||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <algorithm>
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
#include "./buffer.h"
|
#include "./buffer.h"
|
||||||
#include "./decode.h"
|
#include "./decode.h"
|
||||||
#include "./round.h"
|
#include "./round.h"
|
||||||
#include "./store_bytes.h"
|
#include "./store_bytes.h"
|
||||||
#include "./table_tags.h"
|
#include "./table_tags.h"
|
||||||
|
#include "./variable_length.h"
|
||||||
#include "./woff2_common.h"
|
#include "./woff2_common.h"
|
||||||
|
|
||||||
namespace woff2 {
|
namespace woff2 {
|
||||||
@@ -56,70 +57,17 @@ const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6;
|
|||||||
const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7;
|
const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7;
|
||||||
const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
|
const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8;
|
||||||
|
|
||||||
const size_t kSfntHeaderSize = 12;
|
|
||||||
const size_t kSfntEntrySize = 16;
|
|
||||||
const size_t kCheckSumAdjustmentOffset = 8;
|
const size_t kCheckSumAdjustmentOffset = 8;
|
||||||
|
|
||||||
const size_t kEndPtsOfContoursOffset = 10;
|
const size_t kEndPtsOfContoursOffset = 10;
|
||||||
const size_t kCompositeGlyphBegin = 10;
|
const size_t kCompositeGlyphBegin = 10;
|
||||||
|
|
||||||
// Based on section 6.1.1 of MicroType Express draft spec
|
// metadata for a TTC font entry
|
||||||
bool Read255UShort(Buffer* buf, unsigned int* value) {
|
struct TtcFont {
|
||||||
static const int kWordCode = 253;
|
uint32_t flavor;
|
||||||
static const int kOneMoreByteCode2 = 254;
|
uint32_t dst_offset;
|
||||||
static const int kOneMoreByteCode1 = 255;
|
std::vector<uint16_t> table_indices;
|
||||||
static const int kLowestUCode = 253;
|
};
|
||||||
uint8_t code = 0;
|
|
||||||
if (!buf->ReadU8(&code)) {
|
|
||||||
return FONT_COMPRESSION_FAILURE();
|
|
||||||
}
|
|
||||||
if (code == kWordCode) {
|
|
||||||
uint16_t result = 0;
|
|
||||||
if (!buf->ReadU16(&result)) {
|
|
||||||
return FONT_COMPRESSION_FAILURE();
|
|
||||||
}
|
|
||||||
*value = result;
|
|
||||||
return true;
|
|
||||||
} else if (code == kOneMoreByteCode1) {
|
|
||||||
uint8_t result = 0;
|
|
||||||
if (!buf->ReadU8(&result)) {
|
|
||||||
return FONT_COMPRESSION_FAILURE();
|
|
||||||
}
|
|
||||||
*value = result + kLowestUCode;
|
|
||||||
return true;
|
|
||||||
} else if (code == kOneMoreByteCode2) {
|
|
||||||
uint8_t result = 0;
|
|
||||||
if (!buf->ReadU8(&result)) {
|
|
||||||
return FONT_COMPRESSION_FAILURE();
|
|
||||||
}
|
|
||||||
*value = result + kLowestUCode * 2;
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
*value = code;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ReadBase128(Buffer* buf, uint32_t* value) {
|
|
||||||
uint32_t result = 0;
|
|
||||||
for (size_t i = 0; i < 5; ++i) {
|
|
||||||
uint8_t code = 0;
|
|
||||||
if (!buf->ReadU8(&code)) {
|
|
||||||
return FONT_COMPRESSION_FAILURE();
|
|
||||||
}
|
|
||||||
// If any of the top seven bits are set then we're about to overflow.
|
|
||||||
if (result & 0xfe000000) {
|
|
||||||
return FONT_COMPRESSION_FAILURE();
|
|
||||||
}
|
|
||||||
result = (result << 7) | (code & 0x7f);
|
|
||||||
if ((code & 0x80) == 0) {
|
|
||||||
*value = result;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Make sure not to exceed the size bound
|
|
||||||
return FONT_COMPRESSION_FAILURE();
|
|
||||||
}
|
|
||||||
|
|
||||||
int WithSign(int flag, int baseval) {
|
int WithSign(int flag, int baseval) {
|
||||||
// Precondition: 0 <= baseval < 65536 (to avoid integer overflow)
|
// Precondition: 0 <= baseval < 65536 (to avoid integer overflow)
|
||||||
@@ -394,7 +342,7 @@ bool ProcessComposite(Buffer* composite_stream, uint8_t* dst,
|
|||||||
|
|
||||||
// Build TrueType loca table
|
// Build TrueType loca table
|
||||||
bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format,
|
bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format,
|
||||||
uint8_t* dst, size_t dst_size) {
|
uint8_t* dst, size_t dst_size) {
|
||||||
const uint64_t loca_size = loca_values.size();
|
const uint64_t loca_size = loca_values.size();
|
||||||
const uint64_t offset_size = index_format ? 4 : 2;
|
const uint64_t offset_size = index_format ? 4 : 2;
|
||||||
if ((loca_size << 2) >> 2 != loca_size) {
|
if ((loca_size << 2) >> 2 != loca_size) {
|
||||||
@@ -417,8 +365,8 @@ bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format,
|
|||||||
|
|
||||||
// Reconstruct entire glyf table based on transformed original
|
// Reconstruct entire glyf table based on transformed original
|
||||||
bool ReconstructGlyf(const uint8_t* data, size_t data_size,
|
bool ReconstructGlyf(const uint8_t* data, size_t data_size,
|
||||||
uint8_t* dst, size_t dst_size,
|
uint8_t* dst, size_t dst_size,
|
||||||
uint8_t* loca_buf, size_t loca_size) {
|
uint8_t* loca_buf, size_t loca_size) {
|
||||||
static const int kNumSubStreams = 7;
|
static const int kNumSubStreams = 7;
|
||||||
Buffer file(data, data_size);
|
Buffer file(data, data_size);
|
||||||
uint32_t version;
|
uint32_t version;
|
||||||
@@ -433,6 +381,7 @@ bool ReconstructGlyf(const uint8_t* data, size_t data_size,
|
|||||||
!file.ReadU16(&index_format)) {
|
!file.ReadU16(&index_format)) {
|
||||||
return FONT_COMPRESSION_FAILURE();
|
return FONT_COMPRESSION_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int offset = (2 + kNumSubStreams) * 4;
|
unsigned int offset = (2 + kNumSubStreams) * 4;
|
||||||
if (offset > data_size) {
|
if (offset > data_size) {
|
||||||
return FONT_COMPRESSION_FAILURE();
|
return FONT_COMPRESSION_FAILURE();
|
||||||
@@ -595,26 +544,33 @@ const Table* FindTable(const std::vector<Table>& tables, uint32_t tag) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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) {
|
||||||
|
if (glyf_table == NULL || loca_table == NULL) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
if (static_cast<uint64_t>(glyf_table->dst_offset + glyf_table->dst_length) >
|
||||||
|
dst_length) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
if (static_cast<uint64_t>(loca_table->dst_offset + loca_table->dst_length) >
|
||||||
|
dst_length) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
return ReconstructGlyf(transformed_buf, transformed_size,
|
||||||
|
dst + glyf_table->dst_offset, glyf_table->dst_length,
|
||||||
|
dst + loca_table->dst_offset, loca_table->dst_length);
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
if (glyf_table == NULL || loca_table == NULL) {
|
return ReconstructTransformedGlyf(transformed_buf, transformed_size,
|
||||||
return FONT_COMPRESSION_FAILURE();
|
glyf_table, loca_table, dst, dst_length);
|
||||||
}
|
|
||||||
if (static_cast<uint64_t>(glyf_table->dst_offset + glyf_table->dst_length) >
|
|
||||||
dst_length) {
|
|
||||||
return FONT_COMPRESSION_FAILURE();
|
|
||||||
}
|
|
||||||
if (static_cast<uint64_t>(loca_table->dst_offset + loca_table->dst_length) >
|
|
||||||
dst_length) {
|
|
||||||
return FONT_COMPRESSION_FAILURE();
|
|
||||||
}
|
|
||||||
return ReconstructGlyf(transformed_buf, transformed_size,
|
|
||||||
dst + glyf_table->dst_offset, glyf_table->dst_length,
|
|
||||||
dst + loca_table->dst_offset, loca_table->dst_length);
|
|
||||||
} else if (tag == kLocaTableTag) {
|
} else if (tag == kLocaTableTag) {
|
||||||
// processing was already done by glyf table, but validate
|
// processing was already done by glyf table, but validate
|
||||||
if (!FindTable(tables, kGlyfTableTag)) {
|
if (!FindTable(tables, kGlyfTableTag)) {
|
||||||
@@ -627,14 +583,70 @@ bool ReconstructTransformed(const std::vector<Table>& tables, uint32_t tag,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
uint32_t ComputeChecksum(const uint8_t* buf, size_t size) {
|
uint32_t ComputeChecksum(const Table* table, const uint8_t* dst) {
|
||||||
uint32_t checksum = 0;
|
return ComputeULongSum(dst + table->dst_offset, table->dst_length);
|
||||||
for (size_t i = 0; i < size; i += 4) {
|
}
|
||||||
// We assume the addition is mod 2^32, which is valid because unsigned
|
|
||||||
checksum += (buf[i] << 24) | (buf[i + 1] << 16) |
|
const Table* FindTable(TtcFont ttc_font, const std::vector<Table>& tables,
|
||||||
(buf[i + 2] << 8) | buf[i + 3];
|
uint32_t tag) {
|
||||||
|
for (const auto i : ttc_font.table_indices) {
|
||||||
|
if (tables[i].tag == tag) return &tables[i];
|
||||||
}
|
}
|
||||||
return checksum;
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool FixCollectionChecksums(size_t header_version,
|
||||||
|
const std::vector<Table>& tables, const std::vector<TtcFont>& ttc_fonts,
|
||||||
|
uint8_t* dst) {
|
||||||
|
size_t offset = CollectionHeaderSize(header_version, ttc_fonts.size());
|
||||||
|
|
||||||
|
for (const auto& ttc_font : ttc_fonts) {
|
||||||
|
offset += 12; // move to start of Offset Table
|
||||||
|
const std::vector<uint16_t>& table_indices = ttc_font.table_indices;
|
||||||
|
|
||||||
|
const Table* head_table = FindTable(ttc_font, tables, kHeadTableTag);
|
||||||
|
if (head_table == NULL ||
|
||||||
|
head_table->dst_length < kCheckSumAdjustmentOffset + 4) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t first_table_offset = std::numeric_limits<size_t>::max();
|
||||||
|
for (const auto index : table_indices) {
|
||||||
|
const auto& table = tables[index];
|
||||||
|
if (table.dst_offset < first_table_offset) {
|
||||||
|
first_table_offset = table.dst_offset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t adjustment_offset = head_table->dst_offset
|
||||||
|
+ kCheckSumAdjustmentOffset;
|
||||||
|
StoreU32(dst, adjustment_offset, 0);
|
||||||
|
|
||||||
|
uint32_t file_checksum = 0;
|
||||||
|
// compute each tables checksum
|
||||||
|
for (auto i = 0; i < table_indices.size(); i++) {
|
||||||
|
const Table& table = tables[table_indices[i]];
|
||||||
|
uint32_t table_checksum = ComputeChecksum(&table, dst);
|
||||||
|
size_t checksum_offset = offset + 4; // skip past tag to checkSum
|
||||||
|
|
||||||
|
// write the checksum for the Table Record
|
||||||
|
StoreU32(dst, checksum_offset, table_checksum);
|
||||||
|
file_checksum += table_checksum;
|
||||||
|
// next Table Record
|
||||||
|
offset += 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t header_size = kSfntHeaderSize +
|
||||||
|
kSfntEntrySize * table_indices.size();
|
||||||
|
uint32_t header_checksum = ComputeULongSum(dst + ttc_font.dst_offset,
|
||||||
|
header_size);
|
||||||
|
|
||||||
|
file_checksum += header_checksum;
|
||||||
|
uint32_t checksum_adjustment = 0xb1b0afba - file_checksum;
|
||||||
|
StoreU32(dst, adjustment_offset, checksum_adjustment);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool FixChecksums(const std::vector<Table>& tables, uint8_t* dst) {
|
bool FixChecksums(const std::vector<Table>& tables, uint8_t* dst) {
|
||||||
@@ -648,15 +660,12 @@ bool FixChecksums(const std::vector<Table>& tables, uint8_t* dst) {
|
|||||||
size_t n_tables = tables.size();
|
size_t n_tables = tables.size();
|
||||||
uint32_t file_checksum = 0;
|
uint32_t file_checksum = 0;
|
||||||
for (size_t i = 0; i < n_tables; ++i) {
|
for (size_t i = 0; i < n_tables; ++i) {
|
||||||
const Table* table = &tables[i];
|
uint32_t checksum = ComputeChecksum(&tables[i], dst);
|
||||||
size_t table_length = table->dst_length;
|
|
||||||
uint8_t* table_data = dst + table->dst_offset;
|
|
||||||
uint32_t checksum = ComputeChecksum(table_data, table_length);
|
|
||||||
StoreU32(dst, kSfntHeaderSize + i * kSfntEntrySize + 4, checksum);
|
StoreU32(dst, kSfntHeaderSize + i * kSfntEntrySize + 4, checksum);
|
||||||
file_checksum += checksum;
|
file_checksum += checksum;
|
||||||
}
|
}
|
||||||
file_checksum += ComputeChecksum(dst,
|
file_checksum += ComputeULongSum(dst,
|
||||||
kSfntHeaderSize + kSfntEntrySize * n_tables);
|
kSfntHeaderSize + kSfntEntrySize * n_tables);
|
||||||
uint32_t checksum_adjustment = 0xb1b0afba - file_checksum;
|
uint32_t checksum_adjustment = 0xb1b0afba - file_checksum;
|
||||||
StoreU32(dst, adjustment_offset, checksum_adjustment);
|
StoreU32(dst, adjustment_offset, checksum_adjustment);
|
||||||
return true;
|
return true;
|
||||||
@@ -673,7 +682,7 @@ bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadShortDirectory(Buffer* file, std::vector<Table>* tables,
|
bool ReadTableDirectory(Buffer* file, std::vector<Table>* tables,
|
||||||
size_t num_tables) {
|
size_t num_tables) {
|
||||||
for (size_t i = 0; i < num_tables; ++i) {
|
for (size_t i = 0; i < num_tables; ++i) {
|
||||||
Table* table = &(*tables)[i];
|
Table* table = &(*tables)[i];
|
||||||
@@ -732,6 +741,48 @@ size_t ComputeWOFF2FinalSize(const uint8_t* data, size_t length) {
|
|||||||
return total_length;
|
return total_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Writes a single Offset Table entry
|
||||||
|
size_t StoreOffsetTable(uint8_t* result, size_t offset, uint32_t flavor,
|
||||||
|
uint16_t num_tables) {
|
||||||
|
offset = StoreU32(result, offset, flavor); // sfnt version
|
||||||
|
offset = Store16(result, offset, num_tables); // num_tables
|
||||||
|
unsigned max_pow2 = 0;
|
||||||
|
while (1u << (max_pow2 + 1) <= num_tables) {
|
||||||
|
max_pow2++;
|
||||||
|
}
|
||||||
|
const uint16_t output_search_range = (1u << max_pow2) << 4;
|
||||||
|
offset = Store16(result, offset, output_search_range); // searchRange
|
||||||
|
offset = Store16(result, offset, max_pow2); // entrySelector
|
||||||
|
// rangeShift
|
||||||
|
offset = Store16(result, offset, (num_tables << 4) - output_search_range);
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t StoreTableEntry(uint8_t* result, const Table& table, size_t offset) {
|
||||||
|
offset = StoreU32(result, offset, table.tag);
|
||||||
|
offset = StoreU32(result, offset, 0); // checksum, to fill in later
|
||||||
|
offset = StoreU32(result, offset, table.dst_offset);
|
||||||
|
offset = StoreU32(result, offset, table.dst_length);
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
// First table goes after all the headers, table directory, etc
|
||||||
|
uint64_t ComputeOffsetToFirstTable(const uint32_t header_version,
|
||||||
|
const uint16_t num_tables,
|
||||||
|
const std::vector<TtcFont>& ttc_fonts) {
|
||||||
|
uint64_t offset = kSfntHeaderSize +
|
||||||
|
kSfntEntrySize * static_cast<uint64_t>(num_tables);
|
||||||
|
if (header_version) {
|
||||||
|
offset = CollectionHeaderSize(header_version, ttc_fonts.size())
|
||||||
|
+ kSfntHeaderSize * ttc_fonts.size();
|
||||||
|
for (const auto& ttc_font : ttc_fonts) {
|
||||||
|
offset +=
|
||||||
|
kSfntEntrySize * ttc_font.table_indices.size();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
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);
|
||||||
@@ -771,13 +822,83 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
|
|||||||
return FONT_COMPRESSION_FAILURE();
|
return FONT_COMPRESSION_FAILURE();
|
||||||
}
|
}
|
||||||
std::vector<Table> tables(num_tables);
|
std::vector<Table> tables(num_tables);
|
||||||
// Note: change below to ReadLongDirectory to enable long format.
|
if (!ReadTableDirectory(&file, &tables, num_tables)) {
|
||||||
if (!ReadShortDirectory(&file, &tables, num_tables)) {
|
|
||||||
return FONT_COMPRESSION_FAILURE();
|
return FONT_COMPRESSION_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t header_version = 0;
|
||||||
|
// for each font in a ttc, metadata to use when rebuilding
|
||||||
|
std::vector<TtcFont> ttc_fonts;
|
||||||
|
std::map<const Table*, const Table*> loca_by_glyf;
|
||||||
|
|
||||||
|
if (flavor == kTtcFontFlavor) {
|
||||||
|
if (!file.ReadU32(&header_version)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
uint32_t num_fonts;
|
||||||
|
if (!Read255UShort(&file, &num_fonts) || !num_fonts) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
ttc_fonts.resize(num_fonts);
|
||||||
|
|
||||||
|
for (auto i = 0; i < num_fonts; i++) {
|
||||||
|
TtcFont& ttc_font = ttc_fonts[i];
|
||||||
|
uint32_t num_tables;
|
||||||
|
if (!Read255UShort(&file, &num_tables) || !num_tables) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
if (!file.ReadU32(&ttc_font.flavor)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
ttc_font.table_indices.resize(num_tables);
|
||||||
|
|
||||||
|
const Table* glyf_table = NULL;
|
||||||
|
const Table* loca_table = NULL;
|
||||||
|
uint16_t glyf_idx;
|
||||||
|
uint16_t loca_idx;
|
||||||
|
|
||||||
|
for (auto j = 0; j < num_tables; j++) {
|
||||||
|
unsigned int table_idx;
|
||||||
|
if (!Read255UShort(&file, &table_idx)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
ttc_font.table_indices[j] = table_idx;
|
||||||
|
|
||||||
|
const Table& table = tables[table_idx];
|
||||||
|
if (table.tag == kLocaTableTag) {
|
||||||
|
loca_table = &table;
|
||||||
|
loca_idx = table_idx;
|
||||||
|
}
|
||||||
|
if (table.tag == kGlyfTableTag) {
|
||||||
|
glyf_table = &table;
|
||||||
|
glyf_idx = table_idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((glyf_table == NULL) != (loca_table == NULL)) {
|
||||||
|
fprintf(stderr, "Cannot have just one of glyf/loca\n");
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (glyf_table != NULL && loca_table != NULL) {
|
||||||
|
loca_by_glyf[glyf_table] = loca_table;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint64_t first_table_offset =
|
||||||
|
ComputeOffsetToFirstTable(header_version, num_tables, ttc_fonts);
|
||||||
|
|
||||||
|
if (first_table_offset > result_length) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
|
||||||
uint64_t src_offset = file.offset();
|
uint64_t src_offset = file.offset();
|
||||||
uint64_t dst_offset = kSfntHeaderSize +
|
uint64_t dst_offset = first_table_offset;
|
||||||
kSfntEntrySize * static_cast<uint64_t>(num_tables);
|
|
||||||
|
|
||||||
uint64_t uncompressed_sum = 0;
|
uint64_t uncompressed_sum = 0;
|
||||||
for (uint16_t i = 0; i < num_tables; ++i) {
|
for (uint16_t i = 0; i < num_tables; ++i) {
|
||||||
Table* table = &tables[i];
|
Table* table = &tables[i];
|
||||||
@@ -787,7 +908,7 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
|
|||||||
if (src_offset > std::numeric_limits<uint32_t>::max()) {
|
if (src_offset > std::numeric_limits<uint32_t>::max()) {
|
||||||
return FONT_COMPRESSION_FAILURE();
|
return FONT_COMPRESSION_FAILURE();
|
||||||
}
|
}
|
||||||
src_offset = Round4(src_offset); // TODO: reconsider
|
src_offset = Round4(src_offset);
|
||||||
table->dst_offset = dst_offset;
|
table->dst_offset = dst_offset;
|
||||||
dst_offset += table->dst_length;
|
dst_offset += table->dst_length;
|
||||||
if (dst_offset > std::numeric_limits<uint32_t>::max()) {
|
if (dst_offset > std::numeric_limits<uint32_t>::max()) {
|
||||||
@@ -805,38 +926,54 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
|
|||||||
return FONT_COMPRESSION_FAILURE();
|
return FONT_COMPRESSION_FAILURE();
|
||||||
}
|
}
|
||||||
if (src_offset > length || dst_offset > result_length) {
|
if (src_offset > length || dst_offset > result_length) {
|
||||||
return FONT_COMPRESSION_FAILURE();
|
fprintf(stderr, "offset fail; src_offset %lu length %lu "
|
||||||
}
|
"dst_offset %lu result_length %lu\n",
|
||||||
|
src_offset, length, dst_offset, result_length);
|
||||||
const uint32_t sfnt_header_and_table_directory_size = 12 + 16 * num_tables;
|
|
||||||
if (sfnt_header_and_table_directory_size > result_length) {
|
|
||||||
return FONT_COMPRESSION_FAILURE();
|
return FONT_COMPRESSION_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start building the font
|
// Start building the font
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
offset = StoreU32(result, offset, flavor);
|
size_t offset_table = 0;
|
||||||
offset = Store16(result, offset, num_tables);
|
if (header_version) {
|
||||||
unsigned max_pow2 = 0;
|
// TTC header
|
||||||
while (1u << (max_pow2 + 1) <= num_tables) {
|
offset = StoreU32(result, offset, flavor); // TAG TTCTag
|
||||||
max_pow2++;
|
offset = StoreU32(result, offset, header_version); // FIXED Version
|
||||||
}
|
offset = StoreU32(result, offset, ttc_fonts.size()); // ULONG numFonts
|
||||||
const uint16_t output_search_range = (1u << max_pow2) << 4;
|
// Space for ULONG OffsetTable[numFonts] (zeroed initially)
|
||||||
offset = Store16(result, offset, output_search_range);
|
offset_table = offset; // keep start of offset table for later
|
||||||
offset = Store16(result, offset, max_pow2);
|
for (int i = 0; i < ttc_fonts.size(); i++) {
|
||||||
offset = Store16(result, offset, (num_tables << 4) - output_search_range);
|
offset = StoreU32(result, offset, 0); // will fill real values in later
|
||||||
|
}
|
||||||
|
// space for DSIG fields for header v2
|
||||||
|
if (header_version == 0x00020000) {
|
||||||
|
offset = StoreU32(result, offset, 0); // ULONG ulDsigTag
|
||||||
|
offset = StoreU32(result, offset, 0); // ULONG ulDsigLength
|
||||||
|
offset = StoreU32(result, offset, 0); // ULONG ulDsigOffset
|
||||||
|
}
|
||||||
|
|
||||||
// sort tags in the table directory in ascending alphabetical order
|
// write Offset Tables and store the location of each in TTC Header
|
||||||
std::vector<Table> sorted_tables(tables);
|
for (auto& ttc_font : ttc_fonts) {
|
||||||
std::sort(sorted_tables.begin(), sorted_tables.end());
|
// write Offset Table location into TTC Header
|
||||||
|
offset_table = StoreU32(result, offset_table, offset);
|
||||||
|
|
||||||
for (uint16_t i = 0; i < num_tables; ++i) {
|
// write the actual offset table so our header doesn't lie
|
||||||
const Table* table = &sorted_tables[i];
|
ttc_font.dst_offset = offset;
|
||||||
offset = StoreU32(result, offset, table->tag);
|
offset = StoreOffsetTable(result, offset, ttc_font.flavor,
|
||||||
offset = StoreU32(result, offset, 0); // checksum, to fill in later
|
ttc_font.table_indices.size());
|
||||||
offset = StoreU32(result, offset, table->dst_offset);
|
|
||||||
offset = StoreU32(result, offset, table->dst_length);
|
// write table entries
|
||||||
|
for (const auto table_index : ttc_font.table_indices) {
|
||||||
|
offset = StoreTableEntry(result, tables[table_index], offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
offset = StoreOffsetTable(result, offset, flavor, num_tables);
|
||||||
|
for (uint16_t i = 0; i < num_tables; ++i) {
|
||||||
|
offset = StoreTableEntry(result, tables[i], offset);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> uncompressed_buf;
|
std::vector<uint8_t> uncompressed_buf;
|
||||||
bool continue_valid = false;
|
bool continue_valid = false;
|
||||||
const uint8_t* transform_buf = NULL;
|
const uint8_t* transform_buf = NULL;
|
||||||
@@ -879,12 +1016,26 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
|
|||||||
result_length) {
|
result_length) {
|
||||||
return FONT_COMPRESSION_FAILURE();
|
return FONT_COMPRESSION_FAILURE();
|
||||||
}
|
}
|
||||||
|
|
||||||
std::memcpy(result + table->dst_offset, transform_buf,
|
std::memcpy(result + table->dst_offset, transform_buf,
|
||||||
transform_length);
|
transform_length);
|
||||||
} else {
|
} else {
|
||||||
if (!ReconstructTransformed(tables, table->tag,
|
if (header_version) {
|
||||||
transform_buf, transform_length, result, result_length)) {
|
if (table->tag == kGlyfTableTag) {
|
||||||
return FONT_COMPRESSION_FAILURE();
|
const Table* loca_table = loca_by_glyf[table];
|
||||||
|
if (!ReconstructTransformedGlyf(transform_buf, transform_length,
|
||||||
|
table, loca_table, result, result_length)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
} else if (table->tag != kLocaTableTag) {
|
||||||
|
// transform for this tag not known
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!ReconstructTransformed(tables, table->tag,
|
||||||
|
transform_buf, transform_length, result, result_length)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (continue_valid) {
|
if (continue_valid) {
|
||||||
@@ -895,7 +1046,17 @@ bool ConvertWOFF2ToTTF(uint8_t* result, size_t result_length,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return FixChecksums(sorted_tables, result);
|
if (header_version) {
|
||||||
|
if (!FixCollectionChecksums(header_version, tables, ttc_fonts, result)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!FixChecksums(tables, result)) {
|
||||||
|
return FONT_COMPRESSION_FAILURE();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace woff2
|
} // namespace woff2
|
||||||
|
|||||||
+185
-86
@@ -31,6 +31,7 @@
|
|||||||
#include "./store_bytes.h"
|
#include "./store_bytes.h"
|
||||||
#include "./table_tags.h"
|
#include "./table_tags.h"
|
||||||
#include "./transform.h"
|
#include "./transform.h"
|
||||||
|
#include "./variable_length.h"
|
||||||
#include "./woff2_common.h"
|
#include "./woff2_common.h"
|
||||||
|
|
||||||
namespace woff2 {
|
namespace woff2 {
|
||||||
@@ -44,23 +45,6 @@ using std::vector;
|
|||||||
const size_t kWoff2HeaderSize = 48;
|
const size_t kWoff2HeaderSize = 48;
|
||||||
const size_t kWoff2EntrySize = 20;
|
const size_t kWoff2EntrySize = 20;
|
||||||
|
|
||||||
size_t Base128Size(size_t n) {
|
|
||||||
size_t size = 1;
|
|
||||||
for (; n >= 128; n >>= 7) ++size;
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StoreBase128(size_t len, size_t* offset, uint8_t* dst) {
|
|
||||||
size_t size = Base128Size(len);
|
|
||||||
for (int i = 0; i < size; ++i) {
|
|
||||||
int b = static_cast<int>((len >> (7 * (size - i - 1))) & 0x7f);
|
|
||||||
if (i < size - 1) {
|
|
||||||
b |= 0x80;
|
|
||||||
}
|
|
||||||
dst[(*offset)++] = b;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Compress(const uint8_t* data, const size_t len,
|
bool Compress(const uint8_t* data, const size_t len,
|
||||||
uint8_t* result, uint32_t* result_len,
|
uint8_t* result, uint32_t* result_len,
|
||||||
brotli::BrotliParams::Mode mode) {
|
brotli::BrotliParams::Mode mode) {
|
||||||
@@ -87,21 +71,6 @@ bool TextCompress(const uint8_t* data, const size_t len,
|
|||||||
brotli::BrotliParams::MODE_TEXT);
|
brotli::BrotliParams::MODE_TEXT);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ReadLongDirectory(Buffer* file, std::vector<Table>* tables,
|
|
||||||
size_t num_tables) {
|
|
||||||
for (size_t i = 0; i < num_tables; ++i) {
|
|
||||||
Table* table = &(*tables)[i];
|
|
||||||
if (!file->ReadU32(&table->tag) ||
|
|
||||||
!file->ReadU32(&table->flags) ||
|
|
||||||
!file->ReadU32(&table->src_length) ||
|
|
||||||
!file->ReadU32(&table->transform_length) ||
|
|
||||||
!file->ReadU32(&table->dst_length)) {
|
|
||||||
return FONT_COMPRESSION_FAILURE();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
int KnownTableIndex(uint32_t tag) {
|
int KnownTableIndex(uint32_t tag) {
|
||||||
for (int i = 0; i < 63; ++i) {
|
for (int i = 0; i < 63; ++i) {
|
||||||
if (tag == kKnownTags[i]) return i;
|
if (tag == kKnownTags[i]) return i;
|
||||||
@@ -134,16 +103,42 @@ size_t TableEntrySize(const Table& table) {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t ComputeWoff2Length(const std::vector<Table>& tables,
|
size_t ComputeWoff2Length(const FontCollection& font_collection,
|
||||||
|
const std::vector<Table>& tables,
|
||||||
|
std::map<uint32_t, uint16_t> index_by_offset,
|
||||||
size_t extended_metadata_length) {
|
size_t extended_metadata_length) {
|
||||||
size_t size = kWoff2HeaderSize;
|
size_t size = kWoff2HeaderSize;
|
||||||
|
|
||||||
for (const auto& table : tables) {
|
for (const auto& table : tables) {
|
||||||
size += TableEntrySize(table);
|
size += TableEntrySize(table);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for collections only, collection tables
|
||||||
|
if (font_collection.fonts.size() > 1) {
|
||||||
|
size += 4; // UInt32 Version of TTC Header
|
||||||
|
size += Size255UShort(font_collection.fonts.size()); // 255UInt16 numFonts
|
||||||
|
|
||||||
|
size += 4 * font_collection.fonts.size(); // UInt32 flavor for each
|
||||||
|
|
||||||
|
for (const auto& font : font_collection.fonts) {
|
||||||
|
size += Size255UShort(font.tables.size()); // 255UInt16 numTables
|
||||||
|
for (const auto& entry : font.tables) {
|
||||||
|
const Font::Table& table = entry.second;
|
||||||
|
// no collection entry for xform table
|
||||||
|
if (table.tag & 0x80808080) continue;
|
||||||
|
|
||||||
|
uint16_t table_index = index_by_offset[table.offset];
|
||||||
|
size += Size255UShort(table_index); // 255UInt16 index entry
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compressed data
|
||||||
for (const auto& table : tables) {
|
for (const auto& table : tables) {
|
||||||
size += table.dst_length;
|
size += table.dst_length;
|
||||||
size = Round4(size);
|
size = Round4(size);
|
||||||
}
|
}
|
||||||
|
|
||||||
size += extended_metadata_length;
|
size += extended_metadata_length;
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
@@ -156,10 +151,37 @@ size_t ComputeTTFLength(const std::vector<Table>& tables) {
|
|||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t ComputeUncompressedLength(const Font& font) {
|
||||||
|
// sfnt header + offset table
|
||||||
|
size_t size = 12 + 16 * font.num_tables;
|
||||||
|
for (const auto& entry : font.tables) {
|
||||||
|
const Font::Table& table = entry.second;
|
||||||
|
if (table.tag & 0x80808080) continue; // xform tables don't stay
|
||||||
|
if (table.IsReused()) continue; // don't have to pay twice
|
||||||
|
size += Round4(table.length);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ComputeUncompressedLength(const FontCollection& font_collection) {
|
||||||
|
if (font_collection.fonts.size() == 1) {
|
||||||
|
return ComputeUncompressedLength(font_collection.fonts[0]);
|
||||||
|
}
|
||||||
|
size_t size = CollectionHeaderSize(font_collection.header_version,
|
||||||
|
font_collection.fonts.size());
|
||||||
|
for (const auto& font : font_collection.fonts) {
|
||||||
|
size += ComputeUncompressedLength(font);
|
||||||
|
}
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
size_t ComputeTotalTransformLength(const Font& font) {
|
size_t ComputeTotalTransformLength(const Font& font) {
|
||||||
size_t total = 0;
|
size_t total = 0;
|
||||||
for (const auto& i : font.tables) {
|
for (const auto& i : font.tables) {
|
||||||
const Font::Table& table = i.second;
|
const Font::Table& table = i.second;
|
||||||
|
if (table.IsReused()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (table.tag & 0x80808080 || !font.FindTable(table.tag ^ 0x80808080)) {
|
if (table.tag & 0x80808080 || !font.FindTable(table.tag ^ 0x80808080)) {
|
||||||
// Count transformed tables and non-transformed tables that do not have
|
// Count transformed tables and non-transformed tables that do not have
|
||||||
// transformed versions.
|
// transformed versions.
|
||||||
@@ -193,28 +215,31 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
|
|||||||
return ConvertTTFToWOFF2(data, length, result, result_length, "");
|
return ConvertTTFToWOFF2(data, length, result, result_length, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool TransformFontCollection(FontCollection* font_collection) {
|
||||||
|
for (auto& font : font_collection->fonts) {
|
||||||
|
if (!TransformGlyfAndLocaTables(&font)) {
|
||||||
|
fprintf(stderr, "Font transformation failed.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
|
bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
|
||||||
uint8_t *result, size_t *result_length,
|
uint8_t *result, size_t *result_length,
|
||||||
const string& extended_metadata) {
|
const string& extended_metadata) {
|
||||||
Font font;
|
FontCollection font_collection;
|
||||||
if (!ReadFont(data, length, &font)) {
|
if (!ReadFontCollection(data, length, &font_collection)) {
|
||||||
fprintf(stderr, "Parsing of the input font failed.\n");
|
fprintf(stderr, "Parsing of the input font failed.\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!NormalizeFont(&font)) {
|
if (!NormalizeFontCollection(&font_collection)) {
|
||||||
fprintf(stderr, "Font normalization failed.\n");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TransformGlyfAndLocaTables(&font)) {
|
if (!TransformFontCollection(&font_collection)) {
|
||||||
fprintf(stderr, "Font transformation failed.\n");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const Font::Table* head_table = font.FindTable(kHeadTableTag);
|
|
||||||
if (head_table == NULL) {
|
|
||||||
fprintf(stderr, "Missing head table.\n");
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -223,7 +248,11 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
|
|||||||
// buffer for the compressor, since the compressor can potentially increase
|
// buffer for the compressor, since the compressor can potentially increase
|
||||||
// the size. If the compressor overflows this, it should return false and
|
// the size. If the compressor overflows this, it should return false and
|
||||||
// then this function will also return false.
|
// then this function will also return false.
|
||||||
size_t total_transform_length = ComputeTotalTransformLength(font);
|
|
||||||
|
size_t total_transform_length = 0;
|
||||||
|
for (const auto& font : font_collection.fonts) {
|
||||||
|
total_transform_length += ComputeTotalTransformLength(font);
|
||||||
|
}
|
||||||
size_t compression_buffer_size = CompressedBufferSize(total_transform_length);
|
size_t compression_buffer_size = CompressedBufferSize(total_transform_length);
|
||||||
std::vector<uint8_t> compression_buf(compression_buffer_size);
|
std::vector<uint8_t> compression_buf(compression_buffer_size);
|
||||||
uint32_t total_compressed_length = compression_buffer_size;
|
uint32_t total_compressed_length = compression_buffer_size;
|
||||||
@@ -231,12 +260,16 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
|
|||||||
// Collect all transformed data into one place.
|
// Collect all transformed data into one place.
|
||||||
std::vector<uint8_t> transform_buf(total_transform_length);
|
std::vector<uint8_t> transform_buf(total_transform_length);
|
||||||
size_t transform_offset = 0;
|
size_t transform_offset = 0;
|
||||||
for (const auto& i : font.tables) {
|
for (const auto& font : font_collection.fonts) {
|
||||||
if (i.second.tag & 0x80808080) continue;
|
for (const auto& i : font.tables) {
|
||||||
const Font::Table* table = font.FindTable(i.second.tag ^ 0x80808080);
|
const Font::Table* table = font.FindTable(i.second.tag ^ 0x80808080);
|
||||||
if (table == NULL) table = &i.second;
|
if (i.second.IsReused()) continue;
|
||||||
StoreBytes(table->data, table->length,
|
if (i.second.tag & 0x80808080) continue;
|
||||||
&transform_offset, &transform_buf[0]);
|
|
||||||
|
if (table == NULL) table = &i.second;
|
||||||
|
StoreBytes(table->data, table->length,
|
||||||
|
&transform_offset, &transform_buf[0]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// Compress all transformed data in one stream.
|
// Compress all transformed data in one stream.
|
||||||
if (!Woff2Compress(transform_buf.data(), total_transform_length,
|
if (!Woff2Compress(transform_buf.data(), total_transform_length,
|
||||||
@@ -247,6 +280,7 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Compress the extended metadata
|
// Compress the extended metadata
|
||||||
|
// TODO(user): how does this apply to collections
|
||||||
uint32_t compressed_metadata_buf_length =
|
uint32_t compressed_metadata_buf_length =
|
||||||
CompressedBufferSize(extended_metadata.length());
|
CompressedBufferSize(extended_metadata.length());
|
||||||
std::vector<uint8_t> compressed_metadata_buf(compressed_metadata_buf_length);
|
std::vector<uint8_t> compressed_metadata_buf(compressed_metadata_buf_length);
|
||||||
@@ -264,39 +298,51 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Table> tables;
|
std::vector<Table> tables;
|
||||||
for (const auto& i : font.tables) {
|
std::map<uint32_t, uint16_t> index_by_offset;
|
||||||
const Font::Table& src_table = i.second;
|
|
||||||
if (src_table.tag & 0x80808080) {
|
for (const auto& font : font_collection.fonts) {
|
||||||
// This is a transformed table, we will write it together with the
|
|
||||||
// original version.
|
for (const auto tag : font.OutputOrderedTags()) {
|
||||||
continue;
|
const Font::Table& src_table = font.tables.at(tag);
|
||||||
|
|
||||||
|
if (index_by_offset.find(src_table.offset) == index_by_offset.end()) {
|
||||||
|
index_by_offset[src_table.offset] = index_by_offset.size();
|
||||||
|
} else if (!src_table.IsReused()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (src_table.IsReused()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
Table table;
|
||||||
|
table.tag = src_table.tag;
|
||||||
|
table.flags = 0;
|
||||||
|
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 |= 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;
|
||||||
|
table.flags |= kWoff2FlagsContinueStream;
|
||||||
|
}
|
||||||
|
tables.push_back(table);
|
||||||
}
|
}
|
||||||
Table table;
|
|
||||||
table.tag = src_table.tag;
|
|
||||||
table.flags = 0;
|
|
||||||
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 |= 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;
|
|
||||||
table.flags |= kWoff2FlagsContinueStream;
|
|
||||||
}
|
|
||||||
tables.push_back(table);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t woff2_length =
|
size_t woff2_length = ComputeWoff2Length(font_collection, tables,
|
||||||
ComputeWoff2Length(tables, compressed_metadata_buf_length);
|
index_by_offset, compressed_metadata_buf_length);
|
||||||
if (woff2_length > *result_length) {
|
if (woff2_length > *result_length) {
|
||||||
fprintf(stderr, "Result allocation was too small (%zd vs %zd bytes).\n",
|
fprintf(stderr, "Result allocation was too small (%zd vs %zd bytes).\n",
|
||||||
*result_length, woff2_length);
|
*result_length, woff2_length);
|
||||||
@@ -304,15 +350,26 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
|
|||||||
}
|
}
|
||||||
*result_length = woff2_length;
|
*result_length = woff2_length;
|
||||||
|
|
||||||
|
const Font& first_font = font_collection.fonts[0];
|
||||||
size_t offset = 0;
|
size_t offset = 0;
|
||||||
|
|
||||||
|
// start of woff2 header (http://www.w3.org/TR/WOFF2/#woff20Header)
|
||||||
StoreU32(kWoff2Signature, &offset, result);
|
StoreU32(kWoff2Signature, &offset, result);
|
||||||
StoreU32(font.flavor, &offset, result);
|
if (font_collection.fonts.size() == 1) {
|
||||||
|
StoreU32(first_font.flavor, &offset, result);
|
||||||
|
} else {
|
||||||
|
StoreU32(kTtcFontFlavor, &offset, result);
|
||||||
|
}
|
||||||
StoreU32(woff2_length, &offset, result);
|
StoreU32(woff2_length, &offset, result);
|
||||||
Store16(tables.size(), &offset, result);
|
Store16(tables.size(), &offset, result);
|
||||||
Store16(0, &offset, result); // reserved
|
Store16(0, &offset, result); // reserved
|
||||||
StoreU32(ComputeTTFLength(tables), &offset, result);
|
// totalSfntSize
|
||||||
StoreU32(total_compressed_length, &offset, result);
|
StoreU32(ComputeUncompressedLength(font_collection), &offset, result);
|
||||||
StoreBytes(head_table->data + 4, 4, &offset, result); // font revision
|
StoreU32(total_compressed_length, &offset, result); // totalCompressedSize
|
||||||
|
|
||||||
|
// TODO(user): is always taking this from the first tables head OK?
|
||||||
|
// font revision
|
||||||
|
StoreBytes(first_font.FindTable(kHeadTableTag)->data + 4, 4, &offset, result);
|
||||||
if (compressed_metadata_buf_length > 0) {
|
if (compressed_metadata_buf_length > 0) {
|
||||||
StoreU32(woff2_length - compressed_metadata_buf_length,
|
StoreU32(woff2_length - compressed_metadata_buf_length,
|
||||||
&offset, result); // metaOffset
|
&offset, result); // metaOffset
|
||||||
@@ -325,9 +382,51 @@ bool ConvertTTFToWOFF2(const uint8_t *data, size_t length,
|
|||||||
}
|
}
|
||||||
StoreU32(0, &offset, result); // privOffset
|
StoreU32(0, &offset, result); // privOffset
|
||||||
StoreU32(0, &offset, result); // privLength
|
StoreU32(0, &offset, result); // privLength
|
||||||
|
// end of woff2 header
|
||||||
|
|
||||||
|
// table directory (http://www.w3.org/TR/WOFF2/#table_dir_format)
|
||||||
for (const auto& table : tables) {
|
for (const auto& table : tables) {
|
||||||
StoreTableEntry(table, &offset, result);
|
StoreTableEntry(table, &offset, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// for collections only, collection table directory
|
||||||
|
if (font_collection.fonts.size() > 1) {
|
||||||
|
StoreU32(font_collection.header_version, &offset, result);
|
||||||
|
Store255UShort(font_collection.fonts.size(), &offset, result);
|
||||||
|
for (const Font& font : font_collection.fonts) {
|
||||||
|
|
||||||
|
uint16_t num_tables = 0;
|
||||||
|
for (const auto& entry : font.tables) {
|
||||||
|
const Font::Table& table = entry.second;
|
||||||
|
if (table.tag & 0x80808080) continue; // don't write xform tables
|
||||||
|
num_tables++;
|
||||||
|
}
|
||||||
|
Store255UShort(num_tables, &offset, result);
|
||||||
|
|
||||||
|
StoreU32(font.flavor, &offset, result);
|
||||||
|
for (const auto& entry : font.tables) {
|
||||||
|
const Font::Table& table = entry.second;
|
||||||
|
if (table.tag & 0x80808080) continue; // don't write xform tables
|
||||||
|
|
||||||
|
// for reused tables, only the original has an updated offset
|
||||||
|
uint32_t table_offset =
|
||||||
|
table.IsReused() ? table.reuse_of->offset : table.offset;
|
||||||
|
uint32_t table_length =
|
||||||
|
table.IsReused() ? table.reuse_of->length : table.length;
|
||||||
|
if (index_by_offset.find(table_offset) == index_by_offset.end()) {
|
||||||
|
fprintf(stderr, "Missing table index for offset 0x%08x\n",
|
||||||
|
table_offset);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
uint16_t index = index_by_offset[table_offset];
|
||||||
|
Store255UShort(index, &offset, result);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// compressed data format (http://www.w3.org/TR/WOFF2/#table_format)
|
||||||
for (const auto& table : tables) {
|
for (const auto& table : tables) {
|
||||||
StoreBytes(table.dst_data, table.dst_length, &offset, result);
|
StoreBytes(table.dst_data, table.dst_length, &offset, result);
|
||||||
offset = Round4(offset);
|
offset = Round4(offset);
|
||||||
|
|||||||
Reference in New Issue
Block a user