| 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 "./encode.h" |
| 26 #include "./buffer.h" | 27 #include "./buffer.h" |
| 27 #include "./encode.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 namespace woff2 { | 38 namespace woff2 { |
| 38 | 39 |
| 39 namespace { | 40 namespace { |
| 40 | 41 |
| 42 |
| 41 using std::string; | 43 using std::string; |
| 42 using std::vector; | 44 using std::vector; |
| 43 | 45 |
| 44 | 46 |
| 45 const size_t kWoff2HeaderSize = 48; | 47 const size_t kWoff2HeaderSize = 48; |
| 46 const size_t kWoff2EntrySize = 20; | 48 const size_t kWoff2EntrySize = 20; |
| 47 | 49 |
| 50 |
| 48 bool Compress(const uint8_t* data, const size_t len, | 51 bool Compress(const uint8_t* data, const size_t len, |
| 49 uint8_t* result, uint32_t* result_len, | 52 uint8_t* result, uint32_t* result_len, |
| 50 brotli::BrotliParams::Mode mode, int quality) { | 53 brotli::BrotliParams::Mode mode, int quality) { |
| 51 size_t compressed_len = *result_len; | 54 size_t compressed_len = *result_len; |
| 52 brotli::BrotliParams params; | 55 brotli::BrotliParams params; |
| 53 params.mode = mode; | 56 params.mode = mode; |
| 54 params.quality = quality; | 57 params.quality = quality; |
| 55 if (brotli::BrotliCompressBuffer(params, len, data, &compressed_len, result) | 58 if (brotli::BrotliCompressBuffer(params, len, data, &compressed_len, result) |
| 56 == 0) { | 59 == 0) { |
| 57 return false; | 60 return false; |
| (...skipping 17 matching lines...) Expand all Loading... |
| 75 } | 78 } |
| 76 | 79 |
| 77 int KnownTableIndex(uint32_t tag) { | 80 int KnownTableIndex(uint32_t tag) { |
| 78 for (int i = 0; i < 63; ++i) { | 81 for (int i = 0; i < 63; ++i) { |
| 79 if (tag == kKnownTags[i]) return i; | 82 if (tag == kKnownTags[i]) return i; |
| 80 } | 83 } |
| 81 return 63; | 84 return 63; |
| 82 } | 85 } |
| 83 | 86 |
| 84 void StoreTableEntry(const Table& table, size_t* offset, uint8_t* dst) { | 87 void StoreTableEntry(const Table& table, size_t* offset, uint8_t* dst) { |
| 85 uint8_t flag_byte = KnownTableIndex(table.tag); | 88 uint8_t flag_byte = (table.flags & 0xC0) | KnownTableIndex(table.tag); |
| 86 dst[(*offset)++] = flag_byte; | 89 dst[(*offset)++] = flag_byte; |
| 87 // The index here is treated as a set of flag bytes because | 90 // The index here is treated as a set of flag bytes because |
| 88 // bits 6 and 7 of the byte are reserved for future use as flags. | 91 // bits 6 and 7 of the byte are reserved for future use as flags. |
| 89 // 0x3f or 63 means an arbitrary table tag. | 92 // 0x3f or 63 means an arbitrary table tag. |
| 90 if ((flag_byte & 0x3f) == 0x3f) { | 93 if ((flag_byte & 0x3f) == 0x3f) { |
| 91 StoreU32(table.tag, offset, dst); | 94 StoreU32(table.tag, offset, dst); |
| 92 } | 95 } |
| 93 StoreBase128(table.src_length, offset, dst); | 96 StoreBase128(table.src_length, offset, dst); |
| 94 if ((table.flags & kWoff2FlagsTransform) != 0) { | 97 if ((table.flags & kWoff2FlagsTransform) != 0) { |
| 95 StoreBase128(table.transform_length, offset, dst); | 98 StoreBase128(table.transform_length, offset, dst); |
| 96 } | 99 } |
| 97 } | 100 } |
| 98 | 101 |
| 99 size_t TableEntrySize(const Table& table) { | 102 size_t TableEntrySize(const Table& table) { |
| 100 uint8_t flag_byte = KnownTableIndex(table.tag); | 103 uint8_t flag_byte = KnownTableIndex(table.tag); |
| 101 size_t size = ((flag_byte & 0x3f) != 0x3f) ? 1 : 5; | 104 size_t size = ((flag_byte & 0x3f) != 0x3f) ? 1 : 5; |
| 102 size += Base128Size(table.src_length); | 105 size += Base128Size(table.src_length); |
| 103 if ((table.flags & kWoff2FlagsTransform) != 0) { | 106 if ((table.flags & kWoff2FlagsTransform) != 0) { |
| 104 size += Base128Size(table.transform_length); | 107 size += Base128Size(table.transform_length); |
| 105 } | 108 } |
| 106 return size; | 109 return size; |
| 107 } | 110 } |
| 108 | 111 |
| 109 size_t ComputeWoff2Length(const FontCollection& font_collection, | 112 size_t ComputeWoff2Length(const FontCollection& font_collection, |
| 110 const std::vector<Table>& tables, | 113 const std::vector<Table>& tables, |
| 111 std::map<uint32_t, uint16_t> index_by_offset, | 114 std::map<uint32_t, uint16_t> index_by_offset, |
| 115 size_t compressed_data_length, |
| 112 size_t extended_metadata_length) { | 116 size_t extended_metadata_length) { |
| 113 size_t size = kWoff2HeaderSize; | 117 size_t size = kWoff2HeaderSize; |
| 114 | 118 |
| 115 for (const auto& table : tables) { | 119 for (const auto& table : tables) { |
| 116 size += TableEntrySize(table); | 120 size += TableEntrySize(table); |
| 117 } | 121 } |
| 118 | 122 |
| 119 // for collections only, collection tables | 123 // for collections only, collection tables |
| 120 if (font_collection.fonts.size() > 1) { | 124 if (font_collection.fonts.size() > 1) { |
| 121 size += 4; // UInt32 Version of TTC Header | 125 size += 4; // UInt32 Version of TTC Header |
| 122 size += Size255UShort(font_collection.fonts.size()); // 255UInt16 numFonts | 126 size += Size255UShort(font_collection.fonts.size()); // 255UInt16 numFonts |
| 123 | 127 |
| 124 size += 4 * font_collection.fonts.size(); // UInt32 flavor for each | 128 size += 4 * font_collection.fonts.size(); // UInt32 flavor for each |
| 125 | 129 |
| 126 for (const auto& font : font_collection.fonts) { | 130 for (const auto& font : font_collection.fonts) { |
| 127 size += Size255UShort(font.tables.size()); // 255UInt16 numTables | 131 size += Size255UShort(font.tables.size()); // 255UInt16 numTables |
| 128 for (const auto& entry : font.tables) { | 132 for (const auto& entry : font.tables) { |
| 129 const Font::Table& table = entry.second; | 133 const Font::Table& table = entry.second; |
| 130 // no collection entry for xform table | 134 // no collection entry for xform table |
| 131 if (table.tag & 0x80808080) continue; | 135 if (table.tag & 0x80808080) continue; |
| 132 | 136 |
| 133 uint16_t table_index = index_by_offset[table.offset]; | 137 uint16_t table_index = index_by_offset[table.offset]; |
| 134 size += Size255UShort(table_index); // 255UInt16 index entry | 138 size += Size255UShort(table_index); // 255UInt16 index entry |
| 135 } | 139 } |
| 136 } | 140 } |
| 137 } | 141 } |
| 138 | 142 |
| 139 // compressed data | 143 // compressed data |
| 140 for (const auto& table : tables) { | 144 size += compressed_data_length; |
| 141 size += table.dst_length; | 145 size = Round4(size); |
| 142 size = Round4(size); | |
| 143 } | |
| 144 | 146 |
| 145 size += extended_metadata_length; | 147 size += extended_metadata_length; |
| 146 return size; | 148 return size; |
| 147 } | 149 } |
| 148 | 150 |
| 149 size_t ComputeTTFLength(const std::vector<Table>& tables) { | 151 size_t ComputeTTFLength(const std::vector<Table>& tables) { |
| 150 size_t size = 12 + 16 * tables.size(); // sfnt header | 152 size_t size = 12 + 16 * tables.size(); // sfnt header |
| 151 for (const auto& table : tables) { | 153 for (const auto& table : tables) { |
| 152 size += Round4(table.src_length); | 154 size += Round4(table.src_length); |
| 153 } | 155 } |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 210 } | 212 } |
| 211 | 213 |
| 212 uint32_t CompressedBufferSize(uint32_t original_size) { | 214 uint32_t CompressedBufferSize(uint32_t original_size) { |
| 213 return 1.2 * original_size + 10240; | 215 return 1.2 * original_size + 10240; |
| 214 } | 216 } |
| 215 | 217 |
| 216 bool TransformFontCollection(FontCollection* font_collection) { | 218 bool TransformFontCollection(FontCollection* font_collection) { |
| 217 for (auto& font : font_collection->fonts) { | 219 for (auto& font : font_collection->fonts) { |
| 218 if (!TransformGlyfAndLocaTables(&font)) { | 220 if (!TransformGlyfAndLocaTables(&font)) { |
| 219 #ifdef FONT_COMPRESSION_BIN | 221 #ifdef FONT_COMPRESSION_BIN |
| 220 fprintf(stderr, "Font transformation failed.\n"); | 222 fprintf(stderr, "glyf/loca transformation failed.\n"); |
| 221 #endif | 223 #endif |
| 222 return FONT_COMPRESSION_FAILURE(); | 224 return FONT_COMPRESSION_FAILURE(); |
| 223 } | 225 } |
| 224 } | 226 } |
| 225 | 227 |
| 226 return true; | 228 return true; |
| 227 } | 229 } |
| 228 | 230 |
| 229 bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, | 231 bool ConvertTTFToWOFF2(const uint8_t *data, size_t length, |
| 230 uint8_t *result, size_t *result_length) { | 232 uint8_t *result, size_t *result_length) { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 241 #ifdef FONT_COMPRESSION_BIN | 243 #ifdef FONT_COMPRESSION_BIN |
| 242 fprintf(stderr, "Parsing of the input font failed.\n"); | 244 fprintf(stderr, "Parsing of the input font failed.\n"); |
| 243 #endif | 245 #endif |
| 244 return FONT_COMPRESSION_FAILURE(); | 246 return FONT_COMPRESSION_FAILURE(); |
| 245 } | 247 } |
| 246 | 248 |
| 247 if (!NormalizeFontCollection(&font_collection)) { | 249 if (!NormalizeFontCollection(&font_collection)) { |
| 248 return FONT_COMPRESSION_FAILURE(); | 250 return FONT_COMPRESSION_FAILURE(); |
| 249 } | 251 } |
| 250 | 252 |
| 251 if (!TransformFontCollection(&font_collection)) { | 253 if (params.allow_transforms && !TransformFontCollection(&font_collection)) { |
| 252 return FONT_COMPRESSION_FAILURE(); | 254 return FONT_COMPRESSION_FAILURE(); |
| 255 } else { |
| 256 // glyf/loca use 11 to flag "not transformed" |
| 257 for (auto& font : font_collection.fonts) { |
| 258 Font::Table* glyf_table = font.FindTable(kGlyfTableTag); |
| 259 Font::Table* loca_table = font.FindTable(kLocaTableTag); |
| 260 if (glyf_table) { |
| 261 glyf_table->flag_byte |= 0xc0; |
| 262 } |
| 263 if (loca_table) { |
| 264 loca_table->flag_byte |= 0xc0; |
| 265 } |
| 266 } |
| 253 } | 267 } |
| 254 | 268 |
| 255 // Although the compressed size of each table in the final woff2 file won't | 269 // Although the compressed size of each table in the final woff2 file won't |
| 256 // be larger than its transform_length, we have to allocate a large enough | 270 // be larger than its transform_length, we have to allocate a large enough |
| 257 // buffer for the compressor, since the compressor can potentially increase | 271 // buffer for the compressor, since the compressor can potentially increase |
| 258 // the size. If the compressor overflows this, it should return false and | 272 // the size. If the compressor overflows this, it should return false and |
| 259 // then this function will also return false. | 273 // then this function will also return false. |
| 260 | 274 |
| 261 size_t total_transform_length = 0; | 275 size_t total_transform_length = 0; |
| 262 for (const auto& font : font_collection.fonts) { | 276 for (const auto& font : font_collection.fonts) { |
| (...skipping 21 matching lines...) Expand all Loading... |
| 284 if (!Woff2Compress(transform_buf.data(), total_transform_length, | 298 if (!Woff2Compress(transform_buf.data(), total_transform_length, |
| 285 &compression_buf[0], | 299 &compression_buf[0], |
| 286 &total_compressed_length, | 300 &total_compressed_length, |
| 287 params.brotli_quality)) { | 301 params.brotli_quality)) { |
| 288 #ifdef FONT_COMPRESSION_BIN | 302 #ifdef FONT_COMPRESSION_BIN |
| 289 fprintf(stderr, "Compression of combined table failed.\n"); | 303 fprintf(stderr, "Compression of combined table failed.\n"); |
| 290 #endif | 304 #endif |
| 291 return FONT_COMPRESSION_FAILURE(); | 305 return FONT_COMPRESSION_FAILURE(); |
| 292 } | 306 } |
| 293 | 307 |
| 308 #ifdef FONT_COMPRESSION_BIN |
| 309 fprintf(stderr, "Compressed %zu to %u.\n", total_transform_length, |
| 310 total_compressed_length); |
| 311 #endif |
| 312 |
| 294 // Compress the extended metadata | 313 // Compress the extended metadata |
| 295 // TODO(user): how does this apply to collections | 314 // TODO(user): how does this apply to collections |
| 296 uint32_t compressed_metadata_buf_length = | 315 uint32_t compressed_metadata_buf_length = |
| 297 CompressedBufferSize(params.extended_metadata.length()); | 316 CompressedBufferSize(params.extended_metadata.length()); |
| 298 std::vector<uint8_t> compressed_metadata_buf(compressed_metadata_buf_length); | 317 std::vector<uint8_t> compressed_metadata_buf(compressed_metadata_buf_length); |
| 299 | 318 |
| 300 if (params.extended_metadata.length() > 0) { | 319 if (params.extended_metadata.length() > 0) { |
| 301 if (!TextCompress((const uint8_t*)params.extended_metadata.data(), | 320 if (!TextCompress((const uint8_t*)params.extended_metadata.data(), |
| 302 params.extended_metadata.length(), | 321 params.extended_metadata.length(), |
| 303 compressed_metadata_buf.data(), | 322 compressed_metadata_buf.data(), |
| (...skipping 20 matching lines...) Expand all Loading... |
| 324 } | 343 } |
| 325 | 344 |
| 326 if (index_by_offset.find(src_table.offset) == index_by_offset.end()) { | 345 if (index_by_offset.find(src_table.offset) == index_by_offset.end()) { |
| 327 index_by_offset[src_table.offset] = tables.size(); | 346 index_by_offset[src_table.offset] = tables.size(); |
| 328 } else { | 347 } else { |
| 329 return false; | 348 return false; |
| 330 } | 349 } |
| 331 | 350 |
| 332 Table table; | 351 Table table; |
| 333 table.tag = src_table.tag; | 352 table.tag = src_table.tag; |
| 334 table.flags = 0; | 353 table.flags = src_table.flag_byte; |
| 335 table.src_length = src_table.length; | 354 table.src_length = src_table.length; |
| 336 table.transform_length = src_table.length; | 355 table.transform_length = src_table.length; |
| 337 const uint8_t* transformed_data = src_table.data; | 356 const uint8_t* transformed_data = src_table.data; |
| 338 const Font::Table* transformed_table = | 357 const Font::Table* transformed_table = |
| 339 font.FindTable(src_table.tag ^ 0x80808080); | 358 font.FindTable(src_table.tag ^ 0x80808080); |
| 340 if (transformed_table != NULL) { | 359 if (transformed_table != NULL) { |
| 360 table.flags = transformed_table->flag_byte; |
| 341 table.flags |= kWoff2FlagsTransform; | 361 table.flags |= kWoff2FlagsTransform; |
| 342 table.transform_length = transformed_table->length; | 362 table.transform_length = transformed_table->length; |
| 343 transformed_data = transformed_table->data; | 363 transformed_data = transformed_table->data; |
| 344 } | 364 |
| 345 if (tables.empty()) { | |
| 346 table.dst_length = total_compressed_length; | |
| 347 table.dst_data = &compression_buf[0]; | |
| 348 } else { | |
| 349 table.dst_length = 0; | |
| 350 table.dst_data = NULL; | |
| 351 } | 365 } |
| 352 tables.push_back(table); | 366 tables.push_back(table); |
| 353 } | 367 } |
| 354 } | 368 } |
| 355 | 369 |
| 356 size_t woff2_length = ComputeWoff2Length(font_collection, tables, | 370 size_t woff2_length = ComputeWoff2Length(font_collection, tables, |
| 357 index_by_offset, compressed_metadata_buf_length); | 371 index_by_offset, total_compressed_length, compressed_metadata_buf_length); |
| 358 if (woff2_length > *result_length) { | 372 if (woff2_length > *result_length) { |
| 359 #ifdef FONT_COMPRESSION_BIN | 373 #ifdef FONT_COMPRESSION_BIN |
| 360 fprintf(stderr, "Result allocation was too small (%zd vs %zd bytes).\n", | 374 fprintf(stderr, "Result allocation was too small (%zd vs %zd bytes).\n", |
| 361 *result_length, woff2_length); | 375 *result_length, woff2_length); |
| 362 #endif | 376 #endif |
| 363 return FONT_COMPRESSION_FAILURE(); | 377 return FONT_COMPRESSION_FAILURE(); |
| 364 } | 378 } |
| 365 *result_length = woff2_length; | 379 *result_length = woff2_length; |
| 366 | 380 |
| 367 const Font& first_font = font_collection.fonts[0]; | 381 const Font& first_font = font_collection.fonts[0]; |
| (...skipping 69 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 437 } | 451 } |
| 438 uint16_t index = index_by_offset[table_offset]; | 452 uint16_t index = index_by_offset[table_offset]; |
| 439 Store255UShort(index, &offset, result); | 453 Store255UShort(index, &offset, result); |
| 440 | 454 |
| 441 } | 455 } |
| 442 | 456 |
| 443 } | 457 } |
| 444 } | 458 } |
| 445 | 459 |
| 446 // compressed data format (http://www.w3.org/TR/WOFF2/#table_format) | 460 // compressed data format (http://www.w3.org/TR/WOFF2/#table_format) |
| 447 for (const auto& table : tables) { | 461 |
| 448 StoreBytes(table.dst_data, table.dst_length, &offset, result); | 462 StoreBytes(&compression_buf[0], total_compressed_length, &offset, result); |
| 449 offset = Round4(offset); | 463 offset = Round4(offset); |
| 450 } | 464 |
| 451 StoreBytes(compressed_metadata_buf.data(), compressed_metadata_buf_length, | 465 StoreBytes(compressed_metadata_buf.data(), compressed_metadata_buf_length, |
| 452 &offset, result); | 466 &offset, result); |
| 453 | 467 |
| 454 if (*result_length != offset) { | 468 if (*result_length != offset) { |
| 455 #ifdef FONT_COMPRESSION_BIN | 469 #ifdef FONT_COMPRESSION_BIN |
| 456 fprintf(stderr, "Mismatch between computed and actual length " | 470 fprintf(stderr, "Mismatch between computed and actual length " |
| 457 "(%zd vs %zd)\n", *result_length, offset); | 471 "(%zd vs %zd)\n", *result_length, offset); |
| 458 #endif | 472 #endif |
| 459 return FONT_COMPRESSION_FAILURE(); | 473 return FONT_COMPRESSION_FAILURE(); |
| 460 } | 474 } |
| 461 return true; | 475 return true; |
| 462 } | 476 } |
| 463 | 477 |
| 464 } // namespace woff2 | 478 } // namespace woff2 |
| OLD | NEW |