1// Copyright (c) 2009 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "ots.h" 6 7#include <sys/types.h> 8#include <zlib.h> 9 10#include <algorithm> 11#include <cstdlib> 12#include <cstring> 13#include <limits> 14#include <map> 15#include <vector> 16 17#include "woff2.h" 18 19// The OpenType Font File 20// http://www.microsoft.com/typography/otspec/cmap.htm 21 22namespace { 23 24bool g_debug_output = true; 25bool g_enable_woff2 = false; 26 27struct OpenTypeTable { 28 uint32_t tag; 29 uint32_t chksum; 30 uint32_t offset; 31 uint32_t length; 32 uint32_t uncompressed_length; 33}; 34 35bool CheckTag(uint32_t tag_value) { 36 for (unsigned i = 0; i < 4; ++i) { 37 const uint32_t check = tag_value & 0xff; 38 if (check < 32 || check > 126) { 39 return false; // non-ASCII character found. 40 } 41 tag_value >>= 8; 42 } 43 return true; 44} 45 46uint32_t Tag(const char *tag_str) { 47 uint32_t ret; 48 std::memcpy(&ret, tag_str, 4); 49 return ret; 50} 51 52struct OutputTable { 53 uint32_t tag; 54 size_t offset; 55 size_t length; 56 uint32_t chksum; 57 58 static bool SortByTag(const OutputTable& a, const OutputTable& b) { 59 const uint32_t atag = ntohl(a.tag); 60 const uint32_t btag = ntohl(b.tag); 61 return atag < btag; 62 } 63}; 64 65struct Arena { 66 public: 67 ~Arena() { 68 for (std::vector<uint8_t*>::iterator 69 i = hunks_.begin(); i != hunks_.end(); ++i) { 70 delete[] *i; 71 } 72 } 73 74 uint8_t* Allocate(size_t length) { 75 uint8_t* p = new uint8_t[length]; 76 hunks_.push_back(p); 77 return p; 78 } 79 80 private: 81 std::vector<uint8_t*> hunks_; 82}; 83 84const struct { 85 const char* tag; 86 bool (*parse)(ots::OpenTypeFile *otf, const uint8_t *data, size_t length); 87 bool (*serialise)(ots::OTSStream *out, ots::OpenTypeFile *file); 88 bool (*should_serialise)(ots::OpenTypeFile *file); 89 void (*free)(ots::OpenTypeFile *file); 90 bool required; 91} table_parsers[] = { 92 { "maxp", ots::ots_maxp_parse, ots::ots_maxp_serialise, 93 ots::ots_maxp_should_serialise, ots::ots_maxp_free, true }, 94 { "head", ots::ots_head_parse, ots::ots_head_serialise, 95 ots::ots_head_should_serialise, ots::ots_head_free, true }, 96 { "OS/2", ots::ots_os2_parse, ots::ots_os2_serialise, 97 ots::ots_os2_should_serialise, ots::ots_os2_free, true }, 98 { "cmap", ots::ots_cmap_parse, ots::ots_cmap_serialise, 99 ots::ots_cmap_should_serialise, ots::ots_cmap_free, true }, 100 { "hhea", ots::ots_hhea_parse, ots::ots_hhea_serialise, 101 ots::ots_hhea_should_serialise, ots::ots_hhea_free, true }, 102 { "hmtx", ots::ots_hmtx_parse, ots::ots_hmtx_serialise, 103 ots::ots_hmtx_should_serialise, ots::ots_hmtx_free, true }, 104 { "name", ots::ots_name_parse, ots::ots_name_serialise, 105 ots::ots_name_should_serialise, ots::ots_name_free, true }, 106 { "post", ots::ots_post_parse, ots::ots_post_serialise, 107 ots::ots_post_should_serialise, ots::ots_post_free, true }, 108 { "loca", ots::ots_loca_parse, ots::ots_loca_serialise, 109 ots::ots_loca_should_serialise, ots::ots_loca_free, false }, 110 { "glyf", ots::ots_glyf_parse, ots::ots_glyf_serialise, 111 ots::ots_glyf_should_serialise, ots::ots_glyf_free, false }, 112 { "CFF ", ots::ots_cff_parse, ots::ots_cff_serialise, 113 ots::ots_cff_should_serialise, ots::ots_cff_free, false }, 114 { "VDMX", ots::ots_vdmx_parse, ots::ots_vdmx_serialise, 115 ots::ots_vdmx_should_serialise, ots::ots_vdmx_free, false }, 116 { "hdmx", ots::ots_hdmx_parse, ots::ots_hdmx_serialise, 117 ots::ots_hdmx_should_serialise, ots::ots_hdmx_free, false }, 118 { "gasp", ots::ots_gasp_parse, ots::ots_gasp_serialise, 119 ots::ots_gasp_should_serialise, ots::ots_gasp_free, false }, 120 { "cvt ", ots::ots_cvt_parse, ots::ots_cvt_serialise, 121 ots::ots_cvt_should_serialise, ots::ots_cvt_free, false }, 122 { "fpgm", ots::ots_fpgm_parse, ots::ots_fpgm_serialise, 123 ots::ots_fpgm_should_serialise, ots::ots_fpgm_free, false }, 124 { "prep", ots::ots_prep_parse, ots::ots_prep_serialise, 125 ots::ots_prep_should_serialise, ots::ots_prep_free, false }, 126 { "LTSH", ots::ots_ltsh_parse, ots::ots_ltsh_serialise, 127 ots::ots_ltsh_should_serialise, ots::ots_ltsh_free, false }, 128 { "VORG", ots::ots_vorg_parse, ots::ots_vorg_serialise, 129 ots::ots_vorg_should_serialise, ots::ots_vorg_free, false }, 130 { "kern", ots::ots_kern_parse, ots::ots_kern_serialise, 131 ots::ots_kern_should_serialise, ots::ots_kern_free, false }, 132 // We need to parse GDEF table in advance of parsing GSUB/GPOS tables 133 // because they could refer GDEF table. 134 { "GDEF", ots::ots_gdef_parse, ots::ots_gdef_serialise, 135 ots::ots_gdef_should_serialise, ots::ots_gdef_free, false }, 136 { "GPOS", ots::ots_gpos_parse, ots::ots_gpos_serialise, 137 ots::ots_gpos_should_serialise, ots::ots_gpos_free, false }, 138 { "GSUB", ots::ots_gsub_parse, ots::ots_gsub_serialise, 139 ots::ots_gsub_should_serialise, ots::ots_gsub_free, false }, 140 { "vhea", ots::ots_vhea_parse, ots::ots_vhea_serialise, 141 ots::ots_vhea_should_serialise, ots::ots_vhea_free, false }, 142 { "vmtx", ots::ots_vmtx_parse, ots::ots_vmtx_serialise, 143 ots::ots_vmtx_should_serialise, ots::ots_vmtx_free, false }, 144 { "MATH", ots::ots_math_parse, ots::ots_math_serialise, 145 ots::ots_math_should_serialise, ots::ots_math_free, false }, 146 { "CBDT", ots::ots_cbdt_parse, ots::ots_cbdt_serialise, 147 ots::ots_cbdt_should_serialise, ots::ots_cbdt_free, false }, 148 { "CBLC", ots::ots_cblc_parse, ots::ots_cblc_serialise, 149 ots::ots_cblc_should_serialise, ots::ots_cblc_free, false }, 150 // TODO(bashi): Support mort, base, and jstf tables. 151 { 0, NULL, NULL, NULL, NULL, false }, 152}; 153 154bool ProcessGeneric(ots::OpenTypeFile *header, 155 uint32_t signature, 156 ots::OTSStream *output, 157 const uint8_t *data, size_t length, 158 const std::vector<OpenTypeTable>& tables, 159 ots::Buffer& file); 160 161bool ProcessTTF(ots::OpenTypeFile *header, 162 ots::OTSStream *output, const uint8_t *data, size_t length) { 163 ots::Buffer file(data, length); 164 165 // we disallow all files > 1GB in size for sanity. 166 if (length > 1024 * 1024 * 1024) { 167 return OTS_FAILURE(); 168 } 169 170 if (!file.ReadTag(&header->version)) { 171 return OTS_FAILURE(); 172 } 173 if (!ots::IsValidVersionTag(header->version)) { 174 return OTS_FAILURE(); 175 } 176 177 if (!file.ReadU16(&header->num_tables) || 178 !file.ReadU16(&header->search_range) || 179 !file.ReadU16(&header->entry_selector) || 180 !file.ReadU16(&header->range_shift)) { 181 return OTS_FAILURE(); 182 } 183 184 // search_range is (Maximum power of 2 <= numTables) x 16. Thus, to avoid 185 // overflow num_tables is, at most, 2^16 / 16 = 2^12 186 if (header->num_tables >= 4096 || header->num_tables < 1) { 187 return OTS_FAILURE(); 188 } 189 190 unsigned max_pow2 = 0; 191 while (1u << (max_pow2 + 1) <= header->num_tables) { 192 max_pow2++; 193 } 194 const uint16_t expected_search_range = (1u << max_pow2) << 4; 195 196 // Don't call ots_failure() here since ~25% of fonts (250+ fonts) in 197 // http://www.princexml.com/fonts/ have unexpected search_range value. 198 if (header->search_range != expected_search_range) { 199 OTS_WARNING("bad search range"); 200 header->search_range = expected_search_range; // Fix the value. 201 } 202 203 // entry_selector is Log2(maximum power of 2 <= numTables) 204 if (header->entry_selector != max_pow2) { 205 return OTS_FAILURE(); 206 } 207 208 // range_shift is NumTables x 16-searchRange. We know that 16*num_tables 209 // doesn't over flow because we range checked it above. Also, we know that 210 // it's > header->search_range by construction of search_range. 211 const uint32_t expected_range_shift 212 = 16 * header->num_tables - header->search_range; 213 if (header->range_shift != expected_range_shift) { 214 OTS_WARNING("bad range shift"); 215 header->range_shift = expected_range_shift; // the same as above. 216 } 217 218 // Next up is the list of tables. 219 std::vector<OpenTypeTable> tables; 220 221 for (unsigned i = 0; i < header->num_tables; ++i) { 222 OpenTypeTable table; 223 if (!file.ReadTag(&table.tag) || 224 !file.ReadU32(&table.chksum) || 225 !file.ReadU32(&table.offset) || 226 !file.ReadU32(&table.length)) { 227 return OTS_FAILURE(); 228 } 229 230 table.uncompressed_length = table.length; 231 tables.push_back(table); 232 } 233 234 return ProcessGeneric(header, header->version, output, data, length, 235 tables, file); 236} 237 238bool ProcessWOFF(ots::OpenTypeFile *header, 239 ots::OTSStream *output, const uint8_t *data, size_t length) { 240 ots::Buffer file(data, length); 241 242 // we disallow all files > 1GB in size for sanity. 243 if (length > 1024 * 1024 * 1024) { 244 return OTS_FAILURE(); 245 } 246 247 uint32_t woff_tag; 248 if (!file.ReadTag(&woff_tag)) { 249 return OTS_FAILURE(); 250 } 251 252 if (woff_tag != Tag("wOFF")) { 253 return OTS_FAILURE(); 254 } 255 256 if (!file.ReadTag(&header->version)) { 257 return OTS_FAILURE(); 258 } 259 if (!ots::IsValidVersionTag(header->version)) { 260 return OTS_FAILURE(); 261 } 262 263 header->search_range = 0; 264 header->entry_selector = 0; 265 header->range_shift = 0; 266 267 uint32_t reported_length; 268 if (!file.ReadU32(&reported_length) || length != reported_length) { 269 return OTS_FAILURE(); 270 } 271 272 if (!file.ReadU16(&header->num_tables) || !header->num_tables) { 273 return OTS_FAILURE(); 274 } 275 276 uint16_t reserved_value; 277 if (!file.ReadU16(&reserved_value) || reserved_value) { 278 return OTS_FAILURE(); 279 } 280 281 uint32_t reported_total_sfnt_size; 282 if (!file.ReadU32(&reported_total_sfnt_size)) { 283 return OTS_FAILURE(); 284 } 285 286 // We don't care about these fields of the header: 287 // uint16_t major_version, minor_version 288 if (!file.Skip(2 * 2)) { 289 return OTS_FAILURE(); 290 } 291 292 // Checks metadata block size. 293 uint32_t meta_offset; 294 uint32_t meta_length; 295 uint32_t meta_length_orig; 296 if (!file.ReadU32(&meta_offset) || 297 !file.ReadU32(&meta_length) || 298 !file.ReadU32(&meta_length_orig)) { 299 return OTS_FAILURE(); 300 } 301 if (meta_offset) { 302 if (meta_offset >= length || length - meta_offset < meta_length) { 303 return OTS_FAILURE(); 304 } 305 } 306 307 // Checks private data block size. 308 uint32_t priv_offset; 309 uint32_t priv_length; 310 if (!file.ReadU32(&priv_offset) || 311 !file.ReadU32(&priv_length)) { 312 return OTS_FAILURE(); 313 } 314 if (priv_offset) { 315 if (priv_offset >= length || length - priv_offset < priv_length) { 316 return OTS_FAILURE(); 317 } 318 } 319 320 // Next up is the list of tables. 321 std::vector<OpenTypeTable> tables; 322 323 uint32_t first_index = 0; 324 uint32_t last_index = 0; 325 // Size of sfnt header plus size of table records. 326 uint64_t total_sfnt_size = 12 + 16 * header->num_tables; 327 for (unsigned i = 0; i < header->num_tables; ++i) { 328 OpenTypeTable table; 329 if (!file.ReadTag(&table.tag) || 330 !file.ReadU32(&table.offset) || 331 !file.ReadU32(&table.length) || 332 !file.ReadU32(&table.uncompressed_length) || 333 !file.ReadU32(&table.chksum)) { 334 return OTS_FAILURE(); 335 } 336 337 total_sfnt_size += ots::Round4(table.uncompressed_length); 338 if (total_sfnt_size > std::numeric_limits<uint32_t>::max()) { 339 return OTS_FAILURE(); 340 } 341 tables.push_back(table); 342 if (i == 0 || tables[first_index].offset > table.offset) 343 first_index = i; 344 if (i == 0 || tables[last_index].offset < table.offset) 345 last_index = i; 346 } 347 348 if (reported_total_sfnt_size != total_sfnt_size) { 349 return OTS_FAILURE(); 350 } 351 352 // Table data must follow immediately after the header. 353 if (tables[first_index].offset != ots::Round4(file.offset())) { 354 return OTS_FAILURE(); 355 } 356 357 if (tables[last_index].offset >= length || 358 length - tables[last_index].offset < tables[last_index].length) { 359 return OTS_FAILURE(); 360 } 361 // Blocks must follow immediately after the previous block. 362 // (Except for padding with a maximum of three null bytes) 363 uint64_t block_end = ots::Round4( 364 static_cast<uint64_t>(tables[last_index].offset) + 365 static_cast<uint64_t>(tables[last_index].length)); 366 if (block_end > std::numeric_limits<uint32_t>::max()) { 367 return OTS_FAILURE(); 368 } 369 if (meta_offset) { 370 if (block_end != meta_offset) { 371 return OTS_FAILURE(); 372 } 373 block_end = ots::Round4(static_cast<uint64_t>(meta_offset) + 374 static_cast<uint64_t>(meta_length)); 375 if (block_end > std::numeric_limits<uint32_t>::max()) { 376 return OTS_FAILURE(); 377 } 378 } 379 if (priv_offset) { 380 if (block_end != priv_offset) { 381 return OTS_FAILURE(); 382 } 383 block_end = ots::Round4(static_cast<uint64_t>(priv_offset) + 384 static_cast<uint64_t>(priv_length)); 385 if (block_end > std::numeric_limits<uint32_t>::max()) { 386 return OTS_FAILURE(); 387 } 388 } 389 if (block_end != ots::Round4(length)) { 390 return OTS_FAILURE(); 391 } 392 393 return ProcessGeneric(header, woff_tag, output, data, length, tables, file); 394} 395 396bool ProcessWOFF2(ots::OpenTypeFile *header, 397 ots::OTSStream *output, const uint8_t *data, size_t length) { 398 size_t decompressed_size = ots::ComputeWOFF2FinalSize(data, length); 399 if (decompressed_size == 0) { 400 return OTS_FAILURE(); 401 } 402 // decompressed font must be <= 30MB 403 if (decompressed_size > 30 * 1024 * 1024) { 404 return OTS_FAILURE(); 405 } 406 407 std::vector<uint8_t> decompressed_buffer(decompressed_size); 408 if (!ots::ConvertWOFF2ToTTF(&decompressed_buffer[0], decompressed_size, 409 data, length)) { 410 return OTS_FAILURE(); 411 } 412 return ProcessTTF(header, output, &decompressed_buffer[0], decompressed_size); 413} 414 415bool ProcessGeneric(ots::OpenTypeFile *header, uint32_t signature, 416 ots::OTSStream *output, 417 const uint8_t *data, size_t length, 418 const std::vector<OpenTypeTable>& tables, 419 ots::Buffer& file) { 420 const size_t data_offset = file.offset(); 421 422 uint32_t uncompressed_sum = 0; 423 424 for (unsigned i = 0; i < header->num_tables; ++i) { 425 // the tables must be sorted by tag (when taken as big-endian numbers). 426 // This also remove the possibility of duplicate tables. 427 if (i) { 428 const uint32_t this_tag = ntohl(tables[i].tag); 429 const uint32_t prev_tag = ntohl(tables[i - 1].tag); 430 if (this_tag <= prev_tag) { 431 return OTS_FAILURE(); 432 } 433 } 434 435 // all tag names must be built from printable ASCII characters 436 if (!CheckTag(tables[i].tag)) { 437 return OTS_FAILURE(); 438 } 439 440 // tables must be 4-byte aligned 441 if (tables[i].offset & 3) { 442 return OTS_FAILURE(); 443 } 444 445 // and must be within the file 446 if (tables[i].offset < data_offset || tables[i].offset >= length) { 447 return OTS_FAILURE(); 448 } 449 // disallow all tables with a zero length 450 if (tables[i].length < 1) { 451 // Note: malayalam.ttf has zero length CVT table... 452 return OTS_FAILURE(); 453 } 454 // disallow all tables with a length > 1GB 455 if (tables[i].length > 1024 * 1024 * 1024) { 456 return OTS_FAILURE(); 457 } 458 // disallow tables where the uncompressed size is < the compressed size. 459 if (tables[i].uncompressed_length < tables[i].length) { 460 return OTS_FAILURE(); 461 } 462 if (tables[i].uncompressed_length > tables[i].length) { 463 // We'll probably be decompressing this table. 464 465 // disallow all tables which uncompress to > 30 MB 466 if (tables[i].uncompressed_length > 30 * 1024 * 1024) { 467 return OTS_FAILURE(); 468 } 469 if (uncompressed_sum + tables[i].uncompressed_length < uncompressed_sum) { 470 return OTS_FAILURE(); 471 } 472 473 uncompressed_sum += tables[i].uncompressed_length; 474 } 475 // since we required that the file be < 1GB in length, and that the table 476 // length is < 1GB, the following addtion doesn't overflow 477 uint32_t end_byte = tables[i].offset + tables[i].length; 478 // Tables in the WOFF file must be aligned 4-byte boundary. 479 if (signature == Tag("wOFF")) { 480 end_byte = ots::Round4(end_byte); 481 } 482 if (!end_byte || end_byte > length) { 483 return OTS_FAILURE(); 484 } 485 } 486 487 // All decompressed tables uncompressed must be <= 30MB. 488 if (uncompressed_sum > 30 * 1024 * 1024) { 489 return OTS_FAILURE(); 490 } 491 492 std::map<uint32_t, OpenTypeTable> table_map; 493 for (unsigned i = 0; i < header->num_tables; ++i) { 494 table_map[tables[i].tag] = tables[i]; 495 } 496 497 // check that the tables are not overlapping. 498 std::vector<std::pair<uint32_t, uint8_t> > overlap_checker; 499 for (unsigned i = 0; i < header->num_tables; ++i) { 500 overlap_checker.push_back( 501 std::make_pair(tables[i].offset, static_cast<uint8_t>(1) /* start */)); 502 overlap_checker.push_back( 503 std::make_pair(tables[i].offset + tables[i].length, 504 static_cast<uint8_t>(0) /* end */)); 505 } 506 std::sort(overlap_checker.begin(), overlap_checker.end()); 507 int overlap_count = 0; 508 for (unsigned i = 0; i < overlap_checker.size(); ++i) { 509 overlap_count += (overlap_checker[i].second ? 1 : -1); 510 if (overlap_count > 1) { 511 return OTS_FAILURE(); 512 } 513 } 514 515 Arena arena; 516 517 for (unsigned i = 0; ; ++i) { 518 if (table_parsers[i].parse == NULL) break; 519 520 const std::map<uint32_t, OpenTypeTable>::const_iterator it 521 = table_map.find(Tag(table_parsers[i].tag)); 522 523 if (it == table_map.end()) { 524 if (table_parsers[i].required) { 525 return OTS_FAILURE(); 526 } 527 continue; 528 } 529 530 const uint8_t* table_data; 531 size_t table_length; 532 533 if (it->second.uncompressed_length != it->second.length) { 534 // compressed table. Need to uncompress into memory first. 535 table_length = it->second.uncompressed_length; 536 table_data = arena.Allocate(table_length); 537 uLongf dest_len = table_length; 538 int r = uncompress((Bytef*) table_data, &dest_len, 539 data + it->second.offset, it->second.length); 540 if (r != Z_OK || dest_len != table_length) { 541 return OTS_FAILURE(); 542 } 543 } else { 544 // uncompressed table. We can process directly from memory. 545 table_data = data + it->second.offset; 546 table_length = it->second.length; 547 } 548 549 if (!table_parsers[i].parse(header, table_data, table_length)) { 550 return OTS_FAILURE(); 551 } 552 } 553 554 if (header->cff) { 555 // font with PostScript glyph 556 if (header->version != Tag("OTTO")) { 557 return OTS_FAILURE(); 558 } 559 if (header->glyf || header->loca) { 560 // mixing outline formats is not recommended 561 return OTS_FAILURE(); 562 } 563 } else { 564 if ((!header->glyf || !header->loca) && (!header->cbdt || !header->cblc)) { 565 // No TrueType glyph or color bitmap found. 566 return OTS_FAILURE(); 567 } 568 } 569 570 unsigned num_output_tables = 0; 571 for (unsigned i = 0; ; ++i) { 572 if (table_parsers[i].parse == NULL) { 573 break; 574 } 575 576 if (table_parsers[i].should_serialise(header)) { 577 num_output_tables++; 578 } 579 } 580 581 unsigned max_pow2 = 0; 582 while (1u << (max_pow2 + 1) <= num_output_tables) { 583 max_pow2++; 584 } 585 const uint16_t output_search_range = (1u << max_pow2) << 4; 586 587 output->ResetChecksum(); 588 if (!output->WriteTag(header->version) || 589 !output->WriteU16(num_output_tables) || 590 !output->WriteU16(output_search_range) || 591 !output->WriteU16(max_pow2) || 592 !output->WriteU16((num_output_tables << 4) - output_search_range)) { 593 return OTS_FAILURE(); 594 } 595 const uint32_t offset_table_chksum = output->chksum(); 596 597 const size_t table_record_offset = output->Tell(); 598 if (!output->Pad(16 * num_output_tables)) { 599 return OTS_FAILURE(); 600 } 601 602 std::vector<OutputTable> out_tables; 603 604 size_t head_table_offset = 0; 605 for (unsigned i = 0; ; ++i) { 606 if (table_parsers[i].parse == NULL) { 607 break; 608 } 609 610 if (!table_parsers[i].should_serialise(header)) { 611 continue; 612 } 613 614 OutputTable out; 615 uint32_t tag = Tag(table_parsers[i].tag); 616 out.tag = tag; 617 out.offset = output->Tell(); 618 619 output->ResetChecksum(); 620 if (tag == Tag("head")) { 621 head_table_offset = out.offset; 622 } 623 if (!table_parsers[i].serialise(output, header)) { 624 return OTS_FAILURE(); 625 } 626 627 const size_t end_offset = output->Tell(); 628 if (end_offset <= out.offset) { 629 // paranoid check. |end_offset| is supposed to be greater than the offset, 630 // as long as the Tell() interface is implemented correctly. 631 return OTS_FAILURE(); 632 } 633 out.length = end_offset - out.offset; 634 635 // align tables to four bytes 636 if (!output->Pad((4 - (end_offset & 3)) % 4)) { 637 return OTS_FAILURE(); 638 } 639 out.chksum = output->chksum(); 640 out_tables.push_back(out); 641 } 642 643 const size_t end_of_file = output->Tell(); 644 645 // Need to sort the output tables for inclusion in the file 646 std::sort(out_tables.begin(), out_tables.end(), OutputTable::SortByTag); 647 if (!output->Seek(table_record_offset)) { 648 return OTS_FAILURE(); 649 } 650 651 output->ResetChecksum(); 652 uint32_t tables_chksum = 0; 653 for (unsigned i = 0; i < out_tables.size(); ++i) { 654 if (!output->WriteTag(out_tables[i].tag) || 655 !output->WriteU32(out_tables[i].chksum) || 656 !output->WriteU32(out_tables[i].offset) || 657 !output->WriteU32(out_tables[i].length)) { 658 return OTS_FAILURE(); 659 } 660 tables_chksum += out_tables[i].chksum; 661 } 662 const uint32_t table_record_chksum = output->chksum(); 663 664 // http://www.microsoft.com/typography/otspec/otff.htm 665 const uint32_t file_chksum 666 = offset_table_chksum + tables_chksum + table_record_chksum; 667 const uint32_t chksum_magic = static_cast<uint32_t>(0xb1b0afba) - file_chksum; 668 669 // seek into the 'head' table and write in the checksum magic value 670 if (!head_table_offset) { 671 return OTS_FAILURE(); // not reached. 672 } 673 if (!output->Seek(head_table_offset + 8)) { 674 return OTS_FAILURE(); 675 } 676 if (!output->WriteU32(chksum_magic)) { 677 return OTS_FAILURE(); 678 } 679 680 if (!output->Seek(end_of_file)) { 681 return OTS_FAILURE(); 682 } 683 684 return true; 685} 686 687} // namespace 688 689namespace ots { 690 691bool g_drop_color_bitmap_tables = true; 692 693bool IsValidVersionTag(uint32_t tag) { 694 return tag == Tag("\x00\x01\x00\x00") || 695 // OpenType fonts with CFF data have 'OTTO' tag. 696 tag == Tag("OTTO") || 697 // Older Mac fonts might have 'true' or 'typ1' tag. 698 tag == Tag("true") || 699 tag == Tag("typ1"); 700} 701 702void DisableDebugOutput() { 703 g_debug_output = false; 704} 705 706void EnableWOFF2() { 707 g_enable_woff2 = true; 708} 709 710void DoNotDropColorBitmapTables() { 711 g_drop_color_bitmap_tables = false; 712} 713 714bool Process(OTSStream *output, const uint8_t *data, size_t length) { 715 OpenTypeFile header; 716 if (length < 4) { 717 return OTS_FAILURE(); 718 } 719 720 bool result; 721 if (data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && data[3] == 'F') { 722 result = ProcessWOFF(&header, output, data, length); 723 } else if (g_enable_woff2 && 724 data[0] == 'w' && data[1] == 'O' && data[2] == 'F' && 725 data[3] == '2') { 726 result = ProcessWOFF2(&header, output, data, length); 727 } else { 728 result = ProcessTTF(&header, output, data, length); 729 } 730 731 for (unsigned i = 0; ; ++i) { 732 if (table_parsers[i].parse == NULL) break; 733 table_parsers[i].free(&header); 734 } 735 return result; 736} 737 738#if !defined(_MSC_VER) && defined(OTS_DEBUG) 739bool Failure(const char *f, int l, const char *fn) { 740 if (g_debug_output) { 741 std::fprintf(stderr, "ERROR at %s:%d (%s)\n", f, l, fn); 742 std::fflush(stderr); 743 } 744 return false; 745} 746 747void Warning(const char *f, int l, const char *format, ...) { 748 if (g_debug_output) { 749 std::fprintf(stderr, "WARNING at %s:%d: ", f, l); 750 std::va_list va; 751 va_start(va, format); 752 std::vfprintf(stderr, format, va); 753 va_end(va); 754 std::fprintf(stderr, "\n"); 755 std::fflush(stderr); 756 } 757} 758#endif 759 760} // namespace ots 761