| OLD | NEW |
| 1 // Copyright 2014 Google Inc. All Rights Reserved. | 1 // Copyright 2014 Google Inc. All Rights Reserved. |
| 2 // | 2 // |
| 3 // Licensed under the Apache License, Version 2.0 (the "License"); | 3 // Licensed under the Apache License, Version 2.0 (the "License"); |
| 4 // you may not use this file except in compliance with the License. | 4 // you may not use this file except in compliance with the License. |
| 5 // You may obtain a copy of the License at | 5 // You may obtain a copy of the License at |
| 6 // | 6 // |
| 7 // http://www.apache.org/licenses/LICENSE-2.0 | 7 // http://www.apache.org/licenses/LICENSE-2.0 |
| 8 // | 8 // |
| 9 // Unless required by applicable law or agreed to in writing, software | 9 // Unless required by applicable law or agreed to in writing, software |
| 10 // distributed under the License is distributed on an "AS IS" BASIS, | 10 // distributed under the License is distributed on an "AS IS" BASIS, |
| 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 12 // See the License for the specific language governing permissions and | 12 // See the License for the specific language governing permissions and |
| 13 // limitations under the License. | 13 // limitations under the License. |
| 14 // | 14 // |
| 15 // Library for converting TTF format font files to their WOFF2 versions. | 15 // Library for converting TTF format font files to their WOFF2 versions. |
| 16 | 16 |
| 17 #include "./woff2_enc.h" | 17 #include "./woff2_enc.h" |
| 18 | 18 |
| 19 #include <stdlib.h> | 19 #include <stdlib.h> |
| 20 #include <complex> | 20 #include <complex> |
| 21 #include <cstring> | 21 #include <cstring> |
| 22 #include <limits> | 22 #include <limits> |
| 23 #include <string> | 23 #include <string> |
| 24 #include <vector> | 24 #include <vector> |
| 25 | 25 |
| 26 #include "./compressor.h" | 26 #include "./brotli/encode.h" |
| 27 #include "./buffer.h" | 27 #include "./buffer.h" |
| 28 #include "./font.h" | 28 #include "./font.h" |
| 29 #include "./normalize.h" | 29 #include "./normalize.h" |
| 30 #include "./round.h" | 30 #include "./round.h" |
| 31 #include "./store_bytes.h" | 31 #include "./store_bytes.h" |
| 32 #include "./table_tags.h" | 32 #include "./table_tags.h" |
| 33 #include "./transform.h" | 33 #include "./transform.h" |
| 34 #include "./variable_length.h" | 34 #include "./variable_length.h" |
| 35 #include "./woff2_common.h" | 35 #include "./woff2_common.h" |
| 36 | 36 |
| 37 | 37 |
| 38 namespace woff2 { | 38 namespace woff2 { |
| 39 | 39 |
| 40 namespace { | 40 namespace { |
| 41 | 41 |
| 42 | 42 |
| 43 using std::string; | 43 using std::string; |
| 44 using std::vector; | 44 using std::vector; |
| 45 | 45 |
| 46 | 46 |
| 47 const size_t kWoff2HeaderSize = 48; | 47 const size_t kWoff2HeaderSize = 48; |
| 48 const size_t kWoff2EntrySize = 20; | 48 const size_t kWoff2EntrySize = 20; |
| 49 | 49 |
| 50 | 50 bool Compress(const uint8_t* data, const size_t len, uint8_t* result, |
| 51 bool Compress(const uint8_t* data, const size_t len, | 51 uint32_t* result_len, BrotliEncoderMode mode, int quality) { |
| 52 uint8_t* result, uint32_t* result_len, | |
| 53 brotli::BrotliParams::Mode mode, int quality) { | |
| 54 size_t compressed_len = *result_len; | 52 size_t compressed_len = *result_len; |
| 55 brotli::BrotliParams params; | 53 if (BrotliEncoderCompress(quality, BROTLI_DEFAULT_WINDOW, mode, len, data, |
| 56 params.mode = mode; | 54 &compressed_len, result) == 0) { |
| 57 params.quality = quality; | |
| 58 if (brotli::BrotliCompressBuffer(params, len, data, &compressed_len, result) | |
| 59 == 0) { | |
| 60 return false; | 55 return false; |
| 61 } | 56 } |
| 62 *result_len = compressed_len; | 57 *result_len = compressed_len; |
| 63 return true; | 58 return true; |
| 64 } | 59 } |
| 65 | 60 |
| 66 bool Woff2Compress(const uint8_t* data, const size_t len, | 61 bool Woff2Compress(const uint8_t* data, const size_t len, |
| 67 uint8_t* result, uint32_t* result_len, | 62 uint8_t* result, uint32_t* result_len, |
| 68 int quality) { | 63 int quality) { |
| 69 return Compress(data, len, result, result_len, | 64 return Compress(data, len, result, result_len, |
| 70 brotli::BrotliParams::MODE_FONT, quality); | 65 BROTLI_MODE_FONT, quality); |
| 71 } | 66 } |
| 72 | 67 |
| 73 bool TextCompress(const uint8_t* data, const size_t len, | 68 bool TextCompress(const uint8_t* data, const size_t len, |
| 74 uint8_t* result, uint32_t* result_len, | 69 uint8_t* result, uint32_t* result_len, |
| 75 int quality) { | 70 int quality) { |
| 76 return Compress(data, len, result, result_len, | 71 return Compress(data, len, result, result_len, |
| 77 brotli::BrotliParams::MODE_TEXT, quality); | 72 BROTLI_MODE_TEXT, quality); |
| 78 } | 73 } |
| 79 | 74 |
| 80 int KnownTableIndex(uint32_t tag) { | 75 int KnownTableIndex(uint32_t tag) { |
| 81 for (int i = 0; i < 63; ++i) { | 76 for (int i = 0; i < 63; ++i) { |
| 82 if (tag == kKnownTags[i]) return i; | 77 if (tag == kKnownTags[i]) return i; |
| 83 } | 78 } |
| 84 return 63; | 79 return 63; |
| 85 } | 80 } |
| 86 | 81 |
| 87 void StoreTableEntry(const Table& table, size_t* offset, uint8_t* dst) { | 82 void StoreTableEntry(const Table& table, size_t* offset, uint8_t* dst) { |
| (...skipping 26 matching lines...) Expand all Loading... |
| 114 std::map<uint32_t, uint16_t> index_by_offset, | 109 std::map<uint32_t, uint16_t> index_by_offset, |
| 115 size_t compressed_data_length, | 110 size_t compressed_data_length, |
| 116 size_t extended_metadata_length) { | 111 size_t extended_metadata_length) { |
| 117 size_t size = kWoff2HeaderSize; | 112 size_t size = kWoff2HeaderSize; |
| 118 | 113 |
| 119 for (const auto& table : tables) { | 114 for (const auto& table : tables) { |
| 120 size += TableEntrySize(table); | 115 size += TableEntrySize(table); |
| 121 } | 116 } |
| 122 | 117 |
| 123 // for collections only, collection tables | 118 // for collections only, collection tables |
| 124 if (font_collection.fonts.size() > 1) { | 119 if (font_collection.flavor == kTtcFontFlavor) { |
| 125 size += 4; // UInt32 Version of TTC Header | 120 size += 4; // UInt32 Version of TTC Header |
| 126 size += Size255UShort(font_collection.fonts.size()); // 255UInt16 numFonts | 121 size += Size255UShort(font_collection.fonts.size()); // 255UInt16 numFonts |
| 127 | 122 |
| 128 size += 4 * font_collection.fonts.size(); // UInt32 flavor for each | 123 size += 4 * font_collection.fonts.size(); // UInt32 flavor for each |
| 129 | 124 |
| 130 for (const auto& font : font_collection.fonts) { | 125 for (const auto& font : font_collection.fonts) { |
| 131 size += Size255UShort(font.tables.size()); // 255UInt16 numTables | 126 size += Size255UShort(font.tables.size()); // 255UInt16 numTables |
| 132 for (const auto& entry : font.tables) { | 127 for (const auto& entry : font.tables) { |
| 133 const Font::Table& table = entry.second; | 128 const Font::Table& table = entry.second; |
| 134 // no collection entry for xform table | 129 // no collection entry for xform table |
| (...skipping 19 matching lines...) Expand all Loading... |
| 154 for (const auto& entry : font.tables) { | 149 for (const auto& entry : font.tables) { |
| 155 const Font::Table& table = entry.second; | 150 const Font::Table& table = entry.second; |
| 156 if (table.tag & 0x80808080) continue; // xform tables don't stay | 151 if (table.tag & 0x80808080) continue; // xform tables don't stay |
| 157 if (table.IsReused()) continue; // don't have to pay twice | 152 if (table.IsReused()) continue; // don't have to pay twice |
| 158 size += Round4(table.length); | 153 size += Round4(table.length); |
| 159 } | 154 } |
| 160 return size; | 155 return size; |
| 161 } | 156 } |
| 162 | 157 |
| 163 size_t ComputeUncompressedLength(const FontCollection& font_collection) { | 158 size_t ComputeUncompressedLength(const FontCollection& font_collection) { |
| 164 if (font_collection.fonts.size() == 1) { | 159 if (font_collection.flavor != kTtcFontFlavor) { |
| 165 return ComputeUncompressedLength(font_collection.fonts[0]); | 160 return ComputeUncompressedLength(font_collection.fonts[0]); |
| 166 } | 161 } |
| 167 size_t size = CollectionHeaderSize(font_collection.header_version, | 162 size_t size = CollectionHeaderSize(font_collection.header_version, |
| 168 font_collection.fonts.size()); | 163 font_collection.fonts.size()); |
| 169 for (const auto& font : font_collection.fonts) { | 164 for (const auto& font : font_collection.fonts) { |
| 170 size += ComputeUncompressedLength(font); | 165 size += ComputeUncompressedLength(font); |
| 171 } | 166 } |
| 172 return size; | 167 return size; |
| 173 } | 168 } |
| 174 | 169 |
| (...skipping 190 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 365 index_by_offset, total_compressed_length, compressed_metadata_buf_length); | 360 index_by_offset, total_compressed_length, compressed_metadata_buf_length); |
| 366 if (woff2_length > *result_length) { | 361 if (woff2_length > *result_length) { |
| 367 #ifdef FONT_COMPRESSION_BIN | 362 #ifdef FONT_COMPRESSION_BIN |
| 368 fprintf(stderr, "Result allocation was too small (%zd vs %zd bytes).\n", | 363 fprintf(stderr, "Result allocation was too small (%zd vs %zd bytes).\n", |
| 369 *result_length, woff2_length); | 364 *result_length, woff2_length); |
| 370 #endif | 365 #endif |
| 371 return FONT_COMPRESSION_FAILURE(); | 366 return FONT_COMPRESSION_FAILURE(); |
| 372 } | 367 } |
| 373 *result_length = woff2_length; | 368 *result_length = woff2_length; |
| 374 | 369 |
| 375 const Font& first_font = font_collection.fonts[0]; | |
| 376 size_t offset = 0; | 370 size_t offset = 0; |
| 377 | 371 |
| 378 // start of woff2 header (http://www.w3.org/TR/WOFF2/#woff20Header) | 372 // start of woff2 header (http://www.w3.org/TR/WOFF2/#woff20Header) |
| 379 StoreU32(kWoff2Signature, &offset, result); | 373 StoreU32(kWoff2Signature, &offset, result); |
| 380 if (font_collection.fonts.size() == 1) { | 374 if (font_collection.flavor != kTtcFontFlavor) { |
| 381 StoreU32(first_font.flavor, &offset, result); | 375 StoreU32(font_collection.fonts[0].flavor, &offset, result); |
| 382 } else { | 376 } else { |
| 383 StoreU32(kTtcFontFlavor, &offset, result); | 377 StoreU32(kTtcFontFlavor, &offset, result); |
| 384 } | 378 } |
| 385 StoreU32(woff2_length, &offset, result); | 379 StoreU32(woff2_length, &offset, result); |
| 386 Store16(tables.size(), &offset, result); | 380 Store16(tables.size(), &offset, result); |
| 387 Store16(0, &offset, result); // reserved | 381 Store16(0, &offset, result); // reserved |
| 388 // totalSfntSize | 382 // totalSfntSize |
| 389 StoreU32(ComputeUncompressedLength(font_collection), &offset, result); | 383 StoreU32(ComputeUncompressedLength(font_collection), &offset, result); |
| 390 StoreU32(total_compressed_length, &offset, result); // totalCompressedSize | 384 StoreU32(total_compressed_length, &offset, result); // totalCompressedSize |
| 391 | 385 |
| 392 // TODO(user): is always taking this from the first tables head OK? | 386 // Let's just all be v1.0 |
| 393 // font revision | 387 Store16(1, &offset, result); // majorVersion |
| 394 StoreBytes(first_font.FindTable(kHeadTableTag)->data + 4, 4, &offset, result); | 388 Store16(0, &offset, result); // minorVersion |
| 395 if (compressed_metadata_buf_length > 0) { | 389 if (compressed_metadata_buf_length > 0) { |
| 396 StoreU32(woff2_length - compressed_metadata_buf_length, | 390 StoreU32(woff2_length - compressed_metadata_buf_length, |
| 397 &offset, result); // metaOffset | 391 &offset, result); // metaOffset |
| 398 StoreU32(compressed_metadata_buf_length, &offset, result); // metaLength | 392 StoreU32(compressed_metadata_buf_length, &offset, result); // metaLength |
| 399 StoreU32(params.extended_metadata.length(), | 393 StoreU32(params.extended_metadata.length(), |
| 400 &offset, result); // metaOrigLength | 394 &offset, result); // metaOrigLength |
| 401 } else { | 395 } else { |
| 402 StoreU32(0, &offset, result); // metaOffset | 396 StoreU32(0, &offset, result); // metaOffset |
| 403 StoreU32(0, &offset, result); // metaLength | 397 StoreU32(0, &offset, result); // metaLength |
| 404 StoreU32(0, &offset, result); // metaOrigLength | 398 StoreU32(0, &offset, result); // metaOrigLength |
| 405 } | 399 } |
| 406 StoreU32(0, &offset, result); // privOffset | 400 StoreU32(0, &offset, result); // privOffset |
| 407 StoreU32(0, &offset, result); // privLength | 401 StoreU32(0, &offset, result); // privLength |
| 408 // end of woff2 header | 402 // end of woff2 header |
| 409 | 403 |
| 410 // table directory (http://www.w3.org/TR/WOFF2/#table_dir_format) | 404 // table directory (http://www.w3.org/TR/WOFF2/#table_dir_format) |
| 411 for (const auto& table : tables) { | 405 for (const auto& table : tables) { |
| 412 StoreTableEntry(table, &offset, result); | 406 StoreTableEntry(table, &offset, result); |
| 413 } | 407 } |
| 414 | 408 |
| 415 // for collections only, collection table directory | 409 // for collections only, collection table directory |
| 416 if (font_collection.fonts.size() > 1) { | 410 if (font_collection.flavor == kTtcFontFlavor) { |
| 417 StoreU32(font_collection.header_version, &offset, result); | 411 StoreU32(font_collection.header_version, &offset, result); |
| 418 Store255UShort(font_collection.fonts.size(), &offset, result); | 412 Store255UShort(font_collection.fonts.size(), &offset, result); |
| 419 for (const Font& font : font_collection.fonts) { | 413 for (const Font& font : font_collection.fonts) { |
| 420 | 414 |
| 421 uint16_t num_tables = 0; | 415 uint16_t num_tables = 0; |
| 422 for (const auto& entry : font.tables) { | 416 for (const auto& entry : font.tables) { |
| 423 const Font::Table& table = entry.second; | 417 const Font::Table& table = entry.second; |
| 424 if (table.tag & 0x80808080) continue; // don't write xform tables | 418 if (table.tag & 0x80808080) continue; // don't write xform tables |
| 425 num_tables++; | 419 num_tables++; |
| 426 } | 420 } |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 463 #ifdef FONT_COMPRESSION_BIN | 457 #ifdef FONT_COMPRESSION_BIN |
| 464 fprintf(stderr, "Mismatch between computed and actual length " | 458 fprintf(stderr, "Mismatch between computed and actual length " |
| 465 "(%zd vs %zd)\n", *result_length, offset); | 459 "(%zd vs %zd)\n", *result_length, offset); |
| 466 #endif | 460 #endif |
| 467 return FONT_COMPRESSION_FAILURE(); | 461 return FONT_COMPRESSION_FAILURE(); |
| 468 } | 462 } |
| 469 return true; | 463 return true; |
| 470 } | 464 } |
| 471 | 465 |
| 472 } // namespace woff2 | 466 } // namespace woff2 |
| OLD | NEW |