afgsub.c (15870B)
1 /**************************************************************************** 2 * 3 * afgsub.c 4 * 5 * Auto-fitter routines to parse the GSUB table (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 #ifdef FT_CONFIG_OPTION_USE_HARFBUZZ 19 20 21 #include <freetype/freetype.h> 22 #include <freetype/tttables.h> 23 #include <freetype/tttags.h> 24 25 #include <freetype/internal/ftstream.h> 26 27 #include "afglobal.h" 28 #include "afgsub.h" 29 #include "aftypes.h" 30 31 32 /*********************************/ 33 /******** ********/ 34 /******** GSUB validation ********/ 35 /******** ********/ 36 /*********************************/ 37 38 39 static FT_Bool 40 af_validate_coverage( FT_Byte* table, 41 FT_Byte* table_limit, 42 FT_UInt *num_glyphs ) 43 { 44 FT_UInt format; 45 46 FT_Byte* p = table; 47 FT_UInt count = 0; 48 49 50 if ( table_limit < p + 4 ) 51 return FALSE; 52 53 format = FT_NEXT_USHORT( p ); 54 if ( format == 1 ) 55 { 56 FT_UInt glyphCount = FT_NEXT_USHORT( p ); 57 58 59 /* We don't validate glyph IDs. */ 60 if ( table_limit < p + glyphCount * 2 ) 61 return FALSE; 62 63 count += glyphCount; 64 } 65 else if ( format == 2 ) 66 { 67 FT_UInt rangeCount = FT_NEXT_USHORT( p ); 68 FT_Byte* limit = p + rangeCount * 6; 69 70 71 if ( table_limit < limit ) 72 return FALSE; 73 74 while ( p < limit ) 75 { 76 FT_UInt startGlyphID = FT_NEXT_USHORT( p ); 77 FT_UInt endGlyphID = FT_NEXT_USHORT( p ); 78 79 80 if ( startGlyphID > endGlyphID ) 81 return FALSE; 82 83 count += endGlyphID - startGlyphID + 1; 84 85 /* We don't validate coverage indices. */ 86 p += 2; 87 } 88 } 89 else 90 return FALSE; 91 92 if ( num_glyphs ) 93 *num_glyphs = count; 94 95 return TRUE; 96 } 97 98 99 static FT_Bool 100 af_validate_single_subst1( FT_Byte* table, 101 FT_Byte* table_limit ) 102 { 103 FT_Byte* coverage; 104 105 106 /* Subtable format is already checked. */ 107 108 /* The four bytes for the coverage table offset */ 109 /* and the glyph ID delta are already checked. */ 110 coverage = table + FT_PEEK_USHORT( table + 2 ); 111 if ( !af_validate_coverage( coverage, table_limit, NULL ) ) 112 return FALSE; 113 114 /* We don't validate glyph IDs. */ 115 116 return TRUE; 117 } 118 119 120 static FT_Bool 121 af_validate_single_subst2( FT_Byte* table, 122 FT_Byte* table_limit ) 123 { 124 FT_Byte* coverage; 125 FT_UInt glyphCount; 126 FT_UInt num_glyphs; 127 128 /* Subtable format is already checked. */ 129 FT_Byte* p = table + 2; 130 131 132 /* The four bytes for the coverage table offset */ 133 /* and `glyphCount` are already checked. */ 134 coverage = table + FT_NEXT_USHORT( p ); 135 if ( !af_validate_coverage( coverage, table_limit, &num_glyphs ) ) 136 return FALSE; 137 138 glyphCount = FT_NEXT_USHORT( p ); 139 /* We don't validate glyph IDs. */ 140 if ( table_limit < p + glyphCount * 2 ) 141 return FALSE; 142 143 if ( glyphCount != num_glyphs ) 144 return FALSE; 145 146 return TRUE; 147 } 148 149 150 static FT_Bool 151 af_validate_alternate( FT_Byte* table, 152 FT_Byte* table_limit ) 153 { 154 FT_Byte* coverage; 155 FT_UInt alternateSetCount; 156 FT_UInt num_glyphs; 157 158 /* Subtable format is already checked. */ 159 FT_Byte* p = table + 2; 160 FT_Byte* limit; 161 162 163 /* The four bytes for the coverage table offset */ 164 /* and `alternateSetCount` are already checked. */ 165 coverage = table + FT_NEXT_USHORT( p ); 166 if ( !af_validate_coverage( coverage, table_limit, &num_glyphs ) ) 167 return FALSE; 168 169 alternateSetCount = FT_NEXT_USHORT( p ); 170 limit = p + alternateSetCount * 2; 171 if ( table_limit < limit ) 172 return FALSE; 173 174 if ( alternateSetCount != num_glyphs ) 175 return FALSE; 176 177 while ( p < limit ) 178 { 179 FT_Byte* alternate_set; 180 FT_UInt glyphCount; 181 182 183 alternate_set = table + FT_NEXT_USHORT( p ); 184 if ( table_limit < alternate_set + 2 ) 185 return FALSE; 186 187 glyphCount = FT_PEEK_USHORT( alternate_set ); 188 /* We don't validate glyph IDs. */ 189 if ( table_limit < alternate_set + 2 + glyphCount * 2 ) 190 return FALSE; 191 } 192 193 return TRUE; 194 } 195 196 197 /* Validate 'SingleSubst' and 'AlternateSubst' lookup tables. */ 198 static FT_Bool 199 af_validate_lookup_table( FT_Byte* table, 200 FT_Byte* table_limit ) 201 { 202 FT_UInt lookupType; 203 FT_UInt real_lookupType = 0; 204 FT_UInt subtableCount; 205 206 FT_Byte* p = table; 207 FT_Byte* limit; 208 209 210 if ( table_limit < p + 6 ) 211 return FALSE; 212 213 lookupType = FT_NEXT_USHORT( p ); 214 215 p += 2; /* Skip `lookupFlag`. */ 216 217 subtableCount = FT_NEXT_USHORT( p ); 218 limit = p + subtableCount * 2; 219 if ( table_limit < limit ) 220 return FALSE; 221 222 while ( p < limit ) 223 { 224 FT_Byte* subtable = table + FT_NEXT_USHORT( p ); 225 FT_UInt format; 226 227 228 if ( lookupType == 7 ) 229 { 230 /* Substitution extension. */ 231 FT_Byte* q = subtable; 232 233 234 if ( table_limit < q + 8 ) 235 return FALSE; 236 237 if ( FT_NEXT_USHORT( q ) != 1 ) /* format */ 238 return FALSE; 239 240 if ( real_lookupType == 0 ) 241 real_lookupType = FT_NEXT_USHORT( q ); 242 else if ( real_lookupType != FT_NEXT_USHORT( q ) ) 243 return FALSE; 244 245 subtable += FT_PEEK_ULONG( q ); 246 } 247 else 248 real_lookupType = lookupType; 249 250 /* Ensure the first six bytes of all subtable formats. */ 251 if ( table_limit < subtable + 6 ) 252 return FALSE; 253 254 format = FT_PEEK_USHORT( subtable ); 255 256 if ( real_lookupType == 1 ) 257 { 258 if ( format == 1 ) 259 { 260 if ( !af_validate_single_subst1( subtable, table_limit ) ) 261 return FALSE; 262 } 263 else if ( format == 2 ) 264 { 265 if ( !af_validate_single_subst2( subtable, table_limit ) ) 266 return FALSE; 267 } 268 else 269 return FALSE; 270 } 271 else if ( real_lookupType == 3 ) 272 { 273 if ( format == 1 ) 274 { 275 if ( !af_validate_alternate( subtable, table_limit ) ) 276 return FALSE; 277 } 278 else 279 return FALSE; 280 } 281 else 282 return FALSE; 283 } 284 285 return TRUE; 286 } 287 288 289 FT_LOCAL_DEF( void ) 290 af_parse_gsub( AF_FaceGlobals globals ) 291 { 292 FT_Error error = FT_Err_Ok; 293 294 FT_Face face = globals->face; 295 FT_Memory memory = face->memory; 296 297 FT_ULong gsub_length; 298 FT_Byte* gsub; 299 FT_Byte* gsub_limit; 300 301 FT_UInt32* gsub_lookups_single_alternate; 302 303 FT_UInt lookupListOffset; 304 FT_Byte* lookup_list; 305 FT_UInt lookupCount; 306 307 FT_UInt idx; 308 309 FT_Byte* p; 310 FT_Byte* limit; 311 312 313 globals->gsub = NULL; 314 globals->gsub_lookups_single_alternate = NULL; 315 316 /* No error if we can't load or parse GSUB data. */ 317 318 gsub = NULL; 319 gsub_lookups_single_alternate = NULL; 320 321 gsub_length = 0; 322 if ( FT_Load_Sfnt_Table( face, TTAG_GSUB, 0, NULL, &gsub_length ) ) 323 goto Fail; 324 325 if ( FT_QALLOC( gsub, gsub_length ) ) 326 goto Fail; 327 328 if ( FT_Load_Sfnt_Table( face, TTAG_GSUB, 0, gsub, &gsub_length ) ) 329 goto Fail; 330 331 if ( gsub_length < 10 ) 332 goto Fail; 333 334 lookupListOffset = FT_PEEK_USHORT( gsub + 8 ); 335 if ( gsub_length < lookupListOffset + 2 ) 336 goto Fail; 337 338 lookupCount = FT_PEEK_USHORT( gsub + lookupListOffset ); 339 if ( gsub_length < lookupListOffset + 2 + lookupCount * 2 ) 340 goto Fail; 341 342 if ( FT_NEW_ARRAY( gsub_lookups_single_alternate, lookupCount ) ) 343 goto Fail; 344 345 gsub_limit = gsub + gsub_length; 346 lookup_list = gsub + lookupListOffset; 347 p = lookup_list + 2; 348 limit = p + lookupCount * 2; 349 idx = 0; 350 while ( p < limit ) 351 { 352 FT_UInt lookupOffset = FT_NEXT_USHORT( p ); 353 354 355 if ( af_validate_lookup_table( lookup_list + lookupOffset, 356 gsub_limit ) ) 357 { 358 /* We store offsets relative to the start of the GSUB table. */ 359 gsub_lookups_single_alternate[idx] = lookupListOffset + lookupOffset; 360 } 361 362 idx++; 363 } 364 365 globals->gsub = gsub; 366 globals->gsub_lookups_single_alternate = gsub_lookups_single_alternate; 367 368 return; 369 370 Fail: 371 FT_FREE( gsub ); 372 FT_FREE( gsub_lookups_single_alternate ); 373 } 374 375 376 /*********************************/ 377 /******** ********/ 378 /******** GSUB access ********/ 379 /******** ********/ 380 /*********************************/ 381 382 383 static FT_UInt 384 af_coverage_format( FT_Byte* coverage ) 385 { 386 return FT_PEEK_USHORT( coverage ); 387 } 388 389 390 static FT_Byte* 391 af_coverage_start( FT_Byte* coverage ) 392 { 393 return coverage + 4; 394 } 395 396 397 static FT_Byte* 398 af_coverage_limit( FT_Byte* coverage ) 399 { 400 if ( af_coverage_format( coverage ) == 1 ) 401 { 402 FT_UInt glyphCount = FT_PEEK_USHORT( coverage + 2 ); 403 404 405 return af_coverage_start( coverage ) + glyphCount * 2; 406 } 407 else 408 { 409 FT_UInt rangeCount = FT_PEEK_USHORT( coverage + 2 ); 410 411 412 return af_coverage_start( coverage ) + rangeCount * 6; 413 } 414 } 415 416 417 typedef struct AF_CoverageIteratorRec_* AF_CoverageIterator; 418 419 typedef struct AF_CoverageIteratorRec_ 420 { 421 FT_UInt format; 422 423 FT_Byte* p; 424 FT_Byte* limit; 425 426 FT_UInt16 glyph; 427 FT_UInt16 glyph_limit; 428 429 } AF_CoverageIteratorRec; 430 431 432 static FT_Bool 433 af_coverage_iterator( AF_CoverageIterator iter, 434 FT_UInt16* glyph ) 435 { 436 if ( iter->p >= iter->limit ) 437 return FALSE; 438 439 if ( iter->format == 1 ) 440 *glyph = FT_NEXT_USHORT( iter->p ); 441 else 442 { 443 if ( iter->glyph > iter->glyph_limit ) 444 { 445 iter->glyph = FT_NEXT_USHORT( iter->p ); 446 iter->glyph_limit = FT_NEXT_USHORT( iter->p ); 447 448 iter->p += 2; 449 } 450 451 *glyph = iter->glyph++; 452 } 453 454 return TRUE; 455 } 456 457 458 static AF_CoverageIteratorRec 459 af_coverage_iterator_init( FT_Byte* coverage ) 460 { 461 AF_CoverageIteratorRec iterator; 462 463 464 iterator.format = af_coverage_format( coverage ); 465 iterator.p = af_coverage_start( coverage ); 466 iterator.limit = af_coverage_limit( coverage ); 467 iterator.glyph = 1; 468 iterator.glyph_limit = 0; 469 470 return iterator; 471 } 472 473 474 /* 475 Because we merge all single and alternate substitution mappings into 476 one, large hash, we need the possibility to have multiple glyphs as 477 values. We utilize that we have 32bit integers but only 16bit glyph 478 indices, using the following scheme. 479 480 If glyph G maps to a single substitute S, the entry in the map is 481 482 G -> S 483 484 If glyph G maps to multiple substitutes S1, S2, ..., Sn, we do 485 486 G -> S1 + ((n - 1) << 16) 487 G + (1 << 16) -> S2 488 G + (2 << 16) -> S3 489 ... 490 G + ((n - 1) << 16) -> Sn 491 */ 492 static FT_Error 493 af_hash_insert( FT_UInt16 glyph, 494 FT_UInt16 substitute, 495 FT_Hash map, 496 FT_Memory memory ) 497 { 498 FT_Error error; 499 500 size_t* value = ft_hash_num_lookup( glyph, map ); 501 502 503 if ( !value ) 504 { 505 error = ft_hash_num_insert( glyph, substitute, map, memory ); 506 if ( error ) 507 return error; 508 } 509 else 510 { 511 /* Get number of substitutes, increased by one... */ 512 FT_UInt mask = ( (FT_UInt)*value & 0xFFFF0000U ) + 0x10000U; 513 514 515 /* ... which becomes the new key mask. */ 516 error = ft_hash_num_insert( (FT_Int)( glyph | mask ), 517 substitute, 518 map, 519 memory ); 520 if ( error ) 521 return error; 522 523 /* Update number of substitutes. */ 524 *value += 0x10000U; 525 } 526 527 return FT_Err_Ok; 528 } 529 530 531 static FT_Error 532 af_map_single_subst1( FT_Hash map, 533 FT_Byte* table, 534 FT_Memory memory ) 535 { 536 FT_Error error; 537 538 FT_Byte* coverage = table + FT_PEEK_USHORT( table + 2 ); 539 FT_UInt deltaGlyphID = FT_PEEK_USHORT( table + 4 ); 540 541 AF_CoverageIteratorRec iterator = af_coverage_iterator_init( coverage ); 542 543 FT_UInt16 glyph; 544 545 546 while ( af_coverage_iterator( &iterator, &glyph ) ) 547 { 548 /* `deltaGlyphID` requires modulo 65536 arithmetic. */ 549 FT_UInt16 subst = (FT_UInt16)( ( glyph + deltaGlyphID ) % 0x10000U ); 550 551 552 error = af_hash_insert( glyph, subst, map, memory ); 553 if ( error ) 554 return error; 555 } 556 557 return FT_Err_Ok; 558 } 559 560 561 static FT_Error 562 af_map_single_subst2( FT_Hash map, 563 FT_Byte* table, 564 FT_Memory memory ) 565 { 566 FT_Error error; 567 568 FT_Byte* coverage = table + FT_PEEK_USHORT( table + 2 ); 569 570 AF_CoverageIteratorRec iterator = af_coverage_iterator_init( coverage ); 571 572 FT_UInt16 glyph; 573 FT_Byte* p = table + 6; 574 575 576 while ( af_coverage_iterator( &iterator, &glyph ) ) 577 { 578 FT_UInt16 subst = FT_NEXT_USHORT( p ); 579 580 581 error = af_hash_insert( glyph, subst, map, memory ); 582 if ( error ) 583 return error; 584 } 585 586 return FT_Err_Ok; 587 } 588 589 590 static FT_Error 591 af_map_alternate( FT_Hash map, 592 FT_Byte* table, 593 FT_Memory memory ) 594 { 595 FT_Error error; 596 597 FT_Byte* coverage = table + FT_PEEK_USHORT( table + 2 ); 598 599 AF_CoverageIteratorRec iterator = af_coverage_iterator_init( coverage ); 600 601 FT_UInt16 glyph; 602 FT_Byte* p = table + 6; 603 604 605 while ( af_coverage_iterator( &iterator, &glyph ) ) 606 { 607 FT_Byte* alternate_set = table + FT_NEXT_USHORT( p ); 608 609 FT_Byte* q = alternate_set; 610 FT_UInt glyphCount = FT_NEXT_USHORT( q ); 611 612 FT_UInt i; 613 614 615 for ( i = 0; i < glyphCount; i++ ) 616 { 617 FT_UInt16 subst = FT_NEXT_USHORT( q ); 618 619 620 error = af_hash_insert( glyph, subst, map, memory ); 621 if ( error ) 622 return error; 623 } 624 } 625 626 return FT_Err_Ok; 627 } 628 629 630 /* Map 'SingleSubst' and 'AlternateSubst' lookup tables. */ 631 FT_LOCAL_DEF( FT_Error ) 632 af_map_lookup( AF_FaceGlobals globals, 633 FT_Hash map, 634 FT_UInt32 lookup_offset ) 635 { 636 FT_Face face = globals->face; 637 FT_Memory memory = face->memory; 638 639 FT_Byte* table = globals->gsub + lookup_offset; 640 641 FT_UInt lookupType = FT_PEEK_USHORT( table ); 642 FT_UInt subtableCount = FT_PEEK_USHORT( table + 4 ); 643 644 FT_Byte* p = table + 6; 645 FT_Byte* limit = p + subtableCount * 2; 646 647 648 while ( p < limit ) 649 { 650 FT_Error error; 651 652 FT_UInt real_lookupType = lookupType; 653 654 FT_Byte* subtable = table + FT_NEXT_USHORT( p ); 655 656 657 if ( lookupType == 7 ) 658 { 659 FT_Byte* q = subtable + 2; 660 661 662 real_lookupType = FT_NEXT_USHORT( q ); 663 subtable += FT_PEEK_ULONG( q ); 664 } 665 666 if ( real_lookupType == 1 ) 667 { 668 FT_UInt format = FT_PEEK_USHORT( subtable ); 669 670 671 error = ( format == 1 ) 672 ? af_map_single_subst1( map, subtable, memory ) 673 : af_map_single_subst2( map, subtable, memory ); 674 } 675 else 676 error = af_map_alternate( map, subtable, memory ); 677 678 if ( error ) 679 return error; 680 } 681 682 return FT_Err_Ok; 683 } 684 685 686 #else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */ 687 688 /* ANSI C doesn't like empty source files */ 689 typedef int afgsub_dummy_; 690 691 #endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */ 692 693 /* END */