woff2_dec.cc (46525B)
1 /* Copyright 2014 Google Inc. All Rights Reserved. 2 3 Distributed under MIT license. 4 See file LICENSE for detail or copy at https://opensource.org/licenses/MIT 5 */ 6 7 /* Library for converting WOFF2 format font files to their TTF versions. */ 8 9 #include <woff2/decode.h> 10 11 #include <stdlib.h> 12 #include <algorithm> 13 #include <complex> 14 #include <cstring> 15 #include <limits> 16 #include <string> 17 #include <vector> 18 #include <map> 19 #include <memory> 20 #include <utility> 21 22 #include "./buffer.h" 23 #include "./port.h" 24 #include "./round.h" 25 #include "./store_bytes.h" 26 #include "./table_tags.h" 27 #include "./variable_length.h" 28 #include "./woff2_common.h" 29 30 #include "../RLBoxWOFF2Sandbox.h" 31 32 namespace woff2 { 33 34 namespace { 35 36 // simple glyph flags 37 const int kGlyfOnCurve = 1 << 0; 38 const int kGlyfXShort = 1 << 1; 39 const int kGlyfYShort = 1 << 2; 40 const int kGlyfRepeat = 1 << 3; 41 const int kGlyfThisXIsSame = 1 << 4; 42 const int kGlyfThisYIsSame = 1 << 5; 43 const int kOverlapSimple = 1 << 6; 44 45 // composite glyph flags 46 // See CompositeGlyph.java in sfntly for full definitions 47 const int FLAG_ARG_1_AND_2_ARE_WORDS = 1 << 0; 48 const int FLAG_WE_HAVE_A_SCALE = 1 << 3; 49 const int FLAG_MORE_COMPONENTS = 1 << 5; 50 const int FLAG_WE_HAVE_AN_X_AND_Y_SCALE = 1 << 6; 51 const int FLAG_WE_HAVE_A_TWO_BY_TWO = 1 << 7; 52 const int FLAG_WE_HAVE_INSTRUCTIONS = 1 << 8; 53 54 // glyf flags 55 const int FLAG_OVERLAP_SIMPLE_BITMAP = 1 << 0; 56 57 const size_t kCheckSumAdjustmentOffset = 8; 58 59 const size_t kEndPtsOfContoursOffset = 10; 60 const size_t kCompositeGlyphBegin = 10; 61 62 // 98% of Google Fonts have no glyph above 5k bytes 63 // Largest glyph ever observed was 72k bytes 64 const size_t kDefaultGlyphBuf = 5120; 65 66 // Over 14k test fonts the max compression ratio seen to date was ~20. 67 // >100 suggests you wrote a bad uncompressed size. 68 const float kMaxPlausibleCompressionRatio = 100.0; 69 70 // metadata for a TTC font entry 71 struct TtcFont { 72 uint32_t flavor; 73 uint32_t dst_offset; 74 uint32_t header_checksum; 75 std::vector<uint16_t> table_indices; 76 }; 77 78 struct WOFF2Header { 79 uint32_t flavor; 80 uint32_t header_version; 81 uint16_t num_tables; 82 uint64_t compressed_offset; 83 uint32_t compressed_length; 84 uint32_t uncompressed_size; 85 std::vector<Table> tables; // num_tables unique tables 86 std::vector<TtcFont> ttc_fonts; // metadata to help rebuild font 87 }; 88 89 /** 90 * Accumulates data we may need to reconstruct a single font. One per font 91 * created for a TTC. 92 */ 93 struct WOFF2FontInfo { 94 uint16_t num_glyphs; 95 uint16_t index_format; 96 uint16_t num_hmetrics; 97 std::vector<int16_t> x_mins; 98 std::map<uint32_t, uint32_t> table_entry_by_tag; 99 }; 100 101 // Accumulates metadata as we rebuild the font 102 struct RebuildMetadata { 103 uint32_t header_checksum; // set by WriteHeaders 104 std::vector<WOFF2FontInfo> font_infos; 105 // checksums for tables that have been written. 106 // (tag, src_offset) => checksum. Need both because 0-length loca. 107 std::map<std::pair<uint32_t, uint32_t>, uint32_t> checksums; 108 }; 109 110 int WithSign(int flag, int baseval) { 111 // Precondition: 0 <= baseval < 65536 (to avoid integer overflow) 112 return (flag & 1) ? baseval : -baseval; 113 } 114 115 bool _SafeIntAddition(int a, int b, int* result) { 116 if (PREDICT_FALSE( 117 ((a > 0) && (b > std::numeric_limits<int>::max() - a)) || 118 ((a < 0) && (b < std::numeric_limits<int>::min() - a)))) { 119 return false; 120 } 121 *result = a + b; 122 return true; 123 } 124 125 bool TripletDecode(const uint8_t* flags_in, const uint8_t* in, size_t in_size, 126 unsigned int n_points, Point* result, size_t* in_bytes_consumed) { 127 int x = 0; 128 int y = 0; 129 130 if (PREDICT_FALSE(n_points > in_size)) { 131 return FONT_COMPRESSION_FAILURE(); 132 } 133 unsigned int triplet_index = 0; 134 135 for (unsigned int i = 0; i < n_points; ++i) { 136 uint8_t flag = flags_in[i]; 137 bool on_curve = !(flag >> 7); 138 flag &= 0x7f; 139 unsigned int n_data_bytes; 140 if (flag < 84) { 141 n_data_bytes = 1; 142 } else if (flag < 120) { 143 n_data_bytes = 2; 144 } else if (flag < 124) { 145 n_data_bytes = 3; 146 } else { 147 n_data_bytes = 4; 148 } 149 if (PREDICT_FALSE(triplet_index + n_data_bytes > in_size || 150 triplet_index + n_data_bytes < triplet_index)) { 151 return FONT_COMPRESSION_FAILURE(); 152 } 153 int dx, dy; 154 if (flag < 10) { 155 dx = 0; 156 dy = WithSign(flag, ((flag & 14) << 7) + in[triplet_index]); 157 } else if (flag < 20) { 158 dx = WithSign(flag, (((flag - 10) & 14) << 7) + in[triplet_index]); 159 dy = 0; 160 } else if (flag < 84) { 161 int b0 = flag - 20; 162 int b1 = in[triplet_index]; 163 dx = WithSign(flag, 1 + (b0 & 0x30) + (b1 >> 4)); 164 dy = WithSign(flag >> 1, 1 + ((b0 & 0x0c) << 2) + (b1 & 0x0f)); 165 } else if (flag < 120) { 166 int b0 = flag - 84; 167 dx = WithSign(flag, 1 + ((b0 / 12) << 8) + in[triplet_index]); 168 dy = WithSign(flag >> 1, 169 1 + (((b0 % 12) >> 2) << 8) + in[triplet_index + 1]); 170 } else if (flag < 124) { 171 int b2 = in[triplet_index + 1]; 172 dx = WithSign(flag, (in[triplet_index] << 4) + (b2 >> 4)); 173 dy = WithSign(flag >> 1, ((b2 & 0x0f) << 8) + in[triplet_index + 2]); 174 } else { 175 dx = WithSign(flag, (in[triplet_index] << 8) + in[triplet_index + 1]); 176 dy = WithSign(flag >> 1, 177 (in[triplet_index + 2] << 8) + in[triplet_index + 3]); 178 } 179 triplet_index += n_data_bytes; 180 if (!_SafeIntAddition(x, dx, &x)) { 181 return false; 182 } 183 if (!_SafeIntAddition(y, dy, &y)) { 184 return false; 185 } 186 *result++ = {x, y, on_curve}; 187 } 188 *in_bytes_consumed = triplet_index; 189 return true; 190 } 191 192 // This function stores just the point data. On entry, dst points to the 193 // beginning of a simple glyph. Returns true on success. 194 bool StorePoints(unsigned int n_points, const Point* points, 195 unsigned int n_contours, unsigned int instruction_length, 196 bool has_overlap_bit, uint8_t* dst, size_t dst_size, 197 size_t* glyph_size) { 198 // I believe that n_contours < 65536, in which case this is safe. However, a 199 // comment and/or an assert would be good. 200 unsigned int flag_offset = kEndPtsOfContoursOffset + 2 * n_contours + 2 + 201 instruction_length; 202 int last_flag = -1; 203 int repeat_count = 0; 204 int last_x = 0; 205 int last_y = 0; 206 unsigned int x_bytes = 0; 207 unsigned int y_bytes = 0; 208 209 for (unsigned int i = 0; i < n_points; ++i) { 210 const Point& point = points[i]; 211 int flag = point.on_curve ? kGlyfOnCurve : 0; 212 if (has_overlap_bit && i == 0) { 213 flag |= kOverlapSimple; 214 } 215 216 int dx = point.x - last_x; 217 int dy = point.y - last_y; 218 if (dx == 0) { 219 flag |= kGlyfThisXIsSame; 220 } else if (dx > -256 && dx < 256) { 221 flag |= kGlyfXShort | (dx > 0 ? kGlyfThisXIsSame : 0); 222 x_bytes += 1; 223 } else { 224 x_bytes += 2; 225 } 226 if (dy == 0) { 227 flag |= kGlyfThisYIsSame; 228 } else if (dy > -256 && dy < 256) { 229 flag |= kGlyfYShort | (dy > 0 ? kGlyfThisYIsSame : 0); 230 y_bytes += 1; 231 } else { 232 y_bytes += 2; 233 } 234 235 if (flag == last_flag && repeat_count != 255) { 236 dst[flag_offset - 1] |= kGlyfRepeat; 237 repeat_count++; 238 } else { 239 if (repeat_count != 0) { 240 if (PREDICT_FALSE(flag_offset >= dst_size)) { 241 return FONT_COMPRESSION_FAILURE(); 242 } 243 dst[flag_offset++] = repeat_count; 244 } 245 if (PREDICT_FALSE(flag_offset >= dst_size)) { 246 return FONT_COMPRESSION_FAILURE(); 247 } 248 dst[flag_offset++] = flag; 249 repeat_count = 0; 250 } 251 last_x = point.x; 252 last_y = point.y; 253 last_flag = flag; 254 } 255 256 if (repeat_count != 0) { 257 if (PREDICT_FALSE(flag_offset >= dst_size)) { 258 return FONT_COMPRESSION_FAILURE(); 259 } 260 dst[flag_offset++] = repeat_count; 261 } 262 unsigned int xy_bytes = x_bytes + y_bytes; 263 if (PREDICT_FALSE(xy_bytes < x_bytes || 264 flag_offset + xy_bytes < flag_offset || 265 flag_offset + xy_bytes > dst_size)) { 266 return FONT_COMPRESSION_FAILURE(); 267 } 268 269 int x_offset = flag_offset; 270 int y_offset = flag_offset + x_bytes; 271 last_x = 0; 272 last_y = 0; 273 for (unsigned int i = 0; i < n_points; ++i) { 274 int dx = points[i].x - last_x; 275 if (dx == 0) { 276 // pass 277 } else if (dx > -256 && dx < 256) { 278 dst[x_offset++] = std::abs(dx); 279 } else { 280 // will always fit for valid input, but overflow is harmless 281 x_offset = Store16(dst, x_offset, dx); 282 } 283 last_x += dx; 284 int dy = points[i].y - last_y; 285 if (dy == 0) { 286 // pass 287 } else if (dy > -256 && dy < 256) { 288 dst[y_offset++] = std::abs(dy); 289 } else { 290 y_offset = Store16(dst, y_offset, dy); 291 } 292 last_y += dy; 293 } 294 *glyph_size = y_offset; 295 return true; 296 } 297 298 // Compute the bounding box of the coordinates, and store into a glyf buffer. 299 // A precondition is that there are at least 10 bytes available. 300 // dst should point to the beginning of a 'glyf' record. 301 void ComputeBbox(unsigned int n_points, const Point* points, uint8_t* dst) { 302 int x_min = 0; 303 int y_min = 0; 304 int x_max = 0; 305 int y_max = 0; 306 307 if (n_points > 0) { 308 x_min = points[0].x; 309 x_max = points[0].x; 310 y_min = points[0].y; 311 y_max = points[0].y; 312 } 313 for (unsigned int i = 1; i < n_points; ++i) { 314 int x = points[i].x; 315 int y = points[i].y; 316 x_min = std::min(x, x_min); 317 x_max = std::max(x, x_max); 318 y_min = std::min(y, y_min); 319 y_max = std::max(y, y_max); 320 } 321 size_t offset = 2; 322 offset = Store16(dst, offset, x_min); 323 offset = Store16(dst, offset, y_min); 324 offset = Store16(dst, offset, x_max); 325 offset = Store16(dst, offset, y_max); 326 } 327 328 329 bool SizeOfComposite(Buffer composite_stream, size_t* size, 330 bool* have_instructions) { 331 size_t start_offset = composite_stream.offset(); 332 bool we_have_instructions = false; 333 334 uint16_t flags = FLAG_MORE_COMPONENTS; 335 while (flags & FLAG_MORE_COMPONENTS) { 336 if (PREDICT_FALSE(!composite_stream.ReadU16(&flags))) { 337 return FONT_COMPRESSION_FAILURE(); 338 } 339 we_have_instructions |= (flags & FLAG_WE_HAVE_INSTRUCTIONS) != 0; 340 size_t arg_size = 2; // glyph index 341 if (flags & FLAG_ARG_1_AND_2_ARE_WORDS) { 342 arg_size += 4; 343 } else { 344 arg_size += 2; 345 } 346 if (flags & FLAG_WE_HAVE_A_SCALE) { 347 arg_size += 2; 348 } else if (flags & FLAG_WE_HAVE_AN_X_AND_Y_SCALE) { 349 arg_size += 4; 350 } else if (flags & FLAG_WE_HAVE_A_TWO_BY_TWO) { 351 arg_size += 8; 352 } 353 if (PREDICT_FALSE(!composite_stream.Skip(arg_size))) { 354 return FONT_COMPRESSION_FAILURE(); 355 } 356 } 357 358 *size = composite_stream.offset() - start_offset; 359 *have_instructions = we_have_instructions; 360 361 return true; 362 } 363 364 bool Pad4(WOFF2Out* out) { 365 uint8_t zeroes[] = {0, 0, 0}; 366 if (PREDICT_FALSE(out->Size() + 3 < out->Size())) { 367 return FONT_COMPRESSION_FAILURE(); 368 } 369 uint32_t pad_bytes = Round4(out->Size()) - out->Size(); 370 if (pad_bytes > 0) { 371 if (PREDICT_FALSE(!out->Write(&zeroes, pad_bytes))) { 372 return FONT_COMPRESSION_FAILURE(); 373 } 374 } 375 return true; 376 } 377 378 // Build TrueType loca table 379 bool StoreLoca(const std::vector<uint32_t>& loca_values, int index_format, 380 uint32_t* checksum, WOFF2Out* out) { 381 // TODO(user) figure out what index format to use based on whether max 382 // offset fits into uint16_t or not 383 const uint64_t loca_size = loca_values.size(); 384 const uint64_t offset_size = index_format ? 4 : 2; 385 if (PREDICT_FALSE((loca_size << 2) >> 2 != loca_size)) { 386 return FONT_COMPRESSION_FAILURE(); 387 } 388 std::vector<uint8_t> loca_content(loca_size * offset_size); 389 uint8_t* dst = &loca_content[0]; 390 size_t offset = 0; 391 for (size_t i = 0; i < loca_values.size(); ++i) { 392 uint32_t value = loca_values[i]; 393 if (index_format) { 394 offset = StoreU32(dst, offset, value); 395 } else { 396 offset = Store16(dst, offset, value >> 1); 397 } 398 } 399 *checksum = ComputeULongSum(&loca_content[0], loca_content.size()); 400 if (PREDICT_FALSE(!out->Write(&loca_content[0], loca_content.size()))) { 401 return FONT_COMPRESSION_FAILURE(); 402 } 403 return true; 404 } 405 406 // Reconstruct entire glyf table based on transformed original 407 bool ReconstructGlyf(const uint8_t* data, Table* glyf_table, 408 uint32_t* glyf_checksum, Table * loca_table, 409 uint32_t* loca_checksum, WOFF2FontInfo* info, 410 WOFF2Out* out) { 411 static const int kNumSubStreams = 7; 412 Buffer file(data, glyf_table->transform_length); 413 uint16_t version; 414 std::vector<std::pair<const uint8_t*, size_t> > substreams(kNumSubStreams); 415 const size_t glyf_start = out->Size(); 416 417 if (PREDICT_FALSE(!file.ReadU16(&version))) { 418 return FONT_COMPRESSION_FAILURE(); 419 } 420 421 uint16_t flags; 422 if (PREDICT_FALSE(!file.ReadU16(&flags))) { 423 return FONT_COMPRESSION_FAILURE(); 424 } 425 bool has_overlap_bitmap = (flags & FLAG_OVERLAP_SIMPLE_BITMAP); 426 427 if (PREDICT_FALSE(!file.ReadU16(&info->num_glyphs) || 428 !file.ReadU16(&info->index_format))) { 429 return FONT_COMPRESSION_FAILURE(); 430 } 431 432 // https://dev.w3.org/webfonts/WOFF2/spec/#conform-mustRejectLoca 433 // dst_length here is origLength in the spec 434 uint32_t expected_loca_dst_length = (info->index_format ? 4 : 2) 435 * (static_cast<uint32_t>(info->num_glyphs) + 1); 436 if (PREDICT_FALSE(loca_table->dst_length != expected_loca_dst_length)) { 437 return FONT_COMPRESSION_FAILURE(); 438 } 439 440 unsigned int offset = (2 + kNumSubStreams) * 4; 441 if (PREDICT_FALSE(offset > glyf_table->transform_length)) { 442 return FONT_COMPRESSION_FAILURE(); 443 } 444 // Invariant from here on: data_size >= offset 445 for (int i = 0; i < kNumSubStreams; ++i) { 446 uint32_t substream_size; 447 if (PREDICT_FALSE(!file.ReadU32(&substream_size))) { 448 return FONT_COMPRESSION_FAILURE(); 449 } 450 if (PREDICT_FALSE(substream_size > glyf_table->transform_length - offset)) { 451 return FONT_COMPRESSION_FAILURE(); 452 } 453 substreams[i] = std::make_pair(data + offset, substream_size); 454 offset += substream_size; 455 } 456 Buffer n_contour_stream(substreams[0].first, substreams[0].second); 457 Buffer n_points_stream(substreams[1].first, substreams[1].second); 458 Buffer flag_stream(substreams[2].first, substreams[2].second); 459 Buffer glyph_stream(substreams[3].first, substreams[3].second); 460 Buffer composite_stream(substreams[4].first, substreams[4].second); 461 Buffer bbox_stream(substreams[5].first, substreams[5].second); 462 Buffer instruction_stream(substreams[6].first, substreams[6].second); 463 464 const uint8_t* overlap_bitmap = nullptr; 465 unsigned int overlap_bitmap_length = 0; 466 if (has_overlap_bitmap) { 467 overlap_bitmap_length = (info->num_glyphs + 7) >> 3; 468 overlap_bitmap = data + offset; 469 if (PREDICT_FALSE(overlap_bitmap_length > 470 glyf_table->transform_length - offset)) { 471 return FONT_COMPRESSION_FAILURE(); 472 } 473 } 474 475 std::vector<uint32_t> loca_values(info->num_glyphs + 1); 476 std::vector<unsigned int> n_points_vec; 477 std::unique_ptr<Point[]> points; 478 size_t points_size = 0; 479 const uint8_t* bbox_bitmap = bbox_stream.buffer(); 480 // Safe because num_glyphs is bounded 481 unsigned int bitmap_length = ((info->num_glyphs + 31) >> 5) << 2; 482 if (!bbox_stream.Skip(bitmap_length)) { 483 return FONT_COMPRESSION_FAILURE(); 484 } 485 486 // Temp buffer for glyph's. 487 size_t glyph_buf_size = kDefaultGlyphBuf; 488 std::unique_ptr<uint8_t[]> glyph_buf(new uint8_t[glyph_buf_size]); 489 490 info->x_mins.resize(info->num_glyphs); 491 for (unsigned int i = 0; i < info->num_glyphs; ++i) { 492 size_t glyph_size = 0; 493 uint16_t n_contours = 0; 494 bool have_bbox = false; 495 if (bbox_bitmap[i >> 3] & (0x80 >> (i & 7))) { 496 have_bbox = true; 497 } 498 if (PREDICT_FALSE(!n_contour_stream.ReadU16(&n_contours))) { 499 return FONT_COMPRESSION_FAILURE(); 500 } 501 502 if (n_contours == 0xffff) { 503 // composite glyph 504 bool have_instructions = false; 505 unsigned int instruction_size = 0; 506 if (PREDICT_FALSE(!have_bbox)) { 507 // composite glyphs must have an explicit bbox 508 return FONT_COMPRESSION_FAILURE(); 509 } 510 511 size_t composite_size; 512 if (PREDICT_FALSE(!SizeOfComposite(composite_stream, &composite_size, 513 &have_instructions))) { 514 return FONT_COMPRESSION_FAILURE(); 515 } 516 if (have_instructions) { 517 if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { 518 return FONT_COMPRESSION_FAILURE(); 519 } 520 } 521 522 size_t size_needed = 12 + composite_size + instruction_size; 523 if (PREDICT_FALSE(glyph_buf_size < size_needed)) { 524 glyph_buf.reset(new uint8_t[size_needed]); 525 glyph_buf_size = size_needed; 526 } 527 528 glyph_size = Store16(glyph_buf.get(), glyph_size, n_contours); 529 if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { 530 return FONT_COMPRESSION_FAILURE(); 531 } 532 glyph_size += 8; 533 534 if (PREDICT_FALSE(!composite_stream.Read(glyph_buf.get() + glyph_size, 535 composite_size))) { 536 return FONT_COMPRESSION_FAILURE(); 537 } 538 glyph_size += composite_size; 539 if (have_instructions) { 540 glyph_size = Store16(glyph_buf.get(), glyph_size, instruction_size); 541 if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, 542 instruction_size))) { 543 return FONT_COMPRESSION_FAILURE(); 544 } 545 glyph_size += instruction_size; 546 } 547 } else if (n_contours > 0) { 548 // simple glyph 549 n_points_vec.clear(); 550 unsigned int total_n_points = 0; 551 unsigned int n_points_contour; 552 for (unsigned int j = 0; j < n_contours; ++j) { 553 if (PREDICT_FALSE( 554 !Read255UShort(&n_points_stream, &n_points_contour))) { 555 return FONT_COMPRESSION_FAILURE(); 556 } 557 n_points_vec.push_back(n_points_contour); 558 if (PREDICT_FALSE(total_n_points + n_points_contour < total_n_points)) { 559 return FONT_COMPRESSION_FAILURE(); 560 } 561 total_n_points += n_points_contour; 562 } 563 unsigned int flag_size = total_n_points; 564 if (PREDICT_FALSE( 565 flag_size > flag_stream.length() - flag_stream.offset())) { 566 return FONT_COMPRESSION_FAILURE(); 567 } 568 const uint8_t* flags_buf = flag_stream.buffer() + flag_stream.offset(); 569 const uint8_t* triplet_buf = glyph_stream.buffer() + 570 glyph_stream.offset(); 571 size_t triplet_size = glyph_stream.length() - glyph_stream.offset(); 572 size_t triplet_bytes_consumed = 0; 573 if (points_size < total_n_points) { 574 points_size = total_n_points; 575 points.reset(new Point[points_size]); 576 } 577 if (PREDICT_FALSE(!TripletDecode(flags_buf, triplet_buf, triplet_size, 578 total_n_points, points.get(), &triplet_bytes_consumed))) { 579 return FONT_COMPRESSION_FAILURE(); 580 } 581 if (PREDICT_FALSE(!flag_stream.Skip(flag_size))) { 582 return FONT_COMPRESSION_FAILURE(); 583 } 584 if (PREDICT_FALSE(!glyph_stream.Skip(triplet_bytes_consumed))) { 585 return FONT_COMPRESSION_FAILURE(); 586 } 587 unsigned int instruction_size; 588 if (PREDICT_FALSE(!Read255UShort(&glyph_stream, &instruction_size))) { 589 return FONT_COMPRESSION_FAILURE(); 590 } 591 592 if (PREDICT_FALSE(total_n_points >= (1 << 27) 593 || instruction_size >= (1 << 30))) { 594 return FONT_COMPRESSION_FAILURE(); 595 } 596 size_t size_needed = 12 + 2 * n_contours + 5 * total_n_points 597 + instruction_size; 598 if (PREDICT_FALSE(glyph_buf_size < size_needed)) { 599 glyph_buf.reset(new uint8_t[size_needed]); 600 glyph_buf_size = size_needed; 601 } 602 603 glyph_size = Store16(glyph_buf.get(), glyph_size, n_contours); 604 if (have_bbox) { 605 if (PREDICT_FALSE(!bbox_stream.Read(glyph_buf.get() + glyph_size, 8))) { 606 return FONT_COMPRESSION_FAILURE(); 607 } 608 } else { 609 ComputeBbox(total_n_points, points.get(), glyph_buf.get()); 610 } 611 glyph_size = kEndPtsOfContoursOffset; 612 int end_point = -1; 613 for (unsigned int contour_ix = 0; contour_ix < n_contours; ++contour_ix) { 614 end_point += n_points_vec[contour_ix]; 615 if (PREDICT_FALSE(end_point >= 65536)) { 616 return FONT_COMPRESSION_FAILURE(); 617 } 618 glyph_size = Store16(glyph_buf.get(), glyph_size, end_point); 619 } 620 621 glyph_size = Store16(glyph_buf.get(), glyph_size, instruction_size); 622 if (PREDICT_FALSE(!instruction_stream.Read(glyph_buf.get() + glyph_size, 623 instruction_size))) { 624 return FONT_COMPRESSION_FAILURE(); 625 } 626 glyph_size += instruction_size; 627 628 bool has_overlap_bit = 629 has_overlap_bitmap && overlap_bitmap[i >> 3] & (0x80 >> (i & 7)); 630 631 if (PREDICT_FALSE(!StorePoints( 632 total_n_points, points.get(), n_contours, instruction_size, 633 has_overlap_bit, glyph_buf.get(), glyph_buf_size, &glyph_size))) { 634 return FONT_COMPRESSION_FAILURE(); 635 } 636 } else { 637 // n_contours == 0; empty glyph. Must NOT have a bbox. 638 if (PREDICT_FALSE(have_bbox)) { 639 #ifdef FONT_COMPRESSION_BIN 640 fprintf(stderr, "Empty glyph has a bbox\n"); 641 #endif 642 return FONT_COMPRESSION_FAILURE(); 643 } 644 } 645 646 loca_values[i] = out->Size() - glyf_start; 647 if (PREDICT_FALSE(!out->Write(glyph_buf.get(), glyph_size))) { 648 return FONT_COMPRESSION_FAILURE(); 649 } 650 651 // TODO(user) Old code aligned glyphs ... but do we actually need to? 652 if (PREDICT_FALSE(!Pad4(out))) { 653 return FONT_COMPRESSION_FAILURE(); 654 } 655 656 *glyf_checksum += ComputeULongSum(glyph_buf.get(), glyph_size); 657 658 // We may need x_min to reconstruct 'hmtx' 659 if (n_contours > 0) { 660 Buffer x_min_buf(glyph_buf.get() + 2, 2); 661 if (PREDICT_FALSE(!x_min_buf.ReadS16(&info->x_mins[i]))) { 662 return FONT_COMPRESSION_FAILURE(); 663 } 664 } 665 } 666 667 // glyf_table dst_offset was set by ReconstructFont 668 glyf_table->dst_length = out->Size() - glyf_table->dst_offset; 669 loca_table->dst_offset = out->Size(); 670 // loca[n] will be equal the length of the glyph data ('glyf') table 671 loca_values[info->num_glyphs] = glyf_table->dst_length; 672 if (PREDICT_FALSE(!StoreLoca(loca_values, info->index_format, loca_checksum, 673 out))) { 674 return FONT_COMPRESSION_FAILURE(); 675 } 676 loca_table->dst_length = out->Size() - loca_table->dst_offset; 677 678 return true; 679 } 680 681 Table* FindTable(std::vector<Table*>* tables, uint32_t tag) { 682 for (Table* table : *tables) { 683 if (table->tag == tag) { 684 return table; 685 } 686 } 687 return NULL; 688 } 689 690 // Get numberOfHMetrics, https://www.microsoft.com/typography/otspec/hhea.htm 691 bool ReadNumHMetrics(const uint8_t* data, size_t data_size, 692 uint16_t* num_hmetrics) { 693 // Skip 34 to reach 'hhea' numberOfHMetrics 694 Buffer buffer(data, data_size); 695 if (PREDICT_FALSE(!buffer.Skip(34) || !buffer.ReadU16(num_hmetrics))) { 696 return FONT_COMPRESSION_FAILURE(); 697 } 698 return true; 699 } 700 701 // http://dev.w3.org/webfonts/WOFF2/spec/Overview.html#hmtx_table_format 702 bool ReconstructTransformedHmtx(const uint8_t* transformed_buf, 703 size_t transformed_size, 704 uint16_t num_glyphs, 705 uint16_t num_hmetrics, 706 const std::vector<int16_t>& x_mins, 707 uint32_t* checksum, 708 WOFF2Out* out) { 709 Buffer hmtx_buff_in(transformed_buf, transformed_size); 710 711 uint8_t hmtx_flags; 712 if (PREDICT_FALSE(!hmtx_buff_in.ReadU8(&hmtx_flags))) { 713 return FONT_COMPRESSION_FAILURE(); 714 } 715 716 std::vector<uint16_t> advance_widths; 717 std::vector<int16_t> lsbs; 718 bool has_proportional_lsbs = (hmtx_flags & 1) == 0; 719 bool has_monospace_lsbs = (hmtx_flags & 2) == 0; 720 721 // Bits 2-7 are reserved and MUST be zero. 722 if ((hmtx_flags & 0xFC) != 0) { 723 #ifdef FONT_COMPRESSION_BIN 724 fprintf(stderr, "Illegal hmtx flags; bits 2-7 must be 0\n"); 725 #endif 726 return FONT_COMPRESSION_FAILURE(); 727 } 728 729 // you say you transformed but there is little evidence of it 730 if (has_proportional_lsbs && has_monospace_lsbs) { 731 return FONT_COMPRESSION_FAILURE(); 732 } 733 734 assert(x_mins.size() == num_glyphs); 735 736 // num_glyphs 0 is OK if there is no 'glyf' but cannot then xform 'hmtx'. 737 if (PREDICT_FALSE(num_hmetrics > num_glyphs)) { 738 return FONT_COMPRESSION_FAILURE(); 739 } 740 741 // https://www.microsoft.com/typography/otspec/hmtx.htm 742 // "...only one entry need be in the array, but that entry is required." 743 if (PREDICT_FALSE(num_hmetrics < 1)) { 744 return FONT_COMPRESSION_FAILURE(); 745 } 746 747 for (uint16_t i = 0; i < num_hmetrics; i++) { 748 uint16_t advance_width; 749 if (PREDICT_FALSE(!hmtx_buff_in.ReadU16(&advance_width))) { 750 return FONT_COMPRESSION_FAILURE(); 751 } 752 advance_widths.push_back(advance_width); 753 } 754 755 for (uint16_t i = 0; i < num_hmetrics; i++) { 756 int16_t lsb; 757 if (has_proportional_lsbs) { 758 if (PREDICT_FALSE(!hmtx_buff_in.ReadS16(&lsb))) { 759 return FONT_COMPRESSION_FAILURE(); 760 } 761 } else { 762 lsb = x_mins[i]; 763 } 764 lsbs.push_back(lsb); 765 } 766 767 for (uint16_t i = num_hmetrics; i < num_glyphs; i++) { 768 int16_t lsb; 769 if (has_monospace_lsbs) { 770 if (PREDICT_FALSE(!hmtx_buff_in.ReadS16(&lsb))) { 771 return FONT_COMPRESSION_FAILURE(); 772 } 773 } else { 774 lsb = x_mins[i]; 775 } 776 lsbs.push_back(lsb); 777 } 778 779 // bake me a shiny new hmtx table 780 uint32_t hmtx_output_size = 2 * num_glyphs + 2 * num_hmetrics; 781 std::vector<uint8_t> hmtx_table(hmtx_output_size); 782 uint8_t* dst = &hmtx_table[0]; 783 size_t dst_offset = 0; 784 for (uint32_t i = 0; i < num_glyphs; i++) { 785 if (i < num_hmetrics) { 786 Store16(advance_widths[i], &dst_offset, dst); 787 } 788 Store16(lsbs[i], &dst_offset, dst); 789 } 790 791 *checksum = ComputeULongSum(&hmtx_table[0], hmtx_output_size); 792 if (PREDICT_FALSE(!out->Write(&hmtx_table[0], hmtx_output_size))) { 793 return FONT_COMPRESSION_FAILURE(); 794 } 795 796 return true; 797 } 798 799 bool Woff2Uncompress(uint8_t* dst_buf, size_t dst_size, 800 const uint8_t* src_buf, size_t src_size) { 801 size_t uncompressed_size = dst_size; 802 BrotliDecoderResult result = RLBoxBrotliDecoderDecompress( 803 src_size, src_buf, &uncompressed_size, dst_buf); 804 if (PREDICT_FALSE(result != BROTLI_DECODER_RESULT_SUCCESS || 805 uncompressed_size != dst_size)) { 806 return FONT_COMPRESSION_FAILURE(); 807 } 808 return true; 809 } 810 811 bool ReadTableDirectory(Buffer* file, std::vector<Table>* tables, 812 size_t num_tables) { 813 uint32_t src_offset = 0; 814 for (size_t i = 0; i < num_tables; ++i) { 815 Table* table = &(*tables)[i]; 816 uint8_t flag_byte; 817 if (PREDICT_FALSE(!file->ReadU8(&flag_byte))) { 818 return FONT_COMPRESSION_FAILURE(); 819 } 820 uint32_t tag; 821 if ((flag_byte & 0x3f) == 0x3f) { 822 if (PREDICT_FALSE(!file->ReadU32(&tag))) { 823 return FONT_COMPRESSION_FAILURE(); 824 } 825 } else { 826 tag = kKnownTags[flag_byte & 0x3f]; 827 } 828 uint32_t flags = 0; 829 uint8_t xform_version = (flag_byte >> 6) & 0x03; 830 831 // 0 means xform for glyph/loca, non-0 for others 832 if (tag == kGlyfTableTag || tag == kLocaTableTag) { 833 if (xform_version == 0) { 834 flags |= kWoff2FlagsTransform; 835 } 836 } else if (xform_version != 0) { 837 flags |= kWoff2FlagsTransform; 838 } 839 flags |= xform_version; 840 841 uint32_t dst_length; 842 if (PREDICT_FALSE(!ReadBase128(file, &dst_length))) { 843 return FONT_COMPRESSION_FAILURE(); 844 } 845 uint32_t transform_length = dst_length; 846 if ((flags & kWoff2FlagsTransform) != 0) { 847 if (PREDICT_FALSE(!ReadBase128(file, &transform_length))) { 848 return FONT_COMPRESSION_FAILURE(); 849 } 850 if (PREDICT_FALSE(tag == kLocaTableTag && transform_length)) { 851 return FONT_COMPRESSION_FAILURE(); 852 } 853 } 854 if (PREDICT_FALSE(src_offset + transform_length < src_offset)) { 855 return FONT_COMPRESSION_FAILURE(); 856 } 857 table->src_offset = src_offset; 858 table->src_length = transform_length; 859 src_offset += transform_length; 860 861 table->tag = tag; 862 table->flags = flags; 863 table->transform_length = transform_length; 864 table->dst_length = dst_length; 865 } 866 return true; 867 } 868 869 // Writes a single Offset Table entry 870 size_t StoreOffsetTable(uint8_t* result, size_t offset, uint32_t flavor, 871 uint16_t num_tables) { 872 offset = StoreU32(result, offset, flavor); // sfnt version 873 offset = Store16(result, offset, num_tables); // num_tables 874 unsigned max_pow2 = 0; 875 while (1u << (max_pow2 + 1) <= num_tables) { 876 max_pow2++; 877 } 878 const uint16_t output_search_range = (1u << max_pow2) << 4; 879 offset = Store16(result, offset, output_search_range); // searchRange 880 offset = Store16(result, offset, max_pow2); // entrySelector 881 // rangeShift 882 offset = Store16(result, offset, (num_tables << 4) - output_search_range); 883 return offset; 884 } 885 886 size_t StoreTableEntry(uint8_t* result, uint32_t offset, uint32_t tag) { 887 offset = StoreU32(result, offset, tag); 888 offset = StoreU32(result, offset, 0); 889 offset = StoreU32(result, offset, 0); 890 offset = StoreU32(result, offset, 0); 891 return offset; 892 } 893 894 // First table goes after all the headers, table directory, etc 895 uint64_t ComputeOffsetToFirstTable(const WOFF2Header& hdr) { 896 uint64_t offset = kSfntHeaderSize + 897 kSfntEntrySize * static_cast<uint64_t>(hdr.num_tables); 898 if (hdr.header_version) { 899 offset = CollectionHeaderSize(hdr.header_version, hdr.ttc_fonts.size()) 900 + kSfntHeaderSize * hdr.ttc_fonts.size(); 901 for (const auto& ttc_font : hdr.ttc_fonts) { 902 offset += kSfntEntrySize * ttc_font.table_indices.size(); 903 } 904 } 905 return offset; 906 } 907 908 std::vector<Table*> Tables(WOFF2Header* hdr, size_t font_index) { 909 std::vector<Table*> tables; 910 if (PREDICT_FALSE(hdr->header_version)) { 911 for (auto index : hdr->ttc_fonts[font_index].table_indices) { 912 tables.push_back(&hdr->tables[index]); 913 } 914 } else { 915 for (auto& table : hdr->tables) { 916 tables.push_back(&table); 917 } 918 } 919 return tables; 920 } 921 922 // Offset tables assumed to have been written in with 0's initially. 923 // WOFF2Header isn't const so we can use [] instead of at() (which upsets FF) 924 bool ReconstructFont(uint8_t* transformed_buf, 925 const uint32_t transformed_buf_size, 926 RebuildMetadata* metadata, 927 WOFF2Header* hdr, 928 size_t font_index, 929 WOFF2Out* out) { 930 size_t dest_offset = out->Size(); 931 uint8_t table_entry[12]; 932 WOFF2FontInfo* info = &metadata->font_infos[font_index]; 933 std::vector<Table*> tables = Tables(hdr, font_index); 934 935 // 'glyf' without 'loca' doesn't make sense 936 const Table* glyf_table = FindTable(&tables, kGlyfTableTag); 937 const Table* loca_table = FindTable(&tables, kLocaTableTag); 938 if (PREDICT_FALSE(static_cast<bool>(glyf_table) != 939 static_cast<bool>(loca_table))) { 940 #ifdef FONT_COMPRESSION_BIN 941 fprintf(stderr, "Cannot have just one of glyf/loca\n"); 942 #endif 943 return FONT_COMPRESSION_FAILURE(); 944 } 945 946 if (glyf_table != NULL) { 947 if (PREDICT_FALSE((glyf_table->flags & kWoff2FlagsTransform) 948 != (loca_table->flags & kWoff2FlagsTransform))) { 949 #ifdef FONT_COMPRESSION_BIN 950 fprintf(stderr, "Cannot transform just one of glyf/loca\n"); 951 #endif 952 return FONT_COMPRESSION_FAILURE(); 953 } 954 } 955 956 uint32_t font_checksum = metadata->header_checksum; 957 if (hdr->header_version) { 958 font_checksum = hdr->ttc_fonts[font_index].header_checksum; 959 } 960 961 uint32_t loca_checksum = 0; 962 for (size_t i = 0; i < tables.size(); i++) { 963 Table& table = *tables[i]; 964 965 std::pair<uint32_t, uint32_t> checksum_key = {table.tag, table.src_offset}; 966 bool reused = metadata->checksums.find(checksum_key) 967 != metadata->checksums.end(); 968 if (PREDICT_FALSE(font_index == 0 && reused)) { 969 return FONT_COMPRESSION_FAILURE(); 970 } 971 972 // TODO(user) a collection with optimized hmtx that reused glyf/loca 973 // would fail. We don't optimize hmtx for collections yet. 974 if (PREDICT_FALSE(static_cast<uint64_t>(table.src_offset) + table.src_length 975 > transformed_buf_size)) { 976 return FONT_COMPRESSION_FAILURE(); 977 } 978 979 if (table.tag == kHheaTableTag) { 980 if (!ReadNumHMetrics(transformed_buf + table.src_offset, 981 table.src_length, &info->num_hmetrics)) { 982 return FONT_COMPRESSION_FAILURE(); 983 } 984 } 985 986 uint32_t checksum = 0; 987 if (!reused) { 988 if ((table.flags & kWoff2FlagsTransform) != kWoff2FlagsTransform) { 989 if (table.tag == kHeadTableTag) { 990 if (PREDICT_FALSE(table.src_length < 12)) { 991 return FONT_COMPRESSION_FAILURE(); 992 } 993 // checkSumAdjustment = 0 994 StoreU32(transformed_buf + table.src_offset, 8, 0); 995 } 996 table.dst_offset = dest_offset; 997 checksum = ComputeULongSum(transformed_buf + table.src_offset, 998 table.src_length); 999 if (PREDICT_FALSE(!out->Write(transformed_buf + table.src_offset, 1000 table.src_length))) { 1001 return FONT_COMPRESSION_FAILURE(); 1002 } 1003 } else { 1004 if (table.tag == kGlyfTableTag) { 1005 table.dst_offset = dest_offset; 1006 1007 Table* loca_table = FindTable(&tables, kLocaTableTag); 1008 if (PREDICT_FALSE(!ReconstructGlyf(transformed_buf + table.src_offset, 1009 &table, &checksum, loca_table, &loca_checksum, info, out))) { 1010 return FONT_COMPRESSION_FAILURE(); 1011 } 1012 } else if (table.tag == kLocaTableTag) { 1013 // All the work was done by ReconstructGlyf. We already know checksum. 1014 checksum = loca_checksum; 1015 } else if (table.tag == kHmtxTableTag) { 1016 table.dst_offset = dest_offset; 1017 // Tables are sorted so all the info we need has been gathered. 1018 if (PREDICT_FALSE(!ReconstructTransformedHmtx( 1019 transformed_buf + table.src_offset, table.src_length, 1020 info->num_glyphs, info->num_hmetrics, info->x_mins, &checksum, 1021 out))) { 1022 return FONT_COMPRESSION_FAILURE(); 1023 } 1024 } else { 1025 return FONT_COMPRESSION_FAILURE(); // transform unknown 1026 } 1027 } 1028 metadata->checksums[checksum_key] = checksum; 1029 } else { 1030 checksum = metadata->checksums[checksum_key]; 1031 } 1032 font_checksum += checksum; 1033 1034 // update the table entry with real values. 1035 StoreU32(table_entry, 0, checksum); 1036 StoreU32(table_entry, 4, table.dst_offset); 1037 StoreU32(table_entry, 8, table.dst_length); 1038 if (PREDICT_FALSE(!out->Write(table_entry, 1039 info->table_entry_by_tag[table.tag] + 4, 12))) { 1040 return FONT_COMPRESSION_FAILURE(); 1041 } 1042 1043 // We replaced 0's. Update overall checksum. 1044 font_checksum += ComputeULongSum(table_entry, 12); 1045 1046 if (PREDICT_FALSE(!Pad4(out))) { 1047 return FONT_COMPRESSION_FAILURE(); 1048 } 1049 1050 if (PREDICT_FALSE(static_cast<uint64_t>(table.dst_offset + table.dst_length) 1051 > out->Size())) { 1052 return FONT_COMPRESSION_FAILURE(); 1053 } 1054 dest_offset = out->Size(); 1055 } 1056 1057 // Update 'head' checkSumAdjustment. We already set it to 0 and summed font. 1058 Table* head_table = FindTable(&tables, kHeadTableTag); 1059 if (head_table) { 1060 if (PREDICT_FALSE(head_table->dst_length < 12)) { 1061 return FONT_COMPRESSION_FAILURE(); 1062 } 1063 uint8_t checksum_adjustment[4]; 1064 StoreU32(checksum_adjustment, 0, 0xB1B0AFBA - font_checksum); 1065 if (PREDICT_FALSE(!out->Write(checksum_adjustment, 1066 head_table->dst_offset + 8, 4))) { 1067 return FONT_COMPRESSION_FAILURE(); 1068 } 1069 } 1070 1071 return true; 1072 } 1073 1074 bool ReadWOFF2Header(const uint8_t* data, size_t length, WOFF2Header* hdr) { 1075 Buffer file(data, length); 1076 1077 uint32_t signature; 1078 if (PREDICT_FALSE(!file.ReadU32(&signature) || signature != kWoff2Signature || 1079 !file.ReadU32(&hdr->flavor))) { 1080 return FONT_COMPRESSION_FAILURE(); 1081 } 1082 1083 // TODO(user): Should call IsValidVersionTag() here. 1084 1085 uint32_t reported_length; 1086 if (PREDICT_FALSE( 1087 !file.ReadU32(&reported_length) || length != reported_length)) { 1088 return FONT_COMPRESSION_FAILURE(); 1089 } 1090 if (PREDICT_FALSE(!file.ReadU16(&hdr->num_tables) || !hdr->num_tables)) { 1091 return FONT_COMPRESSION_FAILURE(); 1092 } 1093 1094 // We don't care about these fields of the header: 1095 // uint16_t reserved 1096 // uint32_t total_sfnt_size, we don't believe this, will compute later 1097 if (PREDICT_FALSE(!file.Skip(6))) { 1098 return FONT_COMPRESSION_FAILURE(); 1099 } 1100 if (PREDICT_FALSE(!file.ReadU32(&hdr->compressed_length))) { 1101 return FONT_COMPRESSION_FAILURE(); 1102 } 1103 // We don't care about these fields of the header: 1104 // uint16_t major_version, minor_version 1105 if (PREDICT_FALSE(!file.Skip(2 * 2))) { 1106 return FONT_COMPRESSION_FAILURE(); 1107 } 1108 uint32_t meta_offset; 1109 uint32_t meta_length; 1110 uint32_t meta_length_orig; 1111 if (PREDICT_FALSE(!file.ReadU32(&meta_offset) || 1112 !file.ReadU32(&meta_length) || 1113 !file.ReadU32(&meta_length_orig))) { 1114 return FONT_COMPRESSION_FAILURE(); 1115 } 1116 if (meta_offset) { 1117 if (PREDICT_FALSE( 1118 meta_offset >= length || length - meta_offset < meta_length)) { 1119 return FONT_COMPRESSION_FAILURE(); 1120 } 1121 } 1122 uint32_t priv_offset; 1123 uint32_t priv_length; 1124 if (PREDICT_FALSE(!file.ReadU32(&priv_offset) || 1125 !file.ReadU32(&priv_length))) { 1126 return FONT_COMPRESSION_FAILURE(); 1127 } 1128 if (priv_offset) { 1129 if (PREDICT_FALSE( 1130 priv_offset >= length || length - priv_offset < priv_length)) { 1131 return FONT_COMPRESSION_FAILURE(); 1132 } 1133 } 1134 hdr->tables.resize(hdr->num_tables); 1135 if (PREDICT_FALSE(!ReadTableDirectory( 1136 &file, &hdr->tables, hdr->num_tables))) { 1137 return FONT_COMPRESSION_FAILURE(); 1138 } 1139 1140 // Before we sort for output the last table end is the uncompressed size. 1141 Table& last_table = hdr->tables.back(); 1142 hdr->uncompressed_size = last_table.src_offset + last_table.src_length; 1143 if (PREDICT_FALSE(hdr->uncompressed_size < last_table.src_offset)) { 1144 return FONT_COMPRESSION_FAILURE(); 1145 } 1146 1147 hdr->header_version = 0; 1148 1149 if (hdr->flavor == kTtcFontFlavor) { 1150 if (PREDICT_FALSE(!file.ReadU32(&hdr->header_version))) { 1151 return FONT_COMPRESSION_FAILURE(); 1152 } 1153 if (PREDICT_FALSE(hdr->header_version != 0x00010000 1154 && hdr->header_version != 0x00020000)) { 1155 return FONT_COMPRESSION_FAILURE(); 1156 } 1157 uint32_t num_fonts; 1158 if (PREDICT_FALSE(!Read255UShort(&file, &num_fonts) || !num_fonts)) { 1159 return FONT_COMPRESSION_FAILURE(); 1160 } 1161 hdr->ttc_fonts.resize(num_fonts); 1162 1163 for (uint32_t i = 0; i < num_fonts; i++) { 1164 TtcFont& ttc_font = hdr->ttc_fonts[i]; 1165 uint32_t num_tables; 1166 if (PREDICT_FALSE(!Read255UShort(&file, &num_tables) || !num_tables)) { 1167 return FONT_COMPRESSION_FAILURE(); 1168 } 1169 if (PREDICT_FALSE(!file.ReadU32(&ttc_font.flavor))) { 1170 return FONT_COMPRESSION_FAILURE(); 1171 } 1172 1173 ttc_font.table_indices.resize(num_tables); 1174 1175 1176 unsigned int glyf_idx = 0; 1177 unsigned int loca_idx = 0; 1178 1179 for (uint32_t j = 0; j < num_tables; j++) { 1180 unsigned int table_idx; 1181 if (PREDICT_FALSE(!Read255UShort(&file, &table_idx)) || 1182 table_idx >= hdr->tables.size()) { 1183 return FONT_COMPRESSION_FAILURE(); 1184 } 1185 ttc_font.table_indices[j] = table_idx; 1186 1187 const Table& table = hdr->tables[table_idx]; 1188 if (table.tag == kLocaTableTag) { 1189 loca_idx = table_idx; 1190 } 1191 if (table.tag == kGlyfTableTag) { 1192 glyf_idx = table_idx; 1193 } 1194 1195 } 1196 1197 // if we have both glyf and loca make sure they are consecutive 1198 // if we have just one we'll reject the font elsewhere 1199 if (glyf_idx > 0 || loca_idx > 0) { 1200 if (PREDICT_FALSE(glyf_idx > loca_idx || loca_idx - glyf_idx != 1)) { 1201 #ifdef FONT_COMPRESSION_BIN 1202 fprintf(stderr, "TTC font %d has non-consecutive glyf/loca\n", i); 1203 #endif 1204 return FONT_COMPRESSION_FAILURE(); 1205 } 1206 } 1207 } 1208 } 1209 1210 const uint64_t first_table_offset = ComputeOffsetToFirstTable(*hdr); 1211 1212 hdr->compressed_offset = file.offset(); 1213 if (PREDICT_FALSE(hdr->compressed_offset > 1214 std::numeric_limits<uint32_t>::max())) { 1215 return FONT_COMPRESSION_FAILURE(); 1216 } 1217 uint64_t src_offset = Round4(hdr->compressed_offset + hdr->compressed_length); 1218 uint64_t dst_offset = first_table_offset; 1219 1220 1221 if (PREDICT_FALSE(src_offset > length)) { 1222 #ifdef FONT_COMPRESSION_BIN 1223 fprintf(stderr, "offset fail; src_offset %" PRIu64 " length %lu " 1224 "dst_offset %" PRIu64 "\n", 1225 src_offset, length, dst_offset); 1226 #endif 1227 return FONT_COMPRESSION_FAILURE(); 1228 } 1229 if (meta_offset) { 1230 if (PREDICT_FALSE(src_offset != meta_offset)) { 1231 return FONT_COMPRESSION_FAILURE(); 1232 } 1233 src_offset = Round4(meta_offset + meta_length); 1234 if (PREDICT_FALSE(src_offset > std::numeric_limits<uint32_t>::max())) { 1235 return FONT_COMPRESSION_FAILURE(); 1236 } 1237 } 1238 1239 if (priv_offset) { 1240 if (PREDICT_FALSE(src_offset != priv_offset)) { 1241 return FONT_COMPRESSION_FAILURE(); 1242 } 1243 src_offset = Round4(priv_offset + priv_length); 1244 if (PREDICT_FALSE(src_offset > std::numeric_limits<uint32_t>::max())) { 1245 return FONT_COMPRESSION_FAILURE(); 1246 } 1247 } 1248 1249 if (PREDICT_FALSE(src_offset != Round4(length))) { 1250 return FONT_COMPRESSION_FAILURE(); 1251 } 1252 1253 return true; 1254 } 1255 1256 // Write everything before the actual table data 1257 bool WriteHeaders(const uint8_t* data, size_t length, RebuildMetadata* metadata, 1258 WOFF2Header* hdr, WOFF2Out* out) { 1259 std::vector<uint8_t> output(ComputeOffsetToFirstTable(*hdr), 0); 1260 1261 // Re-order tables in output (OTSpec) order 1262 std::vector<Table> sorted_tables(hdr->tables); 1263 if (hdr->header_version) { 1264 // collection; we have to sort the table offset vector in each font 1265 for (auto& ttc_font : hdr->ttc_fonts) { 1266 std::map<uint32_t, uint16_t> sorted_index_by_tag; 1267 for (auto table_index : ttc_font.table_indices) { 1268 sorted_index_by_tag[hdr->tables[table_index].tag] = table_index; 1269 } 1270 uint16_t index = 0; 1271 for (auto& i : sorted_index_by_tag) { 1272 ttc_font.table_indices[index++] = i.second; 1273 } 1274 } 1275 } else { 1276 // non-collection; we can just sort the tables 1277 std::sort(sorted_tables.begin(), sorted_tables.end()); 1278 } 1279 1280 // Start building the font 1281 uint8_t* result = &output[0]; 1282 size_t offset = 0; 1283 if (hdr->header_version) { 1284 // TTC header 1285 offset = StoreU32(result, offset, hdr->flavor); // TAG TTCTag 1286 offset = StoreU32(result, offset, hdr->header_version); // FIXED Version 1287 offset = StoreU32(result, offset, hdr->ttc_fonts.size()); // ULONG numFonts 1288 // Space for ULONG OffsetTable[numFonts] (zeroed initially) 1289 size_t offset_table = offset; // keep start of offset table for later 1290 for (size_t i = 0; i < hdr->ttc_fonts.size(); i++) { 1291 offset = StoreU32(result, offset, 0); // will fill real values in later 1292 } 1293 // space for DSIG fields for header v2 1294 if (hdr->header_version == 0x00020000) { 1295 offset = StoreU32(result, offset, 0); // ULONG ulDsigTag 1296 offset = StoreU32(result, offset, 0); // ULONG ulDsigLength 1297 offset = StoreU32(result, offset, 0); // ULONG ulDsigOffset 1298 } 1299 1300 // write Offset Tables and store the location of each in TTC Header 1301 metadata->font_infos.resize(hdr->ttc_fonts.size()); 1302 for (size_t i = 0; i < hdr->ttc_fonts.size(); i++) { 1303 TtcFont& ttc_font = hdr->ttc_fonts[i]; 1304 1305 // write Offset Table location into TTC Header 1306 offset_table = StoreU32(result, offset_table, offset); 1307 1308 // write the actual offset table so our header doesn't lie 1309 ttc_font.dst_offset = offset; 1310 offset = StoreOffsetTable(result, offset, ttc_font.flavor, 1311 ttc_font.table_indices.size()); 1312 1313 for (const auto table_index : ttc_font.table_indices) { 1314 uint32_t tag = hdr->tables[table_index].tag; 1315 metadata->font_infos[i].table_entry_by_tag[tag] = offset; 1316 offset = StoreTableEntry(result, offset, tag); 1317 } 1318 1319 ttc_font.header_checksum = ComputeULongSum(&output[ttc_font.dst_offset], 1320 offset - ttc_font.dst_offset); 1321 } 1322 } else { 1323 metadata->font_infos.resize(1); 1324 offset = StoreOffsetTable(result, offset, hdr->flavor, hdr->num_tables); 1325 for (uint16_t i = 0; i < hdr->num_tables; ++i) { 1326 metadata->font_infos[0].table_entry_by_tag[sorted_tables[i].tag] = offset; 1327 offset = StoreTableEntry(result, offset, sorted_tables[i].tag); 1328 } 1329 } 1330 1331 if (PREDICT_FALSE(!out->Write(&output[0], output.size()))) { 1332 return FONT_COMPRESSION_FAILURE(); 1333 } 1334 metadata->header_checksum = ComputeULongSum(&output[0], output.size()); 1335 return true; 1336 } 1337 1338 } // namespace 1339 1340 size_t ComputeWOFF2FinalSize(const uint8_t* data, size_t length) { 1341 Buffer file(data, length); 1342 uint32_t total_length; 1343 1344 if (!file.Skip(16) || 1345 !file.ReadU32(&total_length)) { 1346 return 0; 1347 } 1348 return total_length; 1349 } 1350 1351 bool ConvertWOFF2ToTTF(uint8_t *result, size_t result_length, 1352 const uint8_t *data, size_t length) { 1353 WOFF2MemoryOut out(result, result_length); 1354 return ConvertWOFF2ToTTF(data, length, &out); 1355 } 1356 1357 bool ConvertWOFF2ToTTF(const uint8_t* data, size_t length, 1358 WOFF2Out* out) { 1359 RebuildMetadata metadata; 1360 WOFF2Header hdr; 1361 if (!ReadWOFF2Header(data, length, &hdr)) { 1362 return FONT_COMPRESSION_FAILURE(); 1363 } 1364 1365 if (!WriteHeaders(data, length, &metadata, &hdr, out)) { 1366 return FONT_COMPRESSION_FAILURE(); 1367 } 1368 1369 const float compression_ratio = (float) hdr.uncompressed_size / length; 1370 if (compression_ratio > kMaxPlausibleCompressionRatio) { 1371 #ifdef FONT_COMPRESSION_BIN 1372 fprintf(stderr, "Implausible compression ratio %.01f\n", compression_ratio); 1373 #endif 1374 return FONT_COMPRESSION_FAILURE(); 1375 } 1376 1377 const uint8_t* src_buf = data + hdr.compressed_offset; 1378 std::vector<uint8_t> uncompressed_buf(hdr.uncompressed_size); 1379 if (PREDICT_FALSE(hdr.uncompressed_size < 1)) { 1380 return FONT_COMPRESSION_FAILURE(); 1381 } 1382 if (PREDICT_FALSE(!Woff2Uncompress(&uncompressed_buf[0], 1383 hdr.uncompressed_size, src_buf, 1384 hdr.compressed_length))) { 1385 return FONT_COMPRESSION_FAILURE(); 1386 } 1387 1388 for (size_t i = 0; i < metadata.font_infos.size(); i++) { 1389 if (PREDICT_FALSE(!ReconstructFont(&uncompressed_buf[0], 1390 hdr.uncompressed_size, 1391 &metadata, &hdr, i, out))) { 1392 return FONT_COMPRESSION_FAILURE(); 1393 } 1394 } 1395 1396 return true; 1397 } 1398 1399 } // namespace woff2