ttgpos.c (24035B)
1 /**************************************************************************** 2 * 3 * ttgpos.c 4 * 5 * Routines to parse and access the 'GPOS' table for simple kerning (body). 6 * 7 * Copyright (C) 2025 by 8 * David Turner, Robert Wilhelm, and Werner Lemberg. 9 * 10 * This file is part of the FreeType project, and may only be used, 11 * modified, and distributed under the terms of the FreeType project 12 * license, LICENSE.TXT. By continuing to use, modify, or distribute 13 * this file you indicate that you have read the license and 14 * understand and accept it fully. 15 * 16 */ 17 18 19 #include <freetype/freetype.h> 20 #include <freetype/tttables.h> 21 #include <freetype/tttags.h> 22 23 #include <freetype/internal/ftdebug.h> 24 #include <freetype/internal/ftstream.h> 25 26 #include "ttgpos.h" 27 28 29 #ifdef TT_CONFIG_OPTION_GPOS_KERNING 30 31 32 /************************************************************************** 33 * 34 * The macro FT_COMPONENT is used in trace mode. It is an implicit 35 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log 36 * messages during execution. 37 */ 38 #undef FT_COMPONENT 39 #define FT_COMPONENT ttgpos 40 41 42 /*********************************/ 43 /******** ********/ 44 /******** GPOS validation ********/ 45 /******** ********/ 46 /*********************************/ 47 48 static FT_Bool 49 tt_face_validate_coverage( FT_Byte* table, 50 FT_Byte* table_limit, 51 FT_UInt max_num_coverage_indices ) 52 { 53 FT_UInt format; 54 55 FT_Byte* p = table; 56 FT_Byte* limit; 57 58 FT_Long last_id = -1; 59 60 61 if ( table_limit < p + 4 ) 62 return FALSE; 63 64 format = FT_NEXT_USHORT( p ); 65 if ( format == 1 ) 66 { 67 FT_UInt glyphCount = FT_NEXT_USHORT( p ); 68 69 70 if ( glyphCount > max_num_coverage_indices ) 71 return FALSE; 72 73 limit = p + glyphCount * 2; 74 if ( table_limit < limit ) 75 return FALSE; 76 77 while ( p < limit ) 78 { 79 FT_UInt id = FT_NEXT_USHORT( p ); 80 81 82 if ( last_id >= id ) 83 return FALSE; 84 last_id = id; 85 } 86 } 87 else if ( format == 2 ) 88 { 89 FT_UInt rangeCount = FT_NEXT_USHORT( p ); 90 91 92 limit = p + rangeCount * 6; 93 if ( table_limit < limit ) 94 return FALSE; 95 96 while ( p < limit ) 97 { 98 FT_UInt startGlyphID = FT_NEXT_USHORT( p ); 99 FT_UInt endGlyphID = FT_NEXT_USHORT( p ); 100 FT_UInt startCoverageIndex = FT_NEXT_USHORT( p ); 101 102 103 if ( startGlyphID > endGlyphID ) 104 return FALSE; 105 106 if ( last_id >= startGlyphID ) 107 return FALSE; 108 last_id = endGlyphID; 109 110 /* XXX: Is this modulo 65536 arithmetic? */ 111 if ( startCoverageIndex + endGlyphID - startGlyphID >= 112 max_num_coverage_indices ) 113 return FALSE; 114 } 115 } 116 else 117 return FALSE; 118 119 return TRUE; 120 } 121 122 123 static FT_Bool 124 tt_face_validate_class_def( FT_Byte* table, 125 FT_Byte* table_limit, 126 FT_UInt num_classes ) 127 { 128 FT_UInt format; 129 130 FT_Byte* p = table; 131 FT_Byte* limit; 132 133 FT_UInt max_class_value = 0; 134 135 136 if ( table_limit < p + 2 ) 137 return FALSE; 138 139 format = FT_NEXT_USHORT( p ); 140 if ( format == 1 ) 141 { 142 FT_UInt glyphCount; 143 144 145 if ( table_limit < p + 4 ) 146 return FALSE; 147 148 p += 2; /* Skip `startGlyphID`. */ 149 150 glyphCount = FT_NEXT_USHORT( p ); 151 limit = p + glyphCount * 2; 152 if ( table_limit < limit ) 153 return FALSE; 154 155 while ( p < limit ) 156 { 157 FT_UInt class_value = FT_NEXT_USHORT( p ); 158 159 160 if ( class_value > max_class_value ) 161 max_class_value = class_value; 162 } 163 } 164 else if ( format == 2 ) 165 { 166 FT_UInt classRangeCount; 167 FT_Long last_id = -1; 168 169 170 if ( table_limit < p + 2 ) 171 return FALSE; 172 173 classRangeCount = FT_NEXT_USHORT( p ); 174 limit = p + classRangeCount * 6; 175 if ( table_limit < limit ) 176 return FALSE; 177 178 while ( p < limit ) 179 { 180 FT_UInt startGlyphID = FT_NEXT_USHORT( p ); 181 FT_UInt endGlyphID = FT_NEXT_USHORT( p ); 182 FT_UInt class_value = FT_NEXT_USHORT( p ); 183 184 185 if ( startGlyphID > endGlyphID ) 186 return FALSE; 187 188 if ( last_id >= startGlyphID ) 189 return FALSE; 190 last_id = endGlyphID; 191 192 if ( class_value > max_class_value ) 193 max_class_value = class_value; 194 } 195 } 196 else 197 return FALSE; 198 199 if ( max_class_value + 1 != num_classes ) 200 return FALSE; 201 202 return TRUE; 203 } 204 205 206 static FT_Bool 207 tt_face_validate_feature( FT_Byte* table, 208 FT_Byte* table_limit, 209 FT_UInt use_lookup_table_size, 210 FT_Byte* use_lookup_table ) 211 { 212 FT_UInt lookupIndexCount; 213 214 FT_Byte* p = table; 215 FT_Byte* limit; 216 217 218 if ( table_limit < p + 4 ) 219 return FALSE; 220 221 p += 2; /* Skip `featureParamsOffset`. */ 222 223 lookupIndexCount = FT_NEXT_USHORT( p ); 224 limit = p + lookupIndexCount * 2; 225 if ( table_limit < limit ) 226 return FALSE; 227 228 while ( p < limit ) 229 { 230 FT_UInt lookup_index = FT_NEXT_USHORT( p ); 231 232 233 if ( lookup_index >= use_lookup_table_size ) 234 return FALSE; 235 236 use_lookup_table[lookup_index] = TRUE; 237 } 238 239 return TRUE; 240 } 241 242 243 static FT_Bool 244 tt_face_validate_feature_table( FT_Byte* table, 245 FT_Byte* table_limit, 246 FT_UInt use_lookup_table_size, 247 FT_Byte* use_lookup_table ) 248 { 249 FT_UInt featureCount; 250 251 FT_Byte* p = table; 252 FT_Byte* limit; 253 254 255 if ( table_limit < p + 2 ) 256 return FALSE; 257 258 featureCount = FT_NEXT_USHORT( p ); 259 limit = p + featureCount * 6; 260 if ( table_limit < limit ) 261 return FALSE; 262 263 /* We completely ignore GPOS script information */ 264 /* and collect lookup tables of all 'kern' features. */ 265 while ( p < limit ) 266 { 267 FT_ULong featureTag = FT_NEXT_ULONG( p ); 268 FT_UInt featureOffset = FT_NEXT_USHORT( p ); 269 270 271 if ( featureTag == TTAG_kern ) 272 { 273 if ( !tt_face_validate_feature( table + featureOffset, 274 table_limit, 275 use_lookup_table_size, 276 use_lookup_table ) ) 277 return FALSE; 278 } 279 } 280 281 return TRUE; 282 } 283 284 285 static FT_Bool 286 tt_face_validate_pair_set( FT_Byte* table, 287 FT_Byte* table_limit ) 288 { 289 FT_UInt pairValueCount; 290 291 FT_Byte* p = table; 292 FT_Byte* limit; 293 294 FT_Long last_id = -1; 295 296 297 if ( table_limit < p + 2 ) 298 return FALSE; 299 300 /* For our purposes, the first value record only contains X advances */ 301 /* while the second one is empty; a `PairValue` record has thus a */ 302 /* size of four bytes. */ 303 pairValueCount = FT_NEXT_USHORT( p ); 304 limit = p + pairValueCount * 4; 305 if ( table_limit < limit ) 306 return FALSE; 307 308 /* We validate the order of `secondGlyph` so that binary search works. */ 309 while ( p < limit ) 310 { 311 FT_UInt id = FT_NEXT_USHORT( p ); 312 313 314 if ( last_id >= id ) 315 return FALSE; 316 317 last_id = id; 318 319 p += 2; /* Skip `valueRecord1`. */ 320 } 321 322 return TRUE; 323 } 324 325 326 static FT_Bool 327 tt_face_validate_pair_pos1( FT_Byte* table, 328 FT_Byte* table_limit, 329 FT_Bool* is_fitting ) 330 { 331 FT_Byte* coverage; 332 FT_UInt valueFormat1; 333 FT_UInt valueFormat2; 334 335 /* Subtable format is already checked. */ 336 FT_Byte* p = table + 2; 337 FT_Byte* limit; 338 339 340 /* The six bytes for the coverage table offset */ 341 /* and the value formats are already checked. */ 342 coverage = table + FT_NEXT_USHORT( p ); 343 344 /* For the limited purpose of accessing the simplest type of kerning */ 345 /* (similar to what FreeType's 'kern' table handling provides) we */ 346 /* only consider tables that contains X advance values for the first */ 347 /* glyph and no data for the second glyph. */ 348 valueFormat1 = FT_NEXT_USHORT( p ); 349 valueFormat2 = FT_NEXT_USHORT( p ); 350 if ( valueFormat1 == 0x4 && valueFormat2 == 0 ) 351 { 352 FT_UInt pairSetCount; 353 354 355 if ( table_limit < p + 2 ) 356 return FALSE; 357 358 pairSetCount = FT_NEXT_USHORT( p ); 359 limit = p + pairSetCount * 2; 360 if ( table_limit < limit ) 361 return FALSE; 362 363 if ( !tt_face_validate_coverage( coverage, 364 table_limit, 365 pairSetCount ) ) 366 return FALSE; 367 368 while ( p < limit ) 369 { 370 FT_Byte* pair_set = table + FT_NEXT_USHORT( p ); 371 372 373 if ( !tt_face_validate_pair_set( pair_set, table_limit ) ) 374 return FALSE; 375 } 376 377 *is_fitting = TRUE; 378 } 379 380 return TRUE; 381 } 382 383 384 static FT_Bool 385 tt_face_validate_pair_pos2( FT_Byte* table, 386 FT_Byte* table_limit, 387 FT_Bool* is_fitting ) 388 { 389 FT_Byte* coverage; 390 FT_UInt valueFormat1; 391 FT_UInt valueFormat2; 392 393 /* Subtable format is already checked. */ 394 FT_Byte* p = table + 2; 395 FT_Byte* limit; 396 397 398 /* The six bytes for the coverage table offset */ 399 /* and the value formats are already checked. */ 400 coverage = table + FT_NEXT_USHORT( p ); 401 402 valueFormat1 = FT_NEXT_USHORT( p ); 403 valueFormat2 = FT_NEXT_USHORT( p ); 404 if ( valueFormat1 == 0x4 && valueFormat2 == 0 ) 405 { 406 FT_Byte* class_def1; 407 FT_Byte* class_def2; 408 FT_UInt class1Count; 409 FT_UInt class2Count; 410 411 412 /* The number of coverage indices is not relevant here. */ 413 if ( !tt_face_validate_coverage( coverage, table_limit, FT_UINT_MAX ) ) 414 return FALSE; 415 416 if ( table_limit < p + 8 ) 417 return FALSE; 418 419 class_def1 = table + FT_NEXT_USHORT( p ); 420 class_def2 = table + FT_NEXT_USHORT( p ); 421 class1Count = FT_NEXT_USHORT( p ); 422 class2Count = FT_NEXT_USHORT( p ); 423 424 if ( !tt_face_validate_class_def( class_def1, 425 table_limit, 426 class1Count ) ) 427 return FALSE; 428 if ( !tt_face_validate_class_def( class_def2, 429 table_limit, 430 class2Count ) ) 431 return FALSE; 432 433 /* For our purposes, the first value record only contains */ 434 /* X advances while the second one is empty. */ 435 limit = p + class1Count * class2Count * 2; 436 if ( table_limit < limit ) 437 return FALSE; 438 439 *is_fitting = TRUE; 440 } 441 442 return TRUE; 443 } 444 445 446 /* The return value is the number of fitting subtables. */ 447 static FT_UInt 448 tt_face_validate_lookup_table( FT_Byte* table, 449 FT_Byte* table_limit ) 450 { 451 FT_UInt lookupType; 452 FT_UInt real_lookupType = 0; 453 FT_UInt subtableCount; 454 455 FT_Byte* p = table; 456 FT_Byte* limit; 457 458 FT_UInt num_fitting_subtables = 0; 459 460 461 if ( table_limit < p + 6 ) 462 return 0; 463 464 lookupType = FT_NEXT_USHORT( p ); 465 466 p += 2; /* Skip `lookupFlag`. */ 467 468 subtableCount = FT_NEXT_USHORT( p ); 469 limit = p + subtableCount * 2; 470 if ( table_limit < limit ) 471 return 0; 472 473 while ( p < limit ) 474 { 475 FT_Byte* subtable = table + FT_NEXT_USHORT( p ); 476 FT_UInt format; 477 478 FT_Bool is_fitting = FALSE; 479 480 481 if ( lookupType == 9 ) 482 { 483 /* Positioning extension. */ 484 FT_Byte* q = subtable; 485 486 487 if ( table_limit < q + 8 ) 488 return 0; 489 490 if ( FT_NEXT_USHORT( q ) != 1 ) /* format */ 491 return 0; 492 493 if ( real_lookupType == 0 ) 494 real_lookupType = FT_NEXT_USHORT( q ); 495 else if ( real_lookupType != FT_NEXT_USHORT( q ) ) 496 return 0; 497 498 subtable += FT_PEEK_ULONG( q ); 499 } 500 else 501 real_lookupType = lookupType; 502 503 /* Ensure the first eight bytes of the subtable formats. */ 504 if ( table_limit < subtable + 8 ) 505 return 0; 506 507 format = FT_PEEK_USHORT( subtable ); 508 509 if ( real_lookupType == 2 ) 510 { 511 if ( format == 1 ) 512 { 513 if ( !tt_face_validate_pair_pos1( subtable, 514 table_limit, 515 &is_fitting ) ) 516 return 0; 517 } 518 else if ( format == 2 ) 519 { 520 if ( !tt_face_validate_pair_pos2( subtable, 521 table_limit, 522 &is_fitting ) ) 523 return 0; 524 } 525 else 526 return 0; 527 } 528 else 529 return 0; 530 531 if ( is_fitting ) 532 num_fitting_subtables++; 533 } 534 535 return num_fitting_subtables; 536 } 537 538 539 static void 540 tt_face_get_subtable_offsets( FT_Byte* table, 541 FT_Byte* gpos, 542 FT_UInt32* gpos_lookups_kerning, 543 FT_UInt* idx ) 544 { 545 FT_UInt lookupType; 546 FT_UInt subtableCount; 547 548 FT_Byte* p = table; 549 FT_Byte* limit; 550 551 552 lookupType = FT_NEXT_USHORT( p ); 553 554 p += 2; 555 556 subtableCount = FT_NEXT_USHORT( p ); 557 limit = p + subtableCount * 2; 558 while ( p < limit ) 559 { 560 FT_Byte* subtable = table + FT_NEXT_USHORT( p ); 561 FT_UInt valueFormat1; 562 FT_UInt valueFormat2; 563 564 565 if ( lookupType == 9 ) 566 subtable += FT_PEEK_ULONG( subtable + 4 ); 567 568 /* Table offsets for `valueFormat[12]` values */ 569 /* are identical for both subtable formats. */ 570 valueFormat1 = FT_PEEK_USHORT( subtable + 4 ); 571 valueFormat2 = FT_PEEK_USHORT( subtable + 6 ); 572 if ( valueFormat1 == 0x4 && valueFormat2 == 0 ) 573 { 574 /* We store offsets relative to the start of the GPOS table. */ 575 gpos_lookups_kerning[(*idx)++] = (FT_UInt32)( subtable - gpos ); 576 } 577 } 578 } 579 580 581 FT_LOCAL_DEF( FT_Error ) 582 tt_face_load_gpos( TT_Face face, 583 FT_Stream stream ) 584 { 585 FT_Error error; 586 FT_Memory memory = face->root.memory; 587 588 FT_ULong gpos_length; 589 FT_Byte* gpos; 590 FT_Byte* gpos_limit; 591 592 FT_UInt32* gpos_lookups_kerning; 593 594 FT_UInt featureListOffset; 595 596 FT_UInt lookupListOffset; 597 FT_Byte* lookup_list; 598 FT_UInt lookupCount; 599 600 FT_UInt i; 601 602 FT_Byte* use_lookup_table = NULL; 603 FT_UInt num_fitting_subtables; 604 605 FT_Byte* p; 606 FT_Byte* limit; 607 608 609 face->gpos_table = NULL; 610 face->gpos_lookups_kerning = NULL; 611 face->num_gpos_lookups_kerning = 0; 612 613 gpos = NULL; 614 gpos_lookups_kerning = NULL; 615 616 error = face->goto_table( face, TTAG_GPOS, stream, &gpos_length ); 617 if ( error ) 618 goto Fail; 619 620 if ( FT_FRAME_EXTRACT( gpos_length, gpos ) ) 621 goto Fail; 622 623 if ( gpos_length < 10 ) 624 goto Fail; 625 626 gpos_limit = gpos + gpos_length; 627 628 /* We first need the number of GPOS lookups. */ 629 lookupListOffset = FT_PEEK_USHORT( gpos + 8 ); 630 631 lookup_list = gpos + lookupListOffset; 632 p = lookup_list; 633 if ( gpos_limit < p + 2 ) 634 goto Fail; 635 636 lookupCount = FT_NEXT_USHORT( p ); 637 limit = p + lookupCount * 2; 638 if ( gpos_limit < limit ) 639 goto Fail; 640 641 /* Allocate an auxiliary array for Boolean values that */ 642 /* gets filled while walking over all 'kern' features. */ 643 if ( FT_NEW_ARRAY( use_lookup_table, lookupCount ) ) 644 goto Fail; 645 646 featureListOffset = FT_PEEK_USHORT( gpos + 6 ); 647 648 if ( !tt_face_validate_feature_table( gpos + featureListOffset, 649 gpos_limit, 650 lookupCount, 651 use_lookup_table ) ) 652 goto Fail; 653 654 /* Now walk over all lookup tables and get the */ 655 /* number of fitting subtables. */ 656 num_fitting_subtables = 0; 657 for ( i = 0; i < lookupCount; i++ ) 658 { 659 FT_UInt lookupOffset; 660 661 662 if ( !use_lookup_table[i] ) 663 continue; 664 665 lookupOffset = FT_PEEK_USHORT( p + i * 2 ); 666 667 num_fitting_subtables += 668 tt_face_validate_lookup_table( lookup_list + lookupOffset, 669 gpos_limit ); 670 671 } 672 673 /* Loop again over all lookup tables and */ 674 /* collect offsets to those subtables. */ 675 if ( num_fitting_subtables ) 676 { 677 FT_UInt idx; 678 679 680 if ( FT_QNEW_ARRAY( gpos_lookups_kerning, num_fitting_subtables ) ) 681 goto Fail; 682 683 idx = 0; 684 for ( i = 0; i < lookupCount; i++ ) 685 { 686 FT_UInt lookupOffset; 687 688 689 if ( !use_lookup_table[i] ) 690 continue; 691 692 lookupOffset = FT_PEEK_USHORT( p + i * 2 ); 693 694 tt_face_get_subtable_offsets( lookup_list + lookupOffset, 695 gpos, 696 gpos_lookups_kerning, 697 &idx ); 698 } 699 } 700 701 FT_FREE( use_lookup_table ); 702 use_lookup_table = NULL; 703 704 face->gpos_table = gpos; 705 face->gpos_lookups_kerning = gpos_lookups_kerning; 706 face->num_gpos_lookups_kerning = num_fitting_subtables; 707 708 Exit: 709 return error; 710 711 Fail: 712 FT_FREE( gpos ); 713 FT_FREE( gpos_lookups_kerning ); 714 FT_FREE( use_lookup_table ); 715 716 /* If we don't have an explicit error code, set it to a generic value. */ 717 if ( !error ) 718 error = FT_THROW( Invalid_Table ); 719 720 goto Exit; 721 } 722 723 724 FT_LOCAL_DEF( void ) 725 tt_face_done_gpos( TT_Face face ) 726 { 727 FT_Stream stream = face->root.stream; 728 FT_Memory memory = face->root.memory; 729 730 731 FT_FRAME_RELEASE( face->gpos_table ); 732 FT_FREE( face->gpos_lookups_kerning ); 733 } 734 735 736 /*********************************/ 737 /******** ********/ 738 /******** GPOS access ********/ 739 /******** ********/ 740 /*********************************/ 741 742 743 static FT_Long 744 tt_face_get_coverage_index( FT_Byte* table, 745 FT_UInt glyph_index ) 746 { 747 FT_Byte* p = table; 748 FT_UInt format = FT_NEXT_USHORT( p ); 749 FT_UInt count = FT_NEXT_USHORT( p ); 750 751 FT_UInt min, max; 752 753 754 min = 0; 755 max = count; 756 757 if ( format == 1 ) 758 { 759 while ( min < max ) 760 { 761 FT_UInt mid = min + ( max - min ) / 2; 762 FT_UInt mid_index = FT_PEEK_USHORT( p + mid * 2 ); 763 764 765 if ( glyph_index > mid_index ) 766 min = mid + 1; 767 else if ( glyph_index < mid_index ) 768 max = mid; 769 else 770 return mid; 771 } 772 } 773 else 774 { 775 while ( min < max ) 776 { 777 FT_UInt mid = min + ( max - min ) / 2; 778 FT_UInt startGlyphID = FT_PEEK_USHORT( p + mid * 6 ); 779 FT_UInt endGlyphID = FT_PEEK_USHORT( p + mid * 6 + 2 ); 780 781 782 if ( glyph_index > endGlyphID ) 783 min = mid + 1; 784 else if ( glyph_index < startGlyphID ) 785 max = mid; 786 else 787 { 788 FT_UInt startCoverageIndex = FT_PEEK_USHORT( p + mid * 6 + 4 ); 789 790 791 return startCoverageIndex + glyph_index - startGlyphID; 792 } 793 } 794 } 795 796 return -1; 797 } 798 799 800 static FT_UInt 801 tt_face_get_class( FT_Byte* table, 802 FT_UInt glyph_index ) 803 { 804 FT_Byte* p = table; 805 FT_UInt format = FT_NEXT_USHORT( p ); 806 807 808 if ( format == 1 ) 809 { 810 FT_UInt startGlyphID = FT_NEXT_USHORT( p ); 811 FT_UInt glyphCount = FT_NEXT_USHORT( p ); 812 813 814 /* XXX: Is this modulo 65536 arithmetic? */ 815 if ( startGlyphID <= glyph_index && 816 startGlyphID + glyphCount >= glyph_index ) 817 return FT_PEEK_USHORT( p + ( glyph_index - startGlyphID ) * 2 ); 818 } 819 else 820 { 821 FT_UInt count = FT_NEXT_USHORT( p ); 822 823 FT_UInt min, max; 824 825 826 min = 0; 827 max = count; 828 829 while ( min < max ) 830 { 831 FT_UInt mid = min + ( max - min ) / 2; 832 FT_UInt startGlyphID = FT_PEEK_USHORT( p + mid * 6 ); 833 FT_UInt endGlyphID = FT_PEEK_USHORT( p + mid * 6 + 2 ); 834 835 836 if ( glyph_index > endGlyphID ) 837 min = mid + 1; 838 else if ( glyph_index < startGlyphID ) 839 max = mid; 840 else 841 return FT_PEEK_USHORT( p + mid * 6 + 4 ); 842 } 843 } 844 845 return 0; 846 } 847 848 849 static FT_Bool 850 tt_face_get_pair_pos1_kerning( FT_Byte* table, 851 FT_UInt first_glyph, 852 FT_UInt second_glyph, 853 FT_Int* kerning ) 854 { 855 FT_Byte* coverage = table + FT_PEEK_USHORT( table + 2 ); 856 FT_Long coverage_index = tt_face_get_coverage_index( coverage, 857 first_glyph ); 858 859 FT_UInt pair_set_offset; 860 FT_Byte* p; 861 FT_UInt count; 862 863 FT_UInt min, max; 864 865 866 if ( coverage_index < 0 ) 867 return FALSE; 868 869 pair_set_offset = FT_PEEK_USHORT( table + 10 + coverage_index * 2 ); 870 p = table + pair_set_offset; 871 count = FT_NEXT_USHORT( p ); 872 873 min = 0; 874 max = count; 875 876 while ( min < max ) 877 { 878 FT_UInt mid = min + ( max - min ) / 2; 879 FT_UInt mid_index = FT_PEEK_USHORT( p + mid * 4 ); 880 881 882 if ( second_glyph > mid_index ) 883 min = max + 1; 884 else if ( second_glyph < mid_index ) 885 max = mid; 886 else 887 { 888 *kerning = FT_PEEK_SHORT( p + mid * 4 + 2 ); 889 890 return TRUE; 891 } 892 } 893 894 return FALSE; 895 } 896 897 898 static FT_Bool 899 tt_face_get_pair_pos2_kerning( FT_Byte* table, 900 FT_UInt first_glyph, 901 FT_UInt second_glyph, 902 FT_Int* kerning ) 903 { 904 FT_Byte* coverage = table + FT_PEEK_USHORT( table + 2 ); 905 FT_Long coverage_index = tt_face_get_coverage_index( coverage, 906 first_glyph ); 907 908 FT_Byte* class_def1; 909 FT_Byte* class_def2; 910 FT_UInt first_class; 911 FT_UInt second_class; 912 FT_UInt class2Count; 913 914 915 if ( coverage_index < 0 ) 916 return FALSE; 917 918 class_def1 = table + FT_PEEK_USHORT( table + 8 ); 919 class_def2 = table + FT_PEEK_USHORT( table + 10 ); 920 921 class2Count = FT_PEEK_USHORT( table + 14 ); 922 923 first_class = tt_face_get_class( class_def1, first_glyph ); 924 second_class = tt_face_get_class( class_def2, second_glyph ); 925 926 *kerning = 927 FT_PEEK_SHORT( table + 16 + 928 ( first_class * class2Count + second_class ) * 2 ); 929 930 return TRUE; 931 } 932 933 934 FT_LOCAL_DEF( FT_Int ) 935 tt_face_get_gpos_kerning( TT_Face face, 936 FT_UInt first_glyph, 937 FT_UInt second_glyph ) 938 { 939 FT_Int kerning = 0; 940 941 FT_UInt i; 942 943 944 /* We only have `PairPos` subtables. */ 945 for ( i = 0; i < face->num_gpos_lookups_kerning; i++ ) 946 { 947 FT_Byte* subtable = face->gpos_table + face->gpos_lookups_kerning[i]; 948 FT_Byte* p = subtable; 949 950 FT_UInt format = FT_NEXT_USHORT( p ); 951 952 953 if ( format == 1 ) 954 { 955 if ( tt_face_get_pair_pos1_kerning( subtable, 956 first_glyph, 957 second_glyph, 958 &kerning ) ) 959 break; 960 } 961 else 962 { 963 if ( tt_face_get_pair_pos2_kerning( subtable, 964 first_glyph, 965 second_glyph, 966 &kerning ) ) 967 break; 968 } 969 } 970 971 return kerning; 972 } 973 974 #else /* !TT_CONFIG_OPTION_GPOS_KERNING */ 975 976 /* ANSI C doesn't like empty source files */ 977 typedef int tt_gpos_dummy_; 978 979 #endif /* !TT_CONFIG_OPTION_GPOS_KERNING */ 980 981 982 /* END */