1 // Copyright (c) 2011 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 "gsub.h" 6 7 #include <limits> 8 #include <vector> 9 10 #include "gdef.h" 11 #include "gpos.h" 12 #include "layout.h" 13 #include "maxp.h" 14 15 // GSUB - The Glyph Substitution Table 16 // http://www.microsoft.com/typography/otspec/gsub.htm 17 18 namespace { 19 20 // The GSUB header size 21 const size_t kGsubHeaderSize = 8; 22 23 enum GSUB_TYPE { 24 GSUB_TYPE_SINGLE = 1, 25 GSUB_TYPE_MULTIPLE = 2, 26 GSUB_TYPE_ALTERNATE = 3, 27 GSUB_TYPE_LIGATURE = 4, 28 GSUB_TYPE_CONTEXT = 5, 29 GSUB_TYPE_CHANGING_CONTEXT = 6, 30 GSUB_TYPE_EXTENSION_SUBSTITUTION = 7, 31 GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE = 8, 32 GSUB_TYPE_RESERVED = 9 33 }; 34 35 // Lookup type parsers. 36 bool ParseSingleSubstitution(const ots::OpenTypeFile *file, 37 const uint8_t *data, const size_t length); 38 bool ParseMutipleSubstitution(const ots::OpenTypeFile *file, 39 const uint8_t *data, const size_t length); 40 bool ParseAlternateSubstitution(const ots::OpenTypeFile *file, 41 const uint8_t *data, const size_t length); 42 bool ParseLigatureSubstitution(const ots::OpenTypeFile *file, 43 const uint8_t *data, const size_t length); 44 bool ParseContextSubstitution(const ots::OpenTypeFile *file, 45 const uint8_t *data, const size_t length); 46 bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file, 47 const uint8_t *data, 48 const size_t length); 49 bool ParseExtensionSubstitution(const ots::OpenTypeFile *file, 50 const uint8_t *data, const size_t length); 51 bool ParseReverseChainingContextSingleSubstitution( 52 const ots::OpenTypeFile *file, const uint8_t *data, const size_t length); 53 54 const ots::LookupSubtableParser::TypeParser kGsubTypeParsers[] = { 55 {GSUB_TYPE_SINGLE, ParseSingleSubstitution}, 56 {GSUB_TYPE_MULTIPLE, ParseMutipleSubstitution}, 57 {GSUB_TYPE_ALTERNATE, ParseAlternateSubstitution}, 58 {GSUB_TYPE_LIGATURE, ParseLigatureSubstitution}, 59 {GSUB_TYPE_CONTEXT, ParseContextSubstitution}, 60 {GSUB_TYPE_CHANGING_CONTEXT, ParseChainingContextSubstitution}, 61 {GSUB_TYPE_EXTENSION_SUBSTITUTION, ParseExtensionSubstitution}, 62 {GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE, 63 ParseReverseChainingContextSingleSubstitution} 64 }; 65 66 const ots::LookupSubtableParser kGsubLookupSubtableParser = { 67 arraysize(kGsubTypeParsers), 68 GSUB_TYPE_EXTENSION_SUBSTITUTION, kGsubTypeParsers 69 }; 70 71 // Lookup Type 1: 72 // Single Substitution Subtable 73 bool ParseSingleSubstitution(const ots::OpenTypeFile *file, 74 const uint8_t *data, const size_t length) { 75 ots::Buffer subtable(data, length); 76 77 uint16_t format = 0; 78 uint16_t offset_coverage = 0; 79 80 if (!subtable.ReadU16(&format) || 81 !subtable.ReadU16(&offset_coverage)) { 82 return OTS_FAILURE(); 83 } 84 85 const uint16_t num_glyphs = file->maxp->num_glyphs; 86 if (format == 1) { 87 // Parse SingleSubstFormat1 88 int16_t delta_glyph_id = 0; 89 if (!subtable.ReadS16(&delta_glyph_id)) { 90 return OTS_FAILURE(); 91 } 92 if (std::abs(delta_glyph_id) >= num_glyphs) { 93 return OTS_FAILURE(); 94 } 95 } else if (format == 2) { 96 // Parse SingleSubstFormat2 97 uint16_t glyph_count = 0; 98 if (!subtable.ReadU16(&glyph_count)) { 99 return OTS_FAILURE(); 100 } 101 if (glyph_count > num_glyphs) { 102 return OTS_FAILURE(); 103 } 104 for (unsigned i = 0; i < glyph_count; ++i) { 105 uint16_t substitute = 0; 106 if (!subtable.ReadU16(&substitute)) { 107 return OTS_FAILURE(); 108 } 109 if (substitute >= num_glyphs) { 110 OTS_WARNING("too large substitute: %u", substitute); 111 return OTS_FAILURE(); 112 } 113 } 114 } else { 115 return OTS_FAILURE(); 116 } 117 118 if (offset_coverage < subtable.offset() || offset_coverage >= length) { 119 return OTS_FAILURE(); 120 } 121 if (!ots::ParseCoverageTable(data + offset_coverage, 122 length - offset_coverage, num_glyphs)) { 123 return OTS_FAILURE(); 124 } 125 126 return true; 127 } 128 129 bool ParseSequenceTable(const uint8_t *data, const size_t length, 130 const uint16_t num_glyphs) { 131 ots::Buffer subtable(data, length); 132 133 uint16_t glyph_count = 0; 134 if (!subtable.ReadU16(&glyph_count)) { 135 return OTS_FAILURE(); 136 } 137 if (glyph_count > num_glyphs) { 138 return OTS_FAILURE(); 139 } 140 for (unsigned i = 0; i < glyph_count; ++i) { 141 uint16_t substitute = 0; 142 if (!subtable.ReadU16(&substitute)) { 143 return OTS_FAILURE(); 144 } 145 if (substitute >= num_glyphs) { 146 return OTS_FAILURE(); 147 } 148 } 149 150 return true; 151 } 152 153 // Lookup Type 2: 154 // Multiple Substitution Subtable 155 bool ParseMutipleSubstitution(const ots::OpenTypeFile *file, 156 const uint8_t *data, const size_t length) { 157 ots::Buffer subtable(data, length); 158 159 uint16_t format = 0; 160 uint16_t offset_coverage = 0; 161 uint16_t sequence_count = 0; 162 163 if (!subtable.ReadU16(&format) || 164 !subtable.ReadU16(&offset_coverage) || 165 !subtable.ReadU16(&sequence_count)) { 166 return OTS_FAILURE(); 167 } 168 169 if (format != 1) { 170 return OTS_FAILURE(); 171 } 172 173 const uint16_t num_glyphs = file->maxp->num_glyphs; 174 const unsigned sequence_end = static_cast<unsigned>(6) + 175 sequence_count * 2; 176 if (sequence_end > std::numeric_limits<uint16_t>::max()) { 177 return OTS_FAILURE(); 178 } 179 for (unsigned i = 0; i < sequence_count; ++i) { 180 uint16_t offset_sequence = 0; 181 if (!subtable.ReadU16(&offset_sequence)) { 182 return OTS_FAILURE(); 183 } 184 if (offset_sequence < sequence_end || offset_sequence >= length) { 185 return OTS_FAILURE(); 186 } 187 if (!ParseSequenceTable(data + offset_sequence, length - offset_sequence, 188 num_glyphs)) { 189 return OTS_FAILURE(); 190 } 191 } 192 193 if (offset_coverage < sequence_end || offset_coverage >= length) { 194 return OTS_FAILURE(); 195 } 196 if (!ots::ParseCoverageTable(data + offset_coverage, 197 length - offset_coverage, num_glyphs)) { 198 return OTS_FAILURE(); 199 } 200 201 return true; 202 } 203 204 bool ParseAlternateSetTable(const uint8_t *data, const size_t length, 205 const uint16_t num_glyphs) { 206 ots::Buffer subtable(data, length); 207 208 uint16_t glyph_count = 0; 209 if (!subtable.ReadU16(&glyph_count)) { 210 return OTS_FAILURE(); 211 } 212 if (glyph_count > num_glyphs) { 213 return OTS_FAILURE(); 214 } 215 for (unsigned i = 0; i < glyph_count; ++i) { 216 uint16_t alternate = 0; 217 if (!subtable.ReadU16(&alternate)) { 218 return OTS_FAILURE(); 219 } 220 if (alternate >= num_glyphs) { 221 OTS_WARNING("too arge alternate: %u", alternate); 222 return OTS_FAILURE(); 223 } 224 } 225 return true; 226 } 227 228 // Lookup Type 3: 229 // Alternate Substitution Subtable 230 bool ParseAlternateSubstitution(const ots::OpenTypeFile *file, 231 const uint8_t *data, const size_t length) { 232 ots::Buffer subtable(data, length); 233 234 uint16_t format = 0; 235 uint16_t offset_coverage = 0; 236 uint16_t alternate_set_count = 0; 237 238 if (!subtable.ReadU16(&format) || 239 !subtable.ReadU16(&offset_coverage) || 240 !subtable.ReadU16(&alternate_set_count)) { 241 return OTS_FAILURE(); 242 } 243 244 if (format != 1) { 245 return OTS_FAILURE(); 246 } 247 248 const uint16_t num_glyphs = file->maxp->num_glyphs; 249 const unsigned alternate_set_end = static_cast<unsigned>(6) + 250 alternate_set_count * 2; 251 if (alternate_set_end > std::numeric_limits<uint16_t>::max()) { 252 return OTS_FAILURE(); 253 } 254 for (unsigned i = 0; i < alternate_set_count; ++i) { 255 uint16_t offset_alternate_set = 0; 256 if (!subtable.ReadU16(&offset_alternate_set)) { 257 return OTS_FAILURE(); 258 } 259 if (offset_alternate_set < alternate_set_end || 260 offset_alternate_set >= length) { 261 return OTS_FAILURE(); 262 } 263 if (!ParseAlternateSetTable(data + offset_alternate_set, 264 length - offset_alternate_set, 265 num_glyphs)) { 266 return OTS_FAILURE(); 267 } 268 } 269 270 if (offset_coverage < alternate_set_end || offset_coverage >= length) { 271 return OTS_FAILURE(); 272 } 273 if (!ots::ParseCoverageTable(data + offset_coverage, 274 length - offset_coverage, num_glyphs)) { 275 return OTS_FAILURE(); 276 } 277 278 return true; 279 } 280 281 bool ParseLigatureTable(const uint8_t *data, const size_t length, 282 const uint16_t num_glyphs) { 283 ots::Buffer subtable(data, length); 284 285 uint16_t lig_glyph = 0; 286 uint16_t comp_count = 0; 287 288 if (!subtable.ReadU16(&lig_glyph) || 289 !subtable.ReadU16(&comp_count)) { 290 return OTS_FAILURE(); 291 } 292 293 if (lig_glyph >= num_glyphs) { 294 OTS_WARNING("too large lig_glyph: %u", lig_glyph); 295 return OTS_FAILURE(); 296 } 297 if (comp_count == 0 || comp_count > num_glyphs) { 298 return OTS_FAILURE(); 299 } 300 for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) { 301 uint16_t component = 0; 302 if (!subtable.ReadU16(&component)) { 303 return OTS_FAILURE(); 304 } 305 if (component >= num_glyphs) { 306 return OTS_FAILURE(); 307 } 308 } 309 310 return true; 311 } 312 313 bool ParseLigatureSetTable(const uint8_t *data, const size_t length, 314 const uint16_t num_glyphs) { 315 ots::Buffer subtable(data, length); 316 317 uint16_t ligature_count = 0; 318 319 if (!subtable.ReadU16(&ligature_count)) { 320 return OTS_FAILURE(); 321 } 322 323 const unsigned ligature_end = static_cast<unsigned>(2) + ligature_count * 2; 324 if (ligature_end > std::numeric_limits<uint16_t>::max()) { 325 return OTS_FAILURE(); 326 } 327 for (unsigned i = 0; i < ligature_count; ++i) { 328 uint16_t offset_ligature = 0; 329 if (!subtable.ReadU16(&offset_ligature)) { 330 return OTS_FAILURE(); 331 } 332 if (offset_ligature < ligature_end || offset_ligature >= length) { 333 return OTS_FAILURE(); 334 } 335 if (!ParseLigatureTable(data + offset_ligature, length - offset_ligature, 336 num_glyphs)) { 337 return OTS_FAILURE(); 338 } 339 } 340 341 return true; 342 } 343 344 // Lookup Type 4: 345 // Ligature Substitution Subtable 346 bool ParseLigatureSubstitution(const ots::OpenTypeFile *file, 347 const uint8_t *data, const size_t length) { 348 ots::Buffer subtable(data, length); 349 350 uint16_t format = 0; 351 uint16_t offset_coverage = 0; 352 uint16_t lig_set_count = 0; 353 354 if (!subtable.ReadU16(&format) || 355 !subtable.ReadU16(&offset_coverage) || 356 !subtable.ReadU16(&lig_set_count)) { 357 return OTS_FAILURE(); 358 } 359 360 if (format != 1) { 361 return OTS_FAILURE(); 362 } 363 364 const uint16_t num_glyphs = file->maxp->num_glyphs; 365 const unsigned ligature_set_end = static_cast<unsigned>(6) + 366 lig_set_count * 2; 367 if (ligature_set_end > std::numeric_limits<uint16_t>::max()) { 368 return OTS_FAILURE(); 369 } 370 for (unsigned i = 0; i < lig_set_count; ++i) { 371 uint16_t offset_ligature_set = 0; 372 if (!subtable.ReadU16(&offset_ligature_set)) { 373 return OTS_FAILURE(); 374 } 375 if (offset_ligature_set < ligature_set_end || 376 offset_ligature_set >= length) { 377 return OTS_FAILURE(); 378 } 379 if (!ParseLigatureSetTable(data + offset_ligature_set, 380 length - offset_ligature_set, num_glyphs)) { 381 return OTS_FAILURE(); 382 } 383 } 384 385 if (offset_coverage < ligature_set_end || offset_coverage >= length) { 386 return OTS_FAILURE(); 387 } 388 if (!ots::ParseCoverageTable(data + offset_coverage, 389 length - offset_coverage, num_glyphs)) { 390 return OTS_FAILURE(); 391 } 392 393 return true; 394 } 395 396 // Lookup Type 5: 397 // Contextual Substitution Subtable 398 bool ParseContextSubstitution(const ots::OpenTypeFile *file, 399 const uint8_t *data, const size_t length) { 400 return ots::ParseContextSubtable(data, length, file->maxp->num_glyphs, 401 file->gsub->num_lookups); 402 } 403 404 // Lookup Type 6: 405 // Chaining Contextual Substitution Subtable 406 bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file, 407 const uint8_t *data, 408 const size_t length) { 409 return ots::ParseChainingContextSubtable(data, length, 410 file->maxp->num_glyphs, 411 file->gsub->num_lookups); 412 } 413 414 // Lookup Type 7: 415 // Extension Substition 416 bool ParseExtensionSubstitution(const ots::OpenTypeFile *file, 417 const uint8_t *data, const size_t length) { 418 return ots::ParseExtensionSubtable(file, data, length, 419 &kGsubLookupSubtableParser); 420 } 421 422 // Lookup Type 8: 423 // Reverse Chaining Contexual Single Substitution Subtable 424 bool ParseReverseChainingContextSingleSubstitution( 425 const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) { 426 ots::Buffer subtable(data, length); 427 428 uint16_t format = 0; 429 uint16_t offset_coverage = 0; 430 431 if (!subtable.ReadU16(&format) || 432 !subtable.ReadU16(&offset_coverage)) { 433 return OTS_FAILURE(); 434 } 435 436 const uint16_t num_glyphs = file->maxp->num_glyphs; 437 438 uint16_t backtrack_glyph_count = 0; 439 if (!subtable.ReadU16(&backtrack_glyph_count)) { 440 return OTS_FAILURE(); 441 } 442 if (backtrack_glyph_count > num_glyphs) { 443 return OTS_FAILURE(); 444 } 445 std::vector<uint16_t> offsets_backtrack; 446 offsets_backtrack.reserve(backtrack_glyph_count); 447 for (unsigned i = 0; i < backtrack_glyph_count; ++i) { 448 uint16_t offset = 0; 449 if (!subtable.ReadU16(&offset)) { 450 return OTS_FAILURE(); 451 } 452 offsets_backtrack.push_back(offset); 453 } 454 455 uint16_t lookahead_glyph_count = 0; 456 if (!subtable.ReadU16(&lookahead_glyph_count)) { 457 return OTS_FAILURE(); 458 } 459 if (lookahead_glyph_count > num_glyphs) { 460 return OTS_FAILURE(); 461 } 462 std::vector<uint16_t> offsets_lookahead; 463 offsets_lookahead.reserve(lookahead_glyph_count); 464 for (unsigned i = 0; i < lookahead_glyph_count; ++i) { 465 uint16_t offset = 0; 466 if (!subtable.ReadU16(&offset)) { 467 return OTS_FAILURE(); 468 } 469 offsets_lookahead.push_back(offset); 470 } 471 472 uint16_t glyph_count = 0; 473 if (!subtable.ReadU16(&glyph_count)) { 474 return OTS_FAILURE(); 475 } 476 if (glyph_count > num_glyphs) { 477 return OTS_FAILURE(); 478 } 479 for (unsigned i = 0; i < glyph_count; ++i) { 480 uint16_t substitute = 0; 481 if (!subtable.ReadU16(&substitute)) { 482 return OTS_FAILURE(); 483 } 484 if (substitute >= num_glyphs) { 485 return OTS_FAILURE(); 486 } 487 } 488 489 const unsigned substitute_end = static_cast<unsigned>(10) + 490 (backtrack_glyph_count + lookahead_glyph_count + glyph_count) * 2; 491 if (substitute_end > std::numeric_limits<uint16_t>::max()) { 492 return OTS_FAILURE(); 493 } 494 495 if (offset_coverage < substitute_end || offset_coverage >= length) { 496 return OTS_FAILURE(); 497 } 498 if (!ots::ParseCoverageTable(data + offset_coverage, 499 length - offset_coverage, num_glyphs)) { 500 return OTS_FAILURE(); 501 } 502 503 for (unsigned i = 0; i < backtrack_glyph_count; ++i) { 504 if (offsets_backtrack[i] < substitute_end || 505 offsets_backtrack[i] >= length) { 506 return OTS_FAILURE(); 507 } 508 if (!ots::ParseCoverageTable(data + offsets_backtrack[i], 509 length - offsets_backtrack[i], num_glyphs)) { 510 return OTS_FAILURE(); 511 } 512 } 513 514 for (unsigned i = 0; i < lookahead_glyph_count; ++i) { 515 if (offsets_lookahead[i] < substitute_end || 516 offsets_lookahead[i] >= length) { 517 return OTS_FAILURE(); 518 } 519 if (!ots::ParseCoverageTable(data + offsets_lookahead[i], 520 length - offsets_lookahead[i], num_glyphs)) { 521 return OTS_FAILURE(); 522 } 523 } 524 525 return true; 526 } 527 528 } // namespace 529 530 #define DROP_THIS_TABLE \ 531 do { file->gsub->data = 0; file->gsub->length = 0; } while (0) 532 533 namespace ots { 534 535 // As far as I checked, following fonts contain invalid values in GSUB table. 536 // OTS will drop their GSUB table. 537 // 538 // # too large substitute (value is 0xFFFF) 539 // kaiu.ttf 540 // mingliub2.ttf 541 // mingliub1.ttf 542 // mingliub0.ttf 543 // GraublauWeb.otf 544 // GraublauWebBold.otf 545 // 546 // # too large alternate (value is 0xFFFF) 547 // ManchuFont.ttf 548 // 549 // # bad offset to lang sys table (NULL offset) 550 // DejaVuMonoSansBold.ttf 551 // DejaVuMonoSansBoldOblique.ttf 552 // DejaVuMonoSansOblique.ttf 553 // DejaVuSansMono-BoldOblique.ttf 554 // DejaVuSansMono-Oblique.ttf 555 // DejaVuSansMono-Bold.ttf 556 // 557 // # bad start coverage index 558 // GenBasBI.ttf 559 // GenBasI.ttf 560 // AndBasR.ttf 561 // GenBkBasI.ttf 562 // CharisSILR.ttf 563 // CharisSILBI.ttf 564 // CharisSILI.ttf 565 // CharisSILB.ttf 566 // DoulosSILR.ttf 567 // CharisSILBI.ttf 568 // GenBkBasB.ttf 569 // GenBkBasR.ttf 570 // GenBkBasBI.ttf 571 // GenBasB.ttf 572 // GenBasR.ttf 573 // 574 // # glyph range is overlapping 575 // KacstTitleL.ttf 576 // KacstDecorative.ttf 577 // KacstTitle.ttf 578 // KacstArt.ttf 579 // KacstPoster.ttf 580 // KacstQurn.ttf 581 // KacstDigital.ttf 582 // KacstBook.ttf 583 // KacstFarsi.ttf 584 585 bool ots_gsub_parse(OpenTypeFile *file, const uint8_t *data, size_t length) { 586 // Parsing gsub table requires |file->maxp->num_glyphs| 587 if (!file->maxp) { 588 return OTS_FAILURE(); 589 } 590 591 Buffer table(data, length); 592 593 OpenTypeGSUB *gsub = new OpenTypeGSUB; 594 file->gsub = gsub; 595 596 uint32_t version = 0; 597 uint16_t offset_script_list = 0; 598 uint16_t offset_feature_list = 0; 599 uint16_t offset_lookup_list = 0; 600 if (!table.ReadU32(&version) || 601 !table.ReadU16(&offset_script_list) || 602 !table.ReadU16(&offset_feature_list) || 603 !table.ReadU16(&offset_lookup_list)) { 604 return OTS_FAILURE(); 605 } 606 607 if (version != 0x00010000) { 608 OTS_WARNING("bad GSUB version"); 609 DROP_THIS_TABLE; 610 return true; 611 } 612 if ((offset_script_list < kGsubHeaderSize || 613 offset_script_list >= length) || 614 (offset_feature_list < kGsubHeaderSize || 615 offset_feature_list >= length) || 616 (offset_lookup_list < kGsubHeaderSize || 617 offset_lookup_list >= length)) { 618 OTS_WARNING("bad offset in GSUB header"); 619 DROP_THIS_TABLE; 620 return true; 621 } 622 623 if (!ParseLookupListTable(file, data + offset_lookup_list, 624 length - offset_lookup_list, 625 &kGsubLookupSubtableParser, 626 &gsub->num_lookups)) { 627 OTS_WARNING("faild to parse lookup list table"); 628 DROP_THIS_TABLE; 629 return true; 630 } 631 632 uint16_t num_features = 0; 633 if (!ParseFeatureListTable(data + offset_feature_list, 634 length - offset_feature_list, gsub->num_lookups, 635 &num_features)) { 636 OTS_WARNING("faild to parse feature list table"); 637 DROP_THIS_TABLE; 638 return true; 639 } 640 641 if (!ParseScriptListTable(data + offset_script_list, 642 length - offset_script_list, num_features)) { 643 OTS_WARNING("faild to parse script list table"); 644 DROP_THIS_TABLE; 645 return true; 646 } 647 648 gsub->data = data; 649 gsub->length = length; 650 return true; 651 } 652 653 bool ots_gsub_should_serialise(OpenTypeFile *file) { 654 const bool needed_tables_dropped = 655 (file->gdef && file->gdef->data == NULL) || 656 (file->gpos && file->gpos->data == NULL); 657 return file->gsub != NULL && file->gsub->data != NULL 658 && !needed_tables_dropped; 659 } 660 661 bool ots_gsub_serialise(OTSStream *out, OpenTypeFile *file) { 662 if (!out->Write(file->gsub->data, file->gsub->length)) { 663 return OTS_FAILURE(); 664 } 665 666 return true; 667 } 668 669 void ots_gsub_free(OpenTypeFile *file) { 670 delete file->gsub; 671 } 672 673 } // namespace ots 674 675