ttgxvar.c (135981B)
1 /**************************************************************************** 2 * 3 * ttgxvar.c 4 * 5 * TrueType GX Font Variation loader 6 * 7 * Copyright (C) 2004-2025 by 8 * David Turner, Robert Wilhelm, Werner Lemberg, and George Williams. 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 /************************************************************************** 20 * 21 * Apple documents the `fvar', `gvar', `cvar', and `avar' tables at 22 * 23 * https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6[fgca]var.html 24 * 25 * The documentation for `gvar' is not intelligible; `cvar' refers you 26 * to `gvar' and is thus also incomprehensible. 27 * 28 * The documentation for `avar' appears correct, but Apple has no fonts 29 * with an `avar' table, so it is hard to test. 30 * 31 * Many thanks to John Jenkins (at Apple) in figuring this out. 32 * 33 * 34 * Apple's `kern' table has some references to tuple indices, but as 35 * there is no indication where these indices are defined, nor how to 36 * interpolate the kerning values (different tuples have different 37 * classes) this issue is ignored. 38 * 39 */ 40 41 42 #include <ft2build.h> 43 #include <freetype/internal/ftdebug.h> 44 #include FT_CONFIG_CONFIG_H 45 #include <freetype/internal/ftcalc.h> 46 #include <freetype/internal/ftstream.h> 47 #include <freetype/internal/sfnt.h> 48 #include <freetype/internal/services/svmetric.h> 49 #include <freetype/tttags.h> 50 #include <freetype/ttnameid.h> 51 #include <freetype/ftmm.h> 52 #include <freetype/ftlist.h> 53 54 #include "ttpload.h" 55 #include "ttgxvar.h" 56 57 #include "tterrors.h" 58 59 60 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT 61 62 63 #define FT_Stream_FTell( stream ) \ 64 (FT_ULong)( (stream)->cursor - (stream)->base ) 65 #define FT_Stream_SeekSet( stream, off ) \ 66 (stream)->cursor = \ 67 ( (off) < (FT_ULong)( (stream)->limit - (stream)->base ) ) \ 68 ? (stream)->base + (off) \ 69 : (stream)->limit 70 71 72 /* some macros we need */ 73 #define FT_fdot14ToFixed( x ) \ 74 ( (FT_Fixed)( (FT_ULong)(x) << 2 ) ) 75 #define FT_intToFixed( i ) \ 76 ( (FT_Fixed)( (FT_ULong)(i) << 16 ) ) 77 #define FT_fdot6ToFixed( i ) \ 78 ( (FT_Fixed)( (FT_ULong)(i) << 10 ) ) 79 #define FT_fixedToInt( x ) \ 80 ( (FT_Short)( ( (x) + 0x8000U ) >> 16 ) ) 81 #define FT_fixedToFdot6( x ) \ 82 ( (FT_Pos)( ( (x) + 0x200 ) >> 10 ) ) 83 84 85 /************************************************************************** 86 * 87 * The macro FT_COMPONENT is used in trace mode. It is an implicit 88 * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log 89 * messages during execution. 90 */ 91 #undef FT_COMPONENT 92 #define FT_COMPONENT ttgxvar 93 94 95 /*************************************************************************/ 96 /*************************************************************************/ 97 /***** *****/ 98 /***** Internal Routines *****/ 99 /***** *****/ 100 /*************************************************************************/ 101 /*************************************************************************/ 102 103 104 /************************************************************************** 105 * 106 * The macro ALL_POINTS is used in `ft_var_readpackedpoints'. It 107 * indicates that there is a delta for every point without needing to 108 * enumerate all of them. 109 */ 110 111 /* ensure that value `0' has the same width as a pointer */ 112 #define ALL_POINTS (FT_UShort*)~(FT_PtrDist)0 113 114 115 #define GX_PT_POINTS_ARE_WORDS 0x80U 116 #define GX_PT_POINT_RUN_COUNT_MASK 0x7FU 117 118 119 /************************************************************************** 120 * 121 * @Function: 122 * ft_var_readpackedpoints 123 * 124 * @Description: 125 * Read a set of points to which the following deltas will apply. 126 * Points are packed with a run length encoding. 127 * 128 * @Input: 129 * stream :: 130 * The data stream. 131 * 132 * @Output: 133 * point_cnt :: 134 * The number of points read. A zero value means that 135 * all points in the glyph will be affected, without 136 * enumerating them individually. 137 * 138 * @Return: 139 * An array of FT_UShort containing the affected points or the 140 * special value ALL_POINTS. 141 */ 142 static FT_UShort* 143 ft_var_readpackedpoints( FT_Stream stream, 144 FT_UInt *point_cnt ) 145 { 146 FT_UShort *points = NULL; 147 FT_UInt n; 148 FT_UInt runcnt, cnt; 149 FT_UInt i, j; 150 FT_UShort first; 151 FT_Byte* p; 152 FT_Memory memory = stream->memory; 153 FT_Error error; 154 155 156 *point_cnt = 0; 157 158 n = FT_GET_BYTE(); 159 if ( n == 0 ) 160 return ALL_POINTS; 161 162 if ( n & GX_PT_POINTS_ARE_WORDS ) 163 { 164 n &= GX_PT_POINT_RUN_COUNT_MASK; 165 n <<= 8; 166 n |= FT_GET_BYTE(); 167 } 168 169 if ( FT_QNEW_ARRAY( points, n ) ) 170 return NULL; 171 172 p = stream->cursor; 173 first = 0; 174 i = 0; 175 while ( i < n ) 176 { 177 if ( p >= stream->limit ) 178 goto Fail; 179 180 runcnt = FT_NEXT_BYTE( p ); 181 cnt = runcnt & GX_PT_POINT_RUN_COUNT_MASK; 182 183 /* first point not included in run count */ 184 cnt++; 185 if ( cnt > n - i ) 186 cnt = n - i; 187 188 if ( runcnt & GX_PT_POINTS_ARE_WORDS ) 189 { 190 if ( 2 * cnt > (FT_UInt)( stream->limit - p ) ) 191 goto Fail; 192 193 for ( j = 0; j < cnt; j++ ) 194 { 195 first += FT_NEXT_USHORT( p ); 196 points[i++] = first; 197 } 198 } 199 else 200 { 201 if ( cnt > (FT_UInt)( stream->limit - p ) ) 202 goto Fail; 203 204 for ( j = 0; j < cnt; j++ ) 205 { 206 first += FT_NEXT_BYTE( p ); 207 points[i++] = first; 208 } 209 } 210 } 211 212 stream->cursor = p; 213 214 *point_cnt = n; 215 216 return points; 217 218 Fail: 219 FT_TRACE1(( "ft_var_readpackedpoints: invalid table\n" )); 220 221 FT_FREE( points ); 222 return NULL; 223 } 224 225 226 #define GX_DT_DELTAS_ARE_ZERO 0x80U 227 #define GX_DT_DELTAS_ARE_WORDS 0x40U 228 #define GX_DT_DELTA_RUN_COUNT_MASK 0x3FU 229 230 231 /************************************************************************** 232 * 233 * @Function: 234 * ft_var_readpackeddeltas 235 * 236 * @Description: 237 * Read a set of deltas. These are packed slightly differently than 238 * points. In particular there is no overall count. 239 * 240 * @Input: 241 * stream :: 242 * The data stream. 243 * 244 * delta_cnt :: 245 * The number of deltas to be read. 246 * 247 * @Return: 248 * An array of FT_Fixed containing the deltas for the affected 249 * points. (This only gets the deltas for one dimension. It will 250 * generally be called twice, once for x, once for y. When used in 251 * cvt table, it will only be called once.) 252 * 253 * We use FT_Fixed to avoid accumulation errors while summing up all 254 * deltas (the rounding to integer values happens as the very last 255 * step). 256 */ 257 static FT_Fixed* 258 ft_var_readpackeddeltas( FT_Stream stream, 259 FT_UInt delta_cnt ) 260 { 261 FT_Fixed *deltas = NULL; 262 FT_UInt runcnt, cnt; 263 FT_UInt i, j; 264 FT_Byte* p; 265 FT_Memory memory = stream->memory; 266 FT_Error error; 267 268 269 if ( FT_QNEW_ARRAY( deltas, delta_cnt ) ) 270 return NULL; 271 272 p = stream->cursor; 273 i = 0; 274 while ( i < delta_cnt ) 275 { 276 if ( p >= stream->limit ) 277 goto Fail; 278 279 runcnt = FT_NEXT_BYTE( p ); 280 cnt = runcnt & GX_DT_DELTA_RUN_COUNT_MASK; 281 282 /* first point not included in run count */ 283 cnt++; 284 if ( cnt > delta_cnt - i ) 285 cnt = delta_cnt - i; 286 287 if ( runcnt & GX_DT_DELTAS_ARE_ZERO ) 288 { 289 for ( j = 0; j < cnt; j++ ) 290 deltas[i++] = 0; 291 } 292 else if ( runcnt & GX_DT_DELTAS_ARE_WORDS ) 293 { 294 if ( 2 * cnt > (FT_UInt)( stream->limit - p ) ) 295 goto Fail; 296 297 for ( j = 0; j < cnt; j++ ) 298 deltas[i++] = FT_intToFixed( FT_NEXT_SHORT( p ) ); 299 } 300 else 301 { 302 if ( cnt > (FT_UInt)( stream->limit - p ) ) 303 goto Fail; 304 305 for ( j = 0; j < cnt; j++ ) 306 deltas[i++] = FT_intToFixed( FT_NEXT_CHAR( p ) ); 307 } 308 } 309 310 stream->cursor = p; 311 312 return deltas; 313 314 Fail: 315 FT_TRACE1(( "ft_var_readpackeddeltas: invalid table\n" )); 316 317 FT_FREE( deltas ); 318 return NULL; 319 } 320 321 322 /************************************************************************** 323 * 324 * @Function: 325 * ft_var_load_avar 326 * 327 * @Description: 328 * Parse the `avar' table if present. It need not be, so we return 329 * nothing. 330 * 331 * @InOut: 332 * face :: 333 * The font face. 334 */ 335 static void 336 ft_var_load_avar( TT_Face face ) 337 { 338 FT_Error error; 339 FT_Stream stream = FT_FACE_STREAM( face ); 340 FT_Memory memory = stream->memory; 341 FT_Int i, j; 342 343 GX_Blend blend = face->blend; 344 GX_AVarSegment segment; 345 GX_AVarTable table; 346 347 FT_Long version; 348 FT_Long axisCount; 349 FT_ULong table_len; 350 351 #ifndef TT_CONFIG_OPTION_NO_BORING_EXPANSION 352 FT_ULong table_offset; 353 FT_ULong store_offset; 354 FT_ULong axisMap_offset; 355 #endif 356 357 358 FT_TRACE2(( "AVAR " )); 359 360 blend->avar_loaded = TRUE; 361 error = face->goto_table( face, TTAG_avar, stream, &table_len ); 362 if ( error ) 363 { 364 FT_TRACE2(( "is missing\n" )); 365 return; 366 } 367 368 #ifndef TT_CONFIG_OPTION_NO_BORING_EXPANSION 369 table_offset = FT_STREAM_POS(); 370 #endif 371 372 if ( FT_FRAME_ENTER( table_len ) ) 373 return; 374 375 version = FT_GET_LONG(); 376 axisCount = FT_GET_LONG(); 377 378 if ( version != 0x00010000L 379 #ifndef TT_CONFIG_OPTION_NO_BORING_EXPANSION 380 && version != 0x00020000L 381 #endif 382 ) 383 { 384 FT_TRACE2(( "bad table version\n" )); 385 goto Exit; 386 } 387 388 FT_TRACE2(( "loaded\n" )); 389 390 if ( axisCount != (FT_Long)blend->mmvar->num_axis ) 391 { 392 FT_TRACE2(( "ft_var_load_avar:" 393 " number of axes in `avar' and `fvar'\n" )); 394 FT_TRACE2(( " table are different\n" )); 395 goto Exit; 396 } 397 398 if ( FT_NEW( blend->avar_table ) ) 399 goto Exit; 400 table = blend->avar_table; 401 402 if ( FT_QNEW_ARRAY( table->avar_segment, axisCount ) ) 403 goto Exit; 404 405 segment = &table->avar_segment[0]; 406 for ( i = 0; i < axisCount; i++, segment++ ) 407 { 408 FT_TRACE5(( " axis %d:\n", i )); 409 410 segment->pairCount = FT_GET_USHORT(); 411 if ( (FT_ULong)segment->pairCount * 4 > table_len || 412 FT_QNEW_ARRAY( segment->correspondence, segment->pairCount ) ) 413 { 414 /* Failure. Free everything we have done so far. We must do */ 415 /* it right now since loading the `avar' table is optional. */ 416 417 for ( j = i - 1; j >= 0; j-- ) 418 FT_FREE( table->avar_segment[j].correspondence ); 419 420 FT_FREE( table->avar_segment ); 421 goto Exit; 422 } 423 424 for ( j = 0; j < segment->pairCount; j++ ) 425 { 426 segment->correspondence[j].fromCoord = 427 FT_fdot14ToFixed( FT_GET_SHORT() ); 428 segment->correspondence[j].toCoord = 429 FT_fdot14ToFixed( FT_GET_SHORT() ); 430 431 FT_TRACE5(( " mapping %.5f to %.5f\n", 432 (double)segment->correspondence[j].fromCoord / 65536, 433 (double)segment->correspondence[j].toCoord / 65536 )); 434 } 435 436 FT_TRACE5(( "\n" )); 437 } 438 439 #ifndef TT_CONFIG_OPTION_NO_BORING_EXPANSION 440 if ( version < 0x00020000L ) 441 goto Exit; 442 443 axisMap_offset = FT_GET_ULONG(); 444 store_offset = FT_GET_ULONG(); 445 446 if ( store_offset ) 447 { 448 error = tt_var_load_item_variation_store( 449 FT_FACE( face ), 450 table_offset + store_offset, 451 &table->itemStore ); 452 if ( error ) 453 goto Exit; 454 } 455 456 if ( axisMap_offset ) 457 { 458 error = tt_var_load_delta_set_index_mapping( 459 FT_FACE( face ), 460 table_offset + axisMap_offset, 461 &table->axisMap, 462 &table->itemStore, 463 table_len ); 464 if ( error ) 465 goto Exit; 466 } 467 #endif 468 469 470 Exit: 471 FT_FRAME_EXIT(); 472 } 473 474 475 FT_LOCAL_DEF( FT_Error ) 476 tt_var_load_item_variation_store( FT_Face face, /* TT_Face */ 477 FT_ULong offset, 478 GX_ItemVarStore itemStore ) 479 { 480 TT_Face ttface = (TT_Face)face; 481 FT_Stream stream = FT_FACE_STREAM( face ); 482 FT_Memory memory = stream->memory; 483 484 FT_Error error; 485 FT_UShort format; 486 FT_ULong region_offset; 487 488 FT_UInt data_count; 489 FT_UShort axis_count; 490 FT_UInt region_count; 491 492 FT_UInt i, j; 493 FT_Byte* bytes; 494 FT_Bool long_words; 495 496 GX_Blend blend = ttface->blend; 497 FT_ULong* dataOffsetArray = NULL; 498 499 500 if ( FT_STREAM_SEEK( offset ) || 501 FT_READ_USHORT( format ) ) 502 goto Exit; 503 504 if ( format != 1 ) 505 { 506 FT_TRACE2(( "tt_var_load_item_variation_store: bad store format %d\n", 507 format )); 508 error = FT_THROW( Invalid_Table ); 509 goto Exit; 510 } 511 512 /* read top level fields */ 513 if ( FT_READ_ULONG( region_offset ) || 514 FT_READ_USHORT( data_count ) ) 515 goto Exit; 516 517 /* we need at least one entry in `itemStore->varData' */ 518 if ( !data_count ) 519 { 520 FT_TRACE2(( "tt_var_load_item_variation_store: missing varData\n" )); 521 error = FT_THROW( Invalid_Table ); 522 goto Exit; 523 } 524 525 /* make temporary copy of item variation data offsets; */ 526 /* we will parse region list first, then come back */ 527 if ( FT_QNEW_ARRAY( dataOffsetArray, data_count ) ) 528 goto Exit; 529 530 if ( FT_FRAME_ENTER( data_count * 4 ) ) 531 goto Exit; 532 533 bytes = stream->cursor; 534 535 for ( i = 0; i < data_count; i++ ) 536 dataOffsetArray[i] = FT_NEXT_ULONG( bytes ); 537 538 FT_FRAME_EXIT(); 539 540 /* parse array of region records (region list) */ 541 if ( FT_STREAM_SEEK( offset + region_offset ) ) 542 goto Exit; 543 544 if ( FT_READ_USHORT( axis_count ) || 545 FT_READ_USHORT( region_count ) ) 546 goto Exit; 547 548 if ( axis_count != (FT_Long)blend->mmvar->num_axis ) 549 { 550 FT_TRACE2(( "tt_var_load_item_variation_store:" 551 " number of axes in item variation store\n" )); 552 FT_TRACE2(( " " 553 " and `fvar' table are different\n" )); 554 error = FT_THROW( Invalid_Table ); 555 goto Exit; 556 } 557 itemStore->axisCount = axis_count; 558 559 /* new constraint in OpenType 1.8.4 */ 560 if ( region_count >= 32768U ) 561 { 562 FT_TRACE2(( "tt_var_load_item_variation_store:" 563 " too many variation region tables\n" )); 564 error = FT_THROW( Invalid_Table ); 565 goto Exit; 566 } 567 568 if ( FT_NEW_ARRAY( itemStore->varRegionList, region_count ) ) 569 goto Exit; 570 itemStore->regionCount = region_count; 571 572 if ( FT_FRAME_ENTER( (FT_Long)region_count * axis_count * 6 ) ) 573 { 574 FT_TRACE2(( "tt_var_load_item_variation_store:" 575 " not enough data for variation regions\n" )); 576 error = FT_THROW( Invalid_Table ); 577 goto Exit; 578 } 579 580 bytes = stream->cursor; 581 582 for ( i = 0; i < region_count; i++ ) 583 { 584 GX_AxisCoords axisCoords; 585 586 587 if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList, axis_count ) ) 588 { 589 FT_FRAME_EXIT(); 590 goto Exit; 591 } 592 593 axisCoords = itemStore->varRegionList[i].axisList; 594 595 for ( j = 0; j < itemStore->axisCount; j++ ) 596 { 597 FT_Int start, peak, end; 598 599 600 start = FT_NEXT_SHORT( bytes ); 601 peak = FT_NEXT_SHORT( bytes ); 602 end = FT_NEXT_SHORT( bytes ); 603 604 /* immediately tag invalid ranges with special peak = 0 */ 605 if ( ( start < 0 && end > 0 ) || start > peak || peak > end ) 606 peak = 0; 607 608 axisCoords[j].startCoord = FT_fdot14ToFixed( start ); 609 axisCoords[j].peakCoord = FT_fdot14ToFixed( peak ); 610 axisCoords[j].endCoord = FT_fdot14ToFixed( end ); 611 } 612 } 613 614 FT_FRAME_EXIT(); 615 616 /* end of region list parse */ 617 618 /* use dataOffsetArray now to parse varData items */ 619 if ( FT_NEW_ARRAY( itemStore->varData, data_count ) ) 620 goto Exit; 621 itemStore->dataCount = data_count; 622 623 for ( i = 0; i < data_count; i++ ) 624 { 625 GX_ItemVarData varData = &itemStore->varData[i]; 626 627 FT_UInt item_count; 628 FT_UShort word_delta_count; 629 FT_UInt region_idx_count; 630 FT_UInt per_region_size; 631 632 633 if ( FT_STREAM_SEEK( offset + dataOffsetArray[i] ) ) 634 goto Exit; 635 636 if ( FT_READ_USHORT( item_count ) || 637 FT_READ_USHORT( word_delta_count ) || 638 FT_READ_USHORT( region_idx_count ) ) 639 goto Exit; 640 641 long_words = !!( word_delta_count & 0x8000 ); 642 word_delta_count &= 0x7FFF; 643 644 /* check some data consistency */ 645 if ( word_delta_count > region_idx_count ) 646 { 647 FT_TRACE2(( "bad short count %d or region count %u\n", 648 word_delta_count, 649 region_idx_count )); 650 error = FT_THROW( Invalid_Table ); 651 goto Exit; 652 } 653 654 if ( region_idx_count > itemStore->regionCount ) 655 { 656 FT_TRACE2(( "inconsistent regionCount %u in varData[%u]\n", 657 region_idx_count, 658 i )); 659 error = FT_THROW( Invalid_Table ); 660 goto Exit; 661 } 662 663 /* parse region indices */ 664 if ( FT_NEW_ARRAY( varData->regionIndices, region_idx_count ) ) 665 goto Exit; 666 varData->regionIdxCount = region_idx_count; 667 varData->wordDeltaCount = word_delta_count; 668 varData->longWords = long_words; 669 670 if ( FT_FRAME_ENTER( region_idx_count * 2 ) ) 671 { 672 FT_TRACE2(( "tt_var_load_item_variation_store:" 673 " not enough data for region indices\n" )); 674 error = FT_THROW( Invalid_Table ); 675 goto Exit; 676 } 677 678 bytes = stream->cursor; 679 680 for ( j = 0; j < varData->regionIdxCount; j++ ) 681 { 682 varData->regionIndices[j] = FT_NEXT_USHORT( bytes ); 683 684 if ( varData->regionIndices[j] >= itemStore->regionCount ) 685 { 686 FT_TRACE2(( "bad region index %u\n", 687 varData->regionIndices[j] )); 688 FT_FRAME_EXIT(); 689 error = FT_THROW( Invalid_Table ); 690 goto Exit; 691 } 692 } 693 694 FT_FRAME_EXIT(); 695 696 per_region_size = word_delta_count + region_idx_count; 697 if ( long_words ) 698 per_region_size *= 2; 699 700 if ( FT_NEW_ARRAY( varData->deltaSet, per_region_size * item_count ) ) 701 goto Exit; 702 if ( FT_Stream_Read( stream, 703 varData->deltaSet, 704 per_region_size * item_count ) ) 705 { 706 FT_TRACE2(( "deltaSet read failed." )); 707 error = FT_THROW( Invalid_Table ); 708 goto Exit; 709 } 710 711 varData->itemCount = item_count; 712 } 713 714 Exit: 715 FT_FREE( dataOffsetArray ); 716 717 return error; 718 } 719 720 721 FT_LOCAL_DEF( FT_Error ) 722 tt_var_load_delta_set_index_mapping( FT_Face face, /* TT_Face */ 723 FT_ULong offset, 724 GX_DeltaSetIdxMap map, 725 GX_ItemVarStore itemStore, 726 FT_ULong table_len ) 727 { 728 FT_Stream stream = FT_FACE_STREAM( face ); 729 FT_Memory memory = stream->memory; 730 731 FT_Error error; 732 733 FT_Byte format; 734 FT_Byte entryFormat; 735 FT_UInt entrySize; 736 FT_UInt innerBitCount; 737 FT_UInt innerIndexMask; 738 FT_ULong i; 739 FT_UInt j; 740 FT_Byte* bytes; 741 742 743 if ( FT_STREAM_SEEK( offset ) || 744 FT_READ_BYTE( format ) || 745 FT_READ_BYTE( entryFormat ) ) 746 goto Exit; 747 748 if ( format == 0 ) 749 { 750 if ( FT_READ_USHORT( map->mapCount ) ) 751 goto Exit; 752 } 753 else if ( format == 1 ) /* new in OpenType 1.9 */ 754 { 755 if ( FT_READ_ULONG( map->mapCount ) ) 756 goto Exit; 757 } 758 else 759 { 760 FT_TRACE2(( "bad map format %d\n", format )); 761 error = FT_THROW( Invalid_Table ); 762 goto Exit; 763 } 764 765 if ( entryFormat & 0xC0 ) 766 { 767 FT_TRACE2(( "bad entry format %d\n", format )); 768 error = FT_THROW( Invalid_Table ); 769 goto Exit; 770 } 771 772 /* bytes per entry: 1, 2, 3, or 4 */ 773 entrySize = ( ( entryFormat & 0x30 ) >> 4 ) + 1; 774 innerBitCount = ( entryFormat & 0x0F ) + 1; 775 innerIndexMask = ( 1 << innerBitCount ) - 1; 776 777 /* rough sanity check */ 778 if ( map->mapCount * entrySize > table_len ) 779 { 780 FT_TRACE1(( "tt_var_load_delta_set_index_mapping:" 781 " invalid number of delta-set index mappings\n" )); 782 error = FT_THROW( Invalid_Table ); 783 goto Exit; 784 } 785 786 if ( FT_NEW_ARRAY( map->innerIndex, map->mapCount ) ) 787 goto Exit; 788 789 if ( FT_NEW_ARRAY( map->outerIndex, map->mapCount ) ) 790 goto Exit; 791 792 if ( FT_FRAME_ENTER( map->mapCount * entrySize ) ) 793 { 794 FT_TRACE2(( "tt_var_load_delta_set_index_mapping:" 795 " invalid number of delta-set index mappings\n" )); 796 error = FT_THROW( Invalid_Table ); 797 goto Exit; 798 } 799 800 bytes = stream->cursor; 801 802 for ( i = 0; i < map->mapCount; i++ ) 803 { 804 FT_UInt mapData = 0; 805 FT_UInt outerIndex, innerIndex; 806 807 808 /* read map data one unsigned byte at a time, big endian */ 809 for ( j = 0; j < entrySize; j++ ) 810 { 811 FT_Byte data; 812 813 814 data = FT_NEXT_BYTE( bytes ); 815 mapData = ( mapData << 8 ) | data; 816 } 817 818 /* new in OpenType 1.8.4 */ 819 if ( mapData == 0xFFFFFFFFUL ) 820 { 821 /* no variation data for this item */ 822 map->outerIndex[i] = 0xFFFFU; 823 map->innerIndex[i] = 0xFFFFU; 824 825 continue; 826 } 827 828 outerIndex = mapData >> innerBitCount; 829 830 if ( outerIndex >= itemStore->dataCount ) 831 { 832 FT_TRACE2(( "outerIndex[%lu] == %u out of range\n", 833 i, 834 outerIndex )); 835 error = FT_THROW( Invalid_Table ); 836 goto Exit; 837 } 838 839 map->outerIndex[i] = outerIndex; 840 841 innerIndex = mapData & innerIndexMask; 842 843 if ( innerIndex >= itemStore->varData[outerIndex].itemCount ) 844 { 845 FT_TRACE2(( "innerIndex[%lu] == %u out of range\n", 846 i, 847 innerIndex )); 848 error = FT_THROW( Invalid_Table ); 849 goto Exit; 850 } 851 852 map->innerIndex[i] = innerIndex; 853 } 854 855 FT_FRAME_EXIT(); 856 857 Exit: 858 return error; 859 } 860 861 862 /************************************************************************** 863 * 864 * @Function: 865 * ft_var_load_hvvar 866 * 867 * @Description: 868 * If `vertical' is zero, parse the `HVAR' table and set 869 * `blend->hvar_loaded' to TRUE. On success, `blend->hvar_checked' 870 * is set to TRUE. 871 * 872 * If `vertical' is not zero, parse the `VVAR' table and set 873 * `blend->vvar_loaded' to TRUE. On success, `blend->vvar_checked' 874 * is set to TRUE. 875 * 876 * Some memory may remain allocated on error; it is always freed in 877 * `tt_done_blend', however. 878 * 879 * @InOut: 880 * face :: 881 * The font face. 882 * 883 * @Return: 884 * FreeType error code. 0 means success. 885 */ 886 static FT_Error 887 ft_var_load_hvvar( TT_Face face, 888 FT_Bool vertical ) 889 { 890 FT_Stream stream = FT_FACE_STREAM( face ); 891 FT_Memory memory = stream->memory; 892 893 GX_Blend blend = face->blend; 894 895 GX_HVVarTable table; 896 897 FT_Error error; 898 FT_UShort majorVersion; 899 FT_ULong table_len; 900 FT_ULong table_offset; 901 FT_ULong store_offset; 902 FT_ULong widthMap_offset; 903 904 905 if ( vertical ) 906 { 907 blend->vvar_loaded = TRUE; 908 909 FT_TRACE2(( "VVAR " )); 910 911 error = face->goto_table( face, TTAG_VVAR, stream, &table_len ); 912 } 913 else 914 { 915 blend->hvar_loaded = TRUE; 916 917 FT_TRACE2(( "HVAR " )); 918 919 error = face->goto_table( face, TTAG_HVAR, stream, &table_len ); 920 } 921 922 if ( error ) 923 { 924 FT_TRACE2(( "is missing\n" )); 925 goto Exit; 926 } 927 928 table_offset = FT_STREAM_POS(); 929 930 /* skip minor version */ 931 if ( FT_READ_USHORT( majorVersion ) || 932 FT_STREAM_SKIP( 2 ) ) 933 goto Exit; 934 935 if ( majorVersion != 1 ) 936 { 937 FT_TRACE2(( "bad table version %d\n", majorVersion )); 938 error = FT_THROW( Invalid_Table ); 939 goto Exit; 940 } 941 942 if ( FT_READ_ULONG( store_offset ) || 943 FT_READ_ULONG( widthMap_offset ) ) 944 goto Exit; 945 946 if ( vertical ) 947 { 948 if ( FT_NEW( blend->vvar_table ) ) 949 goto Exit; 950 table = blend->vvar_table; 951 } 952 else 953 { 954 if ( FT_NEW( blend->hvar_table ) ) 955 goto Exit; 956 table = blend->hvar_table; 957 } 958 959 error = tt_var_load_item_variation_store( 960 FT_FACE( face ), 961 table_offset + store_offset, 962 &table->itemStore ); 963 if ( error ) 964 goto Exit; 965 966 if ( widthMap_offset ) 967 { 968 error = tt_var_load_delta_set_index_mapping( 969 FT_FACE( face ), 970 table_offset + widthMap_offset, 971 &table->widthMap, 972 &table->itemStore, 973 table_len ); 974 if ( error ) 975 goto Exit; 976 } 977 978 FT_TRACE2(( "loaded\n" )); 979 error = FT_Err_Ok; 980 981 Exit: 982 if ( !error ) 983 { 984 if ( vertical ) 985 { 986 blend->vvar_checked = TRUE; 987 988 /* FreeType doesn't provide functions to quickly retrieve */ 989 /* TSB, BSB, or VORG values; we thus don't have to implement */ 990 /* support for those three item variation stores. */ 991 992 face->variation_support |= TT_FACE_FLAG_VAR_VADVANCE; 993 } 994 else 995 { 996 blend->hvar_checked = TRUE; 997 998 /* FreeType doesn't provide functions to quickly retrieve */ 999 /* LSB or RSB values; we thus don't have to implement */ 1000 /* support for those two item variation stores. */ 1001 1002 face->variation_support |= TT_FACE_FLAG_VAR_HADVANCE; 1003 } 1004 } 1005 1006 return error; 1007 } 1008 1009 1010 static FT_Fixed 1011 tt_calculate_scalar( GX_AxisCoords axis, 1012 FT_UInt axisCount, 1013 FT_Fixed* normalizedcoords ) 1014 { 1015 FT_Fixed scalar = 0x10000L; 1016 FT_UInt j; 1017 1018 1019 /* Inner loop steps through axes in this region. */ 1020 for ( j = 0; j < axisCount; j++, axis++ ) 1021 { 1022 FT_Fixed ncv = normalizedcoords[j]; 1023 1024 1025 /* Compute the scalar contribution of this axis, */ 1026 /* with peak of 0 used for invalid axes. */ 1027 if ( axis->peakCoord == ncv || 1028 axis->peakCoord == 0 ) 1029 continue; 1030 1031 /* Ignore this region if coordinates are out of range. */ 1032 else if ( ncv <= axis->startCoord || 1033 ncv >= axis->endCoord ) 1034 { 1035 scalar = 0; 1036 break; 1037 } 1038 1039 /* Cumulative product of all the axis scalars. */ 1040 else if ( ncv < axis->peakCoord ) 1041 scalar = FT_MulDiv( scalar, 1042 ncv - axis->startCoord, 1043 axis->peakCoord - axis->startCoord ); 1044 else /* ncv > axis->peakCoord */ 1045 scalar = FT_MulDiv( scalar, 1046 axis->endCoord - ncv, 1047 axis->endCoord - axis->peakCoord ); 1048 1049 } /* per-axis loop */ 1050 1051 return scalar; 1052 } 1053 1054 1055 static FT_Int64 1056 ft_mul_add_delta_scalar( FT_Int64 returnValue, 1057 FT_Int32 delta, 1058 FT_Int32 scalar ) 1059 { 1060 1061 #ifdef FT_INT64 1062 1063 return returnValue + (FT_Int64)delta * scalar; 1064 1065 #else /* !FT_INT64 */ 1066 1067 if ( (FT_UInt32)( delta + 0x8000 ) <= 0x20000 ) 1068 { 1069 /* Fast path: multiplication result fits into 32 bits. */ 1070 1071 FT_Int32 lo = delta * scalar; 1072 1073 1074 returnValue.lo += (FT_UInt32)lo; 1075 1076 if ( returnValue.lo < (FT_UInt32)lo ) 1077 returnValue.hi += ( lo < 0 ) ? 0 : 1; 1078 1079 if ( lo < 0 ) 1080 returnValue.hi -= 1; 1081 1082 return returnValue; 1083 } 1084 else 1085 { 1086 /* Slow path: full 32x32 -> 64-bit signed multiplication. */ 1087 1088 FT_Int64 product; 1089 1090 /* Get absolute values. */ 1091 FT_UInt32 a = ( delta < 0 ) ? -delta : delta; 1092 FT_UInt32 b = ( scalar < 0 ) ? -scalar : scalar; 1093 1094 /* Prepare unsigned multiplication. */ 1095 FT_UInt32 a_lo = a & 0xFFFF; 1096 FT_UInt32 a_hi = a >> 16; 1097 1098 FT_UInt32 b_lo = b & 0xFFFF; 1099 FT_UInt32 b_hi = b >> 16; 1100 1101 /* Partial products. */ 1102 FT_UInt32 p0 = a_lo * b_lo; 1103 FT_UInt32 p1 = a_lo * b_hi; 1104 FT_UInt32 p2 = a_hi * b_lo; 1105 FT_UInt32 p3 = a_hi * b_hi; 1106 1107 /* Combine: result = p3 << 32 + (p1 + p2) << 16 + p0 */ 1108 FT_UInt32 mid = p1 + p2; 1109 FT_UInt32 mid_carry = ( mid < p1 ); 1110 1111 FT_UInt32 carry; 1112 1113 1114 product.lo = ( mid << 16 ) + ( p0 & 0xFFFF ); 1115 carry = ( product.lo < ( p0 & 0xFFFF ) ) ? 1 : 0; 1116 product.hi = p3 + ( mid >> 16 ) + mid_carry + carry; 1117 1118 /* If result should be negative, negate. */ 1119 if ( ( delta < 0 ) ^ ( scalar < 0 ) ) 1120 { 1121 product.lo = ~product.lo + 1; 1122 product.hi = ~product.hi + ( product.lo == 0 ? 1 : 0 ); 1123 } 1124 1125 /* Add to `returnValue`. */ 1126 returnValue.lo += product.lo; 1127 if ( returnValue.lo < product.lo ) 1128 returnValue.hi++; 1129 returnValue.hi += product.hi; 1130 1131 return returnValue; 1132 } 1133 1134 #endif /* !FT_INT64 */ 1135 1136 } 1137 1138 1139 static FT_ItemVarDelta 1140 ft_round_and_shift16( FT_Int64 returnValue ) 1141 { 1142 1143 #ifdef FT_INT64 1144 1145 return (FT_ItemVarDelta)( returnValue + 0x8000L ) >> 16; 1146 1147 #else /* !FT_INT64 */ 1148 1149 FT_UInt hi = returnValue.hi; 1150 FT_UInt lo = returnValue.lo; 1151 1152 FT_UInt delta; 1153 1154 1155 /* Add 0x8000 to round. */ 1156 lo += 0x8000; 1157 if ( lo < 0x8000 ) /* overflow occurred */ 1158 hi += 1; 1159 1160 /* Shift right by 16 bits. */ 1161 delta = ( hi << 16 ) | ( lo >> 16 ); 1162 1163 return (FT_ItemVarDelta)delta; 1164 1165 #endif /* !FT_INT64 */ 1166 1167 } 1168 1169 1170 FT_LOCAL_DEF( FT_ItemVarDelta ) 1171 tt_var_get_item_delta( FT_Face face, /* TT_Face */ 1172 GX_ItemVarStore itemStore, 1173 FT_UInt outerIndex, 1174 FT_UInt innerIndex ) 1175 { 1176 TT_Face ttface = (TT_Face)face; 1177 1178 GX_ItemVarData varData; 1179 1180 FT_UInt master; 1181 FT_Int64 returnValue = FT_INT64_ZERO; 1182 FT_UInt shift_base = 1; 1183 FT_UInt per_region_size; 1184 FT_Byte* bytes; 1185 1186 1187 if ( !ttface->blend || !ttface->blend->normalizedcoords ) 1188 return 0; 1189 1190 /* OpenType 1.8.4+: No variation data for this item */ 1191 /* as indices have special value 0xFFFF. */ 1192 if ( outerIndex == 0xFFFF && innerIndex == 0xFFFF ) 1193 return 0; 1194 1195 /* See pseudo code from `Font Variations Overview' */ 1196 /* in the OpenType specification. */ 1197 1198 if ( outerIndex >= itemStore->dataCount ) 1199 return 0; /* Out of range. */ 1200 1201 varData = &itemStore->varData[outerIndex]; 1202 1203 if ( innerIndex >= varData->itemCount ) 1204 return 0; /* Out of range. */ 1205 1206 if ( varData->regionIdxCount == 0 ) 1207 return 0; /* Avoid "applying zero offset to null pointer". */ 1208 1209 /* Parse delta set. */ 1210 /* */ 1211 /* Deltas are (word_delta_count + region_idx_count) bytes each */ 1212 /* if `longWords` isn't set, and twice as much otherwise. */ 1213 per_region_size = varData->wordDeltaCount + varData->regionIdxCount; 1214 if ( varData->longWords ) 1215 { 1216 shift_base = 2; 1217 per_region_size *= 2; 1218 } 1219 1220 bytes = varData->deltaSet + per_region_size * innerIndex; 1221 1222 /* outer loop steps through master designs to be blended */ 1223 for ( master = 0; master < varData->regionIdxCount; master++ ) 1224 { 1225 FT_UInt regionIndex = varData->regionIndices[master]; 1226 1227 GX_AxisCoords axis = itemStore->varRegionList[regionIndex].axisList; 1228 1229 FT_Fixed scalar = tt_calculate_scalar( 1230 axis, 1231 itemStore->axisCount, 1232 ttface->blend->normalizedcoords ); 1233 1234 1235 if ( scalar ) 1236 { 1237 FT_Int delta; 1238 1239 1240 if ( varData->longWords ) 1241 { 1242 if ( master < varData->wordDeltaCount ) 1243 delta = FT_NEXT_LONG( bytes ); 1244 else 1245 delta = FT_NEXT_SHORT( bytes ); 1246 } 1247 else 1248 { 1249 if ( master < varData->wordDeltaCount ) 1250 delta = FT_NEXT_SHORT( bytes ); 1251 else 1252 delta = FT_NEXT_CHAR( bytes ); 1253 } 1254 1255 returnValue = ft_mul_add_delta_scalar( returnValue, delta, scalar ); 1256 } 1257 else 1258 { 1259 /* Branch-free, yay. */ 1260 bytes += shift_base << ( master < varData->wordDeltaCount ); 1261 } 1262 1263 } /* per-region loop */ 1264 1265 return ft_round_and_shift16( returnValue ); 1266 } 1267 1268 1269 /************************************************************************** 1270 * 1271 * @Function: 1272 * tt_hvadvance_adjust 1273 * 1274 * @Description: 1275 * Apply `HVAR' advance width or `VVAR' advance height adjustment of 1276 * a given glyph. 1277 * 1278 * @Input: 1279 * gindex :: 1280 * The glyph index. 1281 * 1282 * vertical :: 1283 * If set, handle `VVAR' table. 1284 * 1285 * @InOut: 1286 * face :: 1287 * The font face. 1288 * 1289 * adelta :: 1290 * Points to width or height value that gets modified. 1291 */ 1292 static FT_Error 1293 tt_hvadvance_adjust( TT_Face face, 1294 FT_UInt gindex, 1295 FT_Int *avalue, 1296 FT_Bool vertical ) 1297 { 1298 FT_Error error = FT_Err_Ok; 1299 FT_UInt innerIndex, outerIndex; 1300 FT_Int delta; 1301 1302 GX_HVVarTable table; 1303 1304 1305 if ( !face->doblend || !face->blend ) 1306 goto Exit; 1307 1308 if ( vertical ) 1309 { 1310 if ( !face->blend->vvar_loaded ) 1311 { 1312 /* initialize vvar table */ 1313 face->blend->vvar_error = ft_var_load_hvvar( face, 1 ); 1314 } 1315 1316 if ( !face->blend->vvar_checked ) 1317 { 1318 error = face->blend->vvar_error; 1319 goto Exit; 1320 } 1321 1322 table = face->blend->vvar_table; 1323 } 1324 else 1325 { 1326 if ( !face->blend->hvar_loaded ) 1327 { 1328 /* initialize hvar table */ 1329 face->blend->hvar_error = ft_var_load_hvvar( face, 0 ); 1330 } 1331 1332 if ( !face->blend->hvar_checked ) 1333 { 1334 error = face->blend->hvar_error; 1335 goto Exit; 1336 } 1337 1338 table = face->blend->hvar_table; 1339 } 1340 1341 /* advance width or height adjustments are always present in an */ 1342 /* `HVAR' or `VVAR' table; no need to test for this capability */ 1343 1344 if ( table->widthMap.innerIndex ) 1345 { 1346 FT_UInt idx = gindex; 1347 1348 1349 if ( idx >= table->widthMap.mapCount ) 1350 idx = table->widthMap.mapCount - 1; 1351 1352 /* trust that HVAR parser has checked indices */ 1353 outerIndex = table->widthMap.outerIndex[idx]; 1354 innerIndex = table->widthMap.innerIndex[idx]; 1355 } 1356 else 1357 { 1358 /* no widthMap data */ 1359 outerIndex = 0; 1360 innerIndex = gindex; 1361 } 1362 1363 delta = tt_var_get_item_delta( FT_FACE( face ), 1364 &table->itemStore, 1365 outerIndex, 1366 innerIndex ); 1367 1368 if ( delta ) 1369 { 1370 FT_TRACE5(( "%s value %d adjusted by %d unit%s (%s)\n", 1371 vertical ? "vertical height" : "horizontal width", 1372 *avalue, 1373 delta, 1374 delta == 1 ? "" : "s", 1375 vertical ? "VVAR" : "HVAR" )); 1376 1377 *avalue = ADD_INT( *avalue, delta ); 1378 } 1379 1380 Exit: 1381 return error; 1382 } 1383 1384 1385 FT_LOCAL_DEF( FT_Error ) 1386 tt_hadvance_adjust( FT_Face face, /* TT_Face */ 1387 FT_UInt gindex, 1388 FT_Int *avalue ) 1389 { 1390 return tt_hvadvance_adjust( (TT_Face)face, gindex, avalue, 0 ); 1391 } 1392 1393 1394 FT_LOCAL_DEF( FT_Error ) 1395 tt_vadvance_adjust( FT_Face face, /* TT_Face */ 1396 FT_UInt gindex, 1397 FT_Int *avalue ) 1398 { 1399 return tt_hvadvance_adjust( (TT_Face)face, gindex, avalue, 1 ); 1400 } 1401 1402 1403 #define GX_VALUE_SIZE 8 1404 1405 /* all values are FT_Short or FT_UShort entities; */ 1406 /* we treat them consistently as FT_Short */ 1407 #define GX_VALUE_CASE( tag, dflt ) \ 1408 case MVAR_TAG_ ## tag : \ 1409 p = (FT_Short*)&face->dflt; \ 1410 break 1411 1412 #define GX_GASP_CASE( idx ) \ 1413 case MVAR_TAG_GASP_ ## idx : \ 1414 if ( idx < face->gasp.numRanges - 1 ) \ 1415 p = (FT_Short*)&face->gasp.gaspRanges[idx].maxPPEM; \ 1416 else \ 1417 p = NULL; \ 1418 break 1419 1420 1421 static FT_Short* 1422 ft_var_get_value_pointer( TT_Face face, 1423 FT_ULong mvar_tag ) 1424 { 1425 FT_Short* p; 1426 1427 1428 switch ( mvar_tag ) 1429 { 1430 GX_GASP_CASE( 0 ); 1431 GX_GASP_CASE( 1 ); 1432 GX_GASP_CASE( 2 ); 1433 GX_GASP_CASE( 3 ); 1434 GX_GASP_CASE( 4 ); 1435 GX_GASP_CASE( 5 ); 1436 GX_GASP_CASE( 6 ); 1437 GX_GASP_CASE( 7 ); 1438 GX_GASP_CASE( 8 ); 1439 GX_GASP_CASE( 9 ); 1440 1441 GX_VALUE_CASE( CPHT, os2.sCapHeight ); 1442 GX_VALUE_CASE( HASC, os2.sTypoAscender ); 1443 GX_VALUE_CASE( HCLA, os2.usWinAscent ); 1444 GX_VALUE_CASE( HCLD, os2.usWinDescent ); 1445 GX_VALUE_CASE( HCOF, horizontal.caret_Offset ); 1446 GX_VALUE_CASE( HCRN, horizontal.caret_Slope_Run ); 1447 GX_VALUE_CASE( HCRS, horizontal.caret_Slope_Rise ); 1448 GX_VALUE_CASE( HDSC, os2.sTypoDescender ); 1449 GX_VALUE_CASE( HLGP, os2.sTypoLineGap ); 1450 GX_VALUE_CASE( SBXO, os2.ySubscriptXOffset); 1451 GX_VALUE_CASE( SBXS, os2.ySubscriptXSize ); 1452 GX_VALUE_CASE( SBYO, os2.ySubscriptYOffset ); 1453 GX_VALUE_CASE( SBYS, os2.ySubscriptYSize ); 1454 GX_VALUE_CASE( SPXO, os2.ySuperscriptXOffset ); 1455 GX_VALUE_CASE( SPXS, os2.ySuperscriptXSize ); 1456 GX_VALUE_CASE( SPYO, os2.ySuperscriptYOffset ); 1457 GX_VALUE_CASE( SPYS, os2.ySuperscriptYSize ); 1458 GX_VALUE_CASE( STRO, os2.yStrikeoutPosition ); 1459 GX_VALUE_CASE( STRS, os2.yStrikeoutSize ); 1460 GX_VALUE_CASE( UNDO, postscript.underlinePosition ); 1461 GX_VALUE_CASE( UNDS, postscript.underlineThickness ); 1462 GX_VALUE_CASE( VASC, vertical.Ascender ); 1463 GX_VALUE_CASE( VCOF, vertical.caret_Offset ); 1464 GX_VALUE_CASE( VCRN, vertical.caret_Slope_Run ); 1465 GX_VALUE_CASE( VCRS, vertical.caret_Slope_Rise ); 1466 GX_VALUE_CASE( VDSC, vertical.Descender ); 1467 GX_VALUE_CASE( VLGP, vertical.Line_Gap ); 1468 GX_VALUE_CASE( XHGT, os2.sxHeight ); 1469 1470 default: 1471 /* ignore unknown tag */ 1472 p = NULL; 1473 } 1474 1475 return p; 1476 } 1477 1478 1479 /************************************************************************** 1480 * 1481 * @Function: 1482 * ft_var_load_mvar 1483 * 1484 * @Description: 1485 * Parse the `MVAR' table. 1486 * 1487 * Some memory may remain allocated on error; it is always freed in 1488 * `tt_done_blend', however. 1489 * 1490 * @InOut: 1491 * face :: 1492 * The font face. 1493 */ 1494 static void 1495 ft_var_load_mvar( TT_Face face ) 1496 { 1497 FT_Stream stream = FT_FACE_STREAM( face ); 1498 FT_Memory memory = stream->memory; 1499 1500 GX_Blend blend = face->blend; 1501 GX_ItemVarStore itemStore; 1502 GX_Value value, limit; 1503 1504 FT_Error error; 1505 FT_UShort majorVersion; 1506 FT_ULong table_len; 1507 FT_ULong table_offset; 1508 FT_UShort store_offset; 1509 FT_ULong records_offset; 1510 1511 1512 FT_TRACE2(( "MVAR " )); 1513 1514 error = face->goto_table( face, TTAG_MVAR, stream, &table_len ); 1515 if ( error ) 1516 { 1517 FT_TRACE2(( "is missing\n" )); 1518 return; 1519 } 1520 1521 table_offset = FT_STREAM_POS(); 1522 1523 /* skip minor version */ 1524 if ( FT_READ_USHORT( majorVersion ) || 1525 FT_STREAM_SKIP( 2 ) ) 1526 return; 1527 1528 if ( majorVersion != 1 ) 1529 { 1530 FT_TRACE2(( "bad table version %d\n", majorVersion )); 1531 return; 1532 } 1533 1534 if ( FT_NEW( blend->mvar_table ) ) 1535 return; 1536 1537 /* skip reserved entry and value record size */ 1538 if ( FT_STREAM_SKIP( 4 ) || 1539 FT_READ_USHORT( blend->mvar_table->valueCount ) || 1540 FT_READ_USHORT( store_offset ) ) 1541 return; 1542 1543 records_offset = FT_STREAM_POS(); 1544 1545 error = tt_var_load_item_variation_store( 1546 FT_FACE( face ), 1547 table_offset + store_offset, 1548 &blend->mvar_table->itemStore ); 1549 if ( error ) 1550 return; 1551 1552 if ( FT_NEW_ARRAY( blend->mvar_table->values, 1553 blend->mvar_table->valueCount ) ) 1554 return; 1555 1556 if ( FT_STREAM_SEEK( records_offset ) || 1557 FT_FRAME_ENTER( blend->mvar_table->valueCount * GX_VALUE_SIZE ) ) 1558 return; 1559 1560 value = blend->mvar_table->values; 1561 limit = FT_OFFSET( value, blend->mvar_table->valueCount ); 1562 itemStore = &blend->mvar_table->itemStore; 1563 1564 for ( ; value < limit; value++ ) 1565 { 1566 value->tag = FT_GET_ULONG(); 1567 value->outerIndex = FT_GET_USHORT(); 1568 value->innerIndex = FT_GET_USHORT(); 1569 1570 /* new in OpenType 1.8.4 */ 1571 if ( value->outerIndex == 0xFFFFU && value->innerIndex == 0xFFFFU ) 1572 { 1573 /* no variation data for this item */ 1574 continue; 1575 } 1576 1577 if ( value->outerIndex >= itemStore->dataCount || 1578 value->innerIndex >= itemStore->varData[value->outerIndex] 1579 .itemCount ) 1580 { 1581 error = FT_THROW( Invalid_Table ); 1582 break; 1583 } 1584 } 1585 1586 FT_FRAME_EXIT(); 1587 1588 if ( error ) 1589 return; 1590 1591 FT_TRACE2(( "loaded\n" )); 1592 1593 value = blend->mvar_table->values; 1594 limit = FT_OFFSET( value, blend->mvar_table->valueCount ); 1595 1596 /* save original values of the data MVAR is going to modify */ 1597 for ( ; value < limit; value++ ) 1598 { 1599 FT_Short* p = ft_var_get_value_pointer( face, value->tag ); 1600 1601 1602 if ( p ) 1603 value->unmodified = *p; 1604 #ifdef FT_DEBUG_LEVEL_TRACE 1605 else 1606 FT_TRACE1(( "ft_var_load_mvar: Ignoring unknown tag `%c%c%c%c'\n", 1607 (FT_Char)( value->tag >> 24 ), 1608 (FT_Char)( value->tag >> 16 ), 1609 (FT_Char)( value->tag >> 8 ), 1610 (FT_Char)( value->tag ) )); 1611 #endif 1612 } 1613 1614 face->variation_support |= TT_FACE_FLAG_VAR_MVAR; 1615 } 1616 1617 1618 static FT_Error 1619 ft_size_reset_iterator( FT_ListNode node, 1620 void* user ) 1621 { 1622 FT_Size size = (FT_Size)node->data; 1623 FT_Service_MetricsVariations var = (FT_Service_MetricsVariations)user; 1624 1625 1626 var->size_reset( size ); 1627 1628 return FT_Err_Ok; 1629 } 1630 1631 1632 /************************************************************************** 1633 * 1634 * @Function: 1635 * tt_apply_mvar 1636 * 1637 * @Description: 1638 * Apply `MVAR' table adjustments. 1639 * 1640 * @InOut: 1641 * face :: 1642 * The font face. 1643 */ 1644 FT_LOCAL_DEF( void ) 1645 tt_apply_mvar( FT_Face face ) /* TT_Face */ 1646 { 1647 TT_Face ttface = (TT_Face)face; 1648 1649 GX_Blend blend = ttface->blend; 1650 GX_Value value, limit; 1651 1652 FT_Short mvar_hasc_delta = 0; 1653 FT_Short mvar_hdsc_delta = 0; 1654 FT_Short mvar_hlgp_delta = 0; 1655 1656 1657 if ( !( ttface->variation_support & TT_FACE_FLAG_VAR_MVAR ) ) 1658 return; 1659 1660 value = blend->mvar_table->values; 1661 limit = FT_OFFSET( value, blend->mvar_table->valueCount ); 1662 1663 for ( ; value < limit; value++ ) 1664 { 1665 FT_Short* p = ft_var_get_value_pointer( ttface, value->tag ); 1666 FT_Int delta; 1667 1668 1669 delta = tt_var_get_item_delta( face, 1670 &blend->mvar_table->itemStore, 1671 value->outerIndex, 1672 value->innerIndex ); 1673 1674 if ( p && delta ) 1675 { 1676 FT_TRACE5(( "value %c%c%c%c (%d unit%s) adjusted by %d unit%s (MVAR)\n", 1677 (FT_Char)( value->tag >> 24 ), 1678 (FT_Char)( value->tag >> 16 ), 1679 (FT_Char)( value->tag >> 8 ), 1680 (FT_Char)( value->tag ), 1681 value->unmodified, 1682 value->unmodified == 1 ? "" : "s", 1683 delta, 1684 delta == 1 ? "" : "s" )); 1685 1686 /* since we handle both signed and unsigned values as FT_Short, */ 1687 /* ensure proper overflow arithmetic */ 1688 *p = (FT_Short)( value->unmodified + (FT_Short)delta ); 1689 1690 /* Treat hasc, hdsc and hlgp specially, see below. */ 1691 if ( value->tag == MVAR_TAG_HASC ) 1692 mvar_hasc_delta = (FT_Short)delta; 1693 else if ( value->tag == MVAR_TAG_HDSC ) 1694 mvar_hdsc_delta = (FT_Short)delta; 1695 else if ( value->tag == MVAR_TAG_HLGP ) 1696 mvar_hlgp_delta = (FT_Short)delta; 1697 } 1698 } 1699 1700 /* adjust all derived values */ 1701 { 1702 FT_Service_MetricsVariations var = 1703 (FT_Service_MetricsVariations)ttface->face_var; 1704 1705 /* 1706 * Apply the deltas of hasc, hdsc and hlgp to the FT_Face's ascender, 1707 * descender and height attributes, no matter how they were originally 1708 * computed. 1709 * 1710 * (Code that ignores those and accesses the font's metrics values 1711 * directly is already served by the delta application code above.) 1712 * 1713 * The MVAR table supports variations for both typo and win metrics. 1714 * According to Behdad Esfahbod, the thinking of the working group was 1715 * that no one uses win metrics anymore for setting line metrics (the 1716 * specification even calls these metrics "horizontal clipping 1717 * ascent/descent", probably for their role on the Windows platform in 1718 * computing clipping boxes), and new fonts should use typo metrics, so 1719 * typo deltas should be applied to whatever sfnt_load_face decided the 1720 * line metrics should be. 1721 * 1722 * Before, the following led to different line metrics between default 1723 * outline and instances, visible when e.g. the default outlines were 1724 * used as the regular face and instances for everything else: 1725 * 1726 * 1. sfnt_load_face applied the hhea metrics by default. 1727 * 2. This code later applied the typo metrics by default, regardless of 1728 * whether they were actually changed or the font had the OS/2 table's 1729 * fsSelection's bit 7 (USE_TYPO_METRICS) set. 1730 */ 1731 FT_Short current_line_gap = face->height - face->ascender + 1732 face->descender; 1733 1734 1735 face->ascender = face->ascender + mvar_hasc_delta; 1736 face->descender = face->descender + mvar_hdsc_delta; 1737 face->height = face->ascender - face->descender + 1738 current_line_gap + mvar_hlgp_delta; 1739 1740 face->underline_position = ttface->postscript.underlinePosition - 1741 ttface->postscript.underlineThickness / 2; 1742 face->underline_thickness = ttface->postscript.underlineThickness; 1743 1744 /* iterate over all FT_Size objects and call `var->size_reset' */ 1745 /* to propagate the metrics changes */ 1746 if ( var && var->size_reset ) 1747 FT_List_Iterate( &face->sizes_list, 1748 ft_size_reset_iterator, 1749 (void*)var ); 1750 } 1751 } 1752 1753 1754 typedef struct GX_GVar_Head_ 1755 { 1756 FT_Long version; 1757 FT_UShort axisCount; 1758 FT_UShort globalCoordCount; 1759 FT_ULong offsetToCoord; 1760 FT_UShort glyphCount; 1761 FT_UShort flags; 1762 FT_ULong offsetToData; 1763 1764 } GX_GVar_Head; 1765 1766 1767 /************************************************************************** 1768 * 1769 * @Function: 1770 * ft_var_load_gvar 1771 * 1772 * @Description: 1773 * Parse the `gvar' table if present. If `fvar' is there, `gvar' had 1774 * better be there too. 1775 * 1776 * @InOut: 1777 * face :: 1778 * The font face. 1779 * 1780 * @Return: 1781 * FreeType error code. 0 means success. 1782 */ 1783 static FT_Error 1784 ft_var_load_gvar( TT_Face face ) 1785 { 1786 FT_Stream stream = FT_FACE_STREAM( face ); 1787 FT_Memory memory = stream->memory; 1788 GX_Blend blend = face->blend; 1789 FT_Error error; 1790 FT_UInt i, j; 1791 FT_Byte* bytes; 1792 FT_ULong table_len; 1793 FT_ULong gvar_start; 1794 FT_ULong offsetToData; 1795 FT_ULong offsets_len; 1796 GX_GVar_Head gvar_head; 1797 1798 static const FT_Frame_Field gvar_fields[] = 1799 { 1800 1801 #undef FT_STRUCTURE 1802 #define FT_STRUCTURE GX_GVar_Head 1803 1804 FT_FRAME_START( 20 ), 1805 FT_FRAME_LONG ( version ), 1806 FT_FRAME_USHORT( axisCount ), 1807 FT_FRAME_USHORT( globalCoordCount ), 1808 FT_FRAME_ULONG ( offsetToCoord ), 1809 FT_FRAME_USHORT( glyphCount ), 1810 FT_FRAME_USHORT( flags ), 1811 FT_FRAME_ULONG ( offsetToData ), 1812 FT_FRAME_END 1813 }; 1814 1815 1816 FT_TRACE2(( "GVAR " )); 1817 1818 if ( FT_SET_ERROR( face->goto_table( face, 1819 TTAG_gvar, 1820 stream, 1821 &table_len ) ) ) 1822 { 1823 FT_TRACE2(( "is missing\n" )); 1824 goto Exit; 1825 } 1826 1827 gvar_start = FT_STREAM_POS( ); 1828 if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) ) 1829 goto Exit; 1830 1831 if ( gvar_head.version != 0x00010000L ) 1832 { 1833 FT_TRACE1(( "bad table version\n" )); 1834 error = FT_THROW( Invalid_Table ); 1835 goto Exit; 1836 } 1837 1838 if ( gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis ) 1839 { 1840 FT_TRACE1(( "ft_var_load_gvar:" 1841 " number of axes in `gvar' and `cvar'\n" )); 1842 FT_TRACE1(( " table are different\n" )); 1843 error = FT_THROW( Invalid_Table ); 1844 goto Exit; 1845 } 1846 1847 /* rough sanity check, ignoring offsets */ 1848 if ( (FT_ULong)gvar_head.globalCoordCount * gvar_head.axisCount > 1849 table_len / 2 ) 1850 { 1851 FT_TRACE1(( "ft_var_load_gvar:" 1852 " invalid number of global coordinates\n" )); 1853 error = FT_THROW( Invalid_Table ); 1854 goto Exit; 1855 } 1856 1857 /* offsets can be either 2 or 4 bytes */ 1858 /* (one more offset than glyphs, to mark size of last) */ 1859 offsets_len = ( gvar_head.glyphCount + 1 ) * 1860 ( ( gvar_head.flags & 1 ) ? 4L : 2L ); 1861 1862 /* rough sanity check */ 1863 if (offsets_len > table_len ) 1864 { 1865 FT_TRACE1(( "ft_var_load_gvar: invalid number of glyphs\n" )); 1866 error = FT_THROW( Invalid_Table ); 1867 goto Exit; 1868 } 1869 1870 FT_TRACE2(( "loaded\n" )); 1871 1872 blend->gvar_size = table_len; 1873 offsetToData = gvar_start + gvar_head.offsetToData; 1874 1875 FT_TRACE5(( "gvar: there %s %d shared coordinate%s:\n", 1876 gvar_head.globalCoordCount == 1 ? "is" : "are", 1877 gvar_head.globalCoordCount, 1878 gvar_head.globalCoordCount == 1 ? "" : "s" )); 1879 1880 if ( FT_FRAME_ENTER( offsets_len ) ) 1881 goto Exit; 1882 1883 bytes = stream->cursor; 1884 1885 /* offsets (one more offset than glyphs, to mark size of last) */ 1886 if ( FT_QNEW_ARRAY( blend->glyphoffsets, gvar_head.glyphCount + 1 ) ) 1887 goto Fail2; 1888 1889 if ( gvar_head.flags & 1 ) 1890 { 1891 FT_ULong limit = gvar_start + table_len; 1892 FT_ULong max_offset = 0; 1893 1894 1895 if ( stream->limit - stream->cursor < gvar_head.glyphCount * 4 ) 1896 { 1897 FT_TRACE2(( "ft_var_load_gvar:" 1898 " glyph variation data offset not enough\n" )); 1899 error = FT_THROW( Invalid_Table ); 1900 goto Fail; 1901 } 1902 1903 for ( i = 0; i <= gvar_head.glyphCount; i++ ) 1904 { 1905 blend->glyphoffsets[i] = offsetToData + FT_NEXT_ULONG( bytes ); 1906 1907 if ( max_offset <= blend->glyphoffsets[i] ) 1908 max_offset = blend->glyphoffsets[i]; 1909 else 1910 { 1911 FT_TRACE2(( "ft_var_load_gvar:" 1912 " glyph variation data offset %u not monotonic\n", 1913 i )); 1914 blend->glyphoffsets[i] = max_offset; 1915 } 1916 1917 /* use `<', not `<=' */ 1918 if ( limit < blend->glyphoffsets[i] ) 1919 { 1920 FT_TRACE2(( "ft_var_load_gvar:" 1921 " glyph variation data offset %u out of range\n", 1922 i )); 1923 blend->glyphoffsets[i] = limit; 1924 } 1925 } 1926 } 1927 else 1928 { 1929 FT_ULong limit = gvar_start + table_len; 1930 FT_ULong max_offset = 0; 1931 1932 1933 if ( stream->limit - stream->cursor < gvar_head.glyphCount * 2 ) 1934 { 1935 FT_TRACE2(( "ft_var_load_gvar:" 1936 " glyph variation data offset not enough\n" )); 1937 error = FT_THROW( Invalid_Table ); 1938 goto Fail; 1939 } 1940 1941 for ( i = 0; i <= gvar_head.glyphCount; i++ ) 1942 { 1943 blend->glyphoffsets[i] = offsetToData + FT_NEXT_USHORT( bytes ) * 2; 1944 1945 if ( max_offset <= blend->glyphoffsets[i] ) 1946 max_offset = blend->glyphoffsets[i]; 1947 else 1948 { 1949 FT_TRACE2(( "ft_var_load_gvar:" 1950 " glyph variation data offset %u not monotonic\n", 1951 i )); 1952 blend->glyphoffsets[i] = max_offset; 1953 } 1954 1955 /* use `<', not `<=' */ 1956 if ( limit < blend->glyphoffsets[i] ) 1957 { 1958 FT_TRACE2(( "ft_var_load_gvar:" 1959 " glyph variation data offset %u out of range\n", 1960 i )); 1961 blend->glyphoffsets[i] = limit; 1962 } 1963 } 1964 } 1965 1966 blend->gv_glyphcnt = gvar_head.glyphCount; 1967 1968 FT_FRAME_EXIT(); 1969 1970 if ( gvar_head.globalCoordCount != 0 ) 1971 { 1972 if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord ) || 1973 FT_FRAME_ENTER( gvar_head.globalCoordCount * 1974 gvar_head.axisCount * 2L ) ) 1975 { 1976 FT_TRACE2(( "ft_var_load_gvar:" 1977 " glyph variation shared tuples missing\n" )); 1978 goto Fail; 1979 } 1980 1981 bytes = stream->cursor; 1982 1983 if ( FT_QNEW_ARRAY( blend->tuplecoords, 1984 gvar_head.axisCount * gvar_head.globalCoordCount ) ) 1985 goto Fail2; 1986 1987 for ( i = 0; i < gvar_head.globalCoordCount; i++ ) 1988 { 1989 FT_TRACE5(( " [ " )); 1990 for ( j = 0; j < (FT_UInt)gvar_head.axisCount; j++ ) 1991 { 1992 blend->tuplecoords[i * gvar_head.axisCount + j] = 1993 FT_fdot14ToFixed( FT_NEXT_SHORT( bytes ) ); 1994 FT_TRACE5(( "%.5f ", 1995 (double)blend->tuplecoords[i * gvar_head.axisCount + j] / 65536 )); 1996 } 1997 FT_TRACE5(( "]\n" )); 1998 } 1999 2000 if ( FT_NEW_ARRAY( blend->tuplescalars, 2001 gvar_head.globalCoordCount ) ) 2002 goto Fail2; 2003 2004 blend->tuplecount = gvar_head.globalCoordCount; 2005 2006 FT_TRACE5(( "\n" )); 2007 2008 FT_FRAME_EXIT(); 2009 } 2010 2011 Exit: 2012 return error; 2013 2014 Fail2: 2015 FT_FRAME_EXIT(); 2016 2017 Fail: 2018 FT_FREE( blend->glyphoffsets ); 2019 blend->gv_glyphcnt = 0; 2020 goto Exit; 2021 } 2022 2023 2024 /************************************************************************** 2025 * 2026 * @Function: 2027 * ft_var_apply_tuple 2028 * 2029 * @Description: 2030 * Figure out whether a given tuple (design) applies to the current 2031 * blend, and if so, what is the scaling factor. 2032 * 2033 * @Input: 2034 * blend :: 2035 * The current blend of the font. 2036 * 2037 * tupleIndex :: 2038 * A flag saying whether this is an intermediate 2039 * tuple or not. 2040 * 2041 * tuple_coords :: 2042 * The coordinates of the tuple in normalized axis 2043 * units. 2044 * 2045 * im_start_coords :: 2046 * The initial coordinates where this tuple starts 2047 * to apply (for intermediate coordinates). 2048 * 2049 * im_end_coords :: 2050 * The final coordinates after which this tuple no 2051 * longer applies (for intermediate coordinates). 2052 * 2053 * @Return: 2054 * An FT_Fixed value containing the scaling factor. 2055 */ 2056 static FT_Fixed 2057 ft_var_apply_tuple( GX_Blend blend, 2058 FT_UShort tupleIndex, 2059 FT_Fixed* tuple_coords, 2060 FT_Fixed* im_start_coords, 2061 FT_Fixed* im_end_coords ) 2062 { 2063 FT_UInt i; 2064 FT_Fixed apply = 0x10000L; 2065 2066 2067 for ( i = 0; i < blend->num_axis; i++ ) 2068 { 2069 FT_Fixed ncv; 2070 2071 2072 if ( tuple_coords[i] == 0 ) 2073 { 2074 FT_TRACE6(( " tuple coordinate is zero, ignore\n" )); 2075 continue; 2076 } 2077 2078 ncv = blend->normalizedcoords[i]; 2079 2080 FT_TRACE6(( " axis %u coordinate %.5f:\n", i, (double)ncv / 65536 )); 2081 2082 if ( ncv == 0 ) 2083 { 2084 FT_TRACE6(( " axis coordinate is zero, stop\n" )); 2085 apply = 0; 2086 break; 2087 } 2088 2089 if ( tuple_coords[i] == ncv ) 2090 { 2091 FT_TRACE6(( " tuple coordinate %.5f fits perfectly\n", 2092 (double)tuple_coords[i] / 65536 )); 2093 /* `apply' does not change */ 2094 continue; 2095 } 2096 2097 if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) ) 2098 { 2099 /* not an intermediate tuple */ 2100 2101 if ( ( tuple_coords[i] > ncv && ncv > 0 ) || 2102 ( tuple_coords[i] < ncv && ncv < 0 ) ) 2103 { 2104 FT_TRACE6(( " tuple coordinate %.5f fits\n", 2105 (double)tuple_coords[i] / 65536 )); 2106 apply = FT_MulDiv( apply, ncv, tuple_coords[i] ); 2107 } 2108 else 2109 { 2110 FT_TRACE6(( " tuple coordinate %.5f is exceeded, stop\n", 2111 (double)tuple_coords[i] / 65536 )); 2112 apply = 0; 2113 break; 2114 } 2115 } 2116 else 2117 { 2118 /* intermediate tuple */ 2119 2120 if ( ncv <= im_start_coords[i] || 2121 ncv >= im_end_coords[i] ) 2122 { 2123 FT_TRACE6(( " intermediate tuple range ]%.5f;%.5f[ is exceeded," 2124 " stop\n", 2125 (double)im_start_coords[i] / 65536, 2126 (double)im_end_coords[i] / 65536 )); 2127 apply = 0; 2128 break; 2129 } 2130 2131 FT_TRACE6(( " intermediate tuple range ]%.5f;%.5f[ fits\n", 2132 (double)im_start_coords[i] / 65536, 2133 (double)im_end_coords[i] / 65536 )); 2134 if ( ncv < tuple_coords[i] ) 2135 apply = FT_MulDiv( apply, 2136 ncv - im_start_coords[i], 2137 tuple_coords[i] - im_start_coords[i] ); 2138 else /* ncv > tuple_coords[i] */ 2139 apply = FT_MulDiv( apply, 2140 im_end_coords[i] - ncv, 2141 im_end_coords[i] - tuple_coords[i] ); 2142 } 2143 } 2144 2145 FT_TRACE6(( " apply factor is %.5f\n", (double)apply / 65536 )); 2146 2147 return apply; 2148 } 2149 2150 2151 /* convert from design coordinates to normalized coordinates */ 2152 2153 static void 2154 ft_var_to_normalized( TT_Face face, 2155 FT_UInt num_coords, 2156 FT_Fixed* coords, 2157 FT_Fixed* normalized ) 2158 { 2159 FT_Error error = FT_Err_Ok; 2160 FT_Memory memory = face->root.memory; 2161 FT_UInt i, j; 2162 2163 GX_Blend blend; 2164 FT_MM_Var* mmvar; 2165 FT_Var_Axis* a; 2166 GX_AVarSegment av; 2167 2168 FT_Fixed* new_normalized = NULL; 2169 FT_Fixed* old_normalized; 2170 2171 2172 blend = face->blend; 2173 mmvar = blend->mmvar; 2174 2175 if ( num_coords > mmvar->num_axis ) 2176 { 2177 FT_TRACE2(( "ft_var_to_normalized:" 2178 " only using first %u of %u coordinates\n", 2179 mmvar->num_axis, num_coords )); 2180 num_coords = mmvar->num_axis; 2181 } 2182 2183 /* Axis normalization is a two-stage process. First we normalize */ 2184 /* based on the [min,def,max] values for the axis to be [-1,0,1]. */ 2185 /* Then, if there's an `avar' table, we renormalize this range. */ 2186 2187 a = mmvar->axis; 2188 for ( i = 0; i < num_coords; i++, a++ ) 2189 { 2190 FT_Fixed coord = coords[i]; 2191 2192 2193 FT_TRACE5(( " %u: %.5f\n", i, (double)coord / 65536 )); 2194 if ( coord > a->maximum || coord < a->minimum ) 2195 { 2196 FT_TRACE1(( "ft_var_to_normalized: design coordinate %.5f\n", 2197 (double)coord / 65536 )); 2198 FT_TRACE1(( " is out of range [%.5f;%.5f];" 2199 " clamping\n", 2200 (double)a->minimum / 65536, 2201 (double)a->maximum / 65536 )); 2202 } 2203 2204 if ( coord > a->def ) 2205 normalized[i] = coord >= a->maximum ? 0x10000L : 2206 FT_DivFix( SUB_LONG( coord, a->def ), 2207 SUB_LONG( a->maximum, a->def ) ); 2208 else if ( coord < a->def ) 2209 normalized[i] = coord <= a->minimum ? -0x10000L : 2210 FT_DivFix( SUB_LONG( coord, a->def ), 2211 SUB_LONG( a->def, a->minimum ) ); 2212 else 2213 normalized[i] = 0; 2214 } 2215 2216 FT_TRACE5(( "\n" )); 2217 2218 for ( ; i < mmvar->num_axis; i++ ) 2219 normalized[i] = 0; 2220 2221 if ( blend->avar_table ) 2222 { 2223 GX_AVarTable table = blend->avar_table; 2224 2225 2226 FT_TRACE5(( "normalized design coordinates" 2227 " before applying `avar' data:\n" )); 2228 2229 if ( table->avar_segment ) 2230 { 2231 av = table->avar_segment; 2232 2233 for ( i = 0; i < mmvar->num_axis; i++, av++ ) 2234 { 2235 for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) 2236 { 2237 if ( normalized[i] < av->correspondence[j].fromCoord ) 2238 { 2239 FT_TRACE5(( " %.5f\n", (double)normalized[i] / 65536 )); 2240 2241 normalized[i] = 2242 FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord, 2243 av->correspondence[j].toCoord - 2244 av->correspondence[j - 1].toCoord, 2245 av->correspondence[j].fromCoord - 2246 av->correspondence[j - 1].fromCoord ) + 2247 av->correspondence[j - 1].toCoord; 2248 break; 2249 } 2250 } 2251 } 2252 } 2253 2254 if ( table->itemStore.varData ) 2255 { 2256 if ( FT_QNEW_ARRAY( new_normalized, mmvar->num_axis ) ) 2257 return; 2258 2259 /* Install our half-normalized coordinates for the next */ 2260 /* Item Variation Store to work with. */ 2261 old_normalized = face->blend->normalizedcoords; 2262 face->blend->normalizedcoords = normalized; 2263 2264 for ( i = 0; i < mmvar->num_axis; i++ ) 2265 { 2266 FT_Fixed v = normalized[i]; 2267 FT_UInt innerIndex = i; 2268 FT_UInt outerIndex = 0; 2269 FT_Int delta; 2270 2271 2272 if ( table->axisMap.innerIndex ) 2273 { 2274 FT_UInt idx = i; 2275 2276 2277 if ( idx >= table->axisMap.mapCount ) 2278 idx = table->axisMap.mapCount - 1; 2279 2280 outerIndex = table->axisMap.outerIndex[idx]; 2281 innerIndex = table->axisMap.innerIndex[idx]; 2282 } 2283 2284 delta = tt_var_get_item_delta( FT_FACE( face ), 2285 &table->itemStore, 2286 outerIndex, 2287 innerIndex ); 2288 2289 /* Convert delta in F2DOT14 to 16.16 before adding. */ 2290 v += MUL_INT( delta, 4 ); 2291 2292 /* Clamp value to range [-1, 1]. */ 2293 v = v >= 0x10000L ? 0x10000 : v; 2294 v = v <= -0x10000L ? -0x10000 : v; 2295 2296 new_normalized[i] = v; 2297 } 2298 2299 for ( i = 0; i < mmvar->num_axis; i++ ) 2300 { 2301 normalized[i] = new_normalized[i]; 2302 } 2303 2304 face->blend->normalizedcoords = old_normalized; 2305 2306 FT_FREE( new_normalized ); 2307 } 2308 } 2309 } 2310 2311 2312 /* convert from normalized coordinates to design coordinates */ 2313 2314 static void 2315 ft_var_to_design( TT_Face face, 2316 FT_UInt num_coords, 2317 FT_Fixed* coords, 2318 FT_Fixed* design ) 2319 { 2320 GX_Blend blend; 2321 FT_MM_Var* mmvar; 2322 FT_Var_Axis* a; 2323 2324 FT_UInt i, j, nc; 2325 2326 2327 blend = face->blend; 2328 2329 nc = num_coords; 2330 if ( num_coords > blend->num_axis ) 2331 { 2332 FT_TRACE2(( "ft_var_to_design:" 2333 " only using first %u of %u coordinates\n", 2334 blend->num_axis, num_coords )); 2335 nc = blend->num_axis; 2336 } 2337 2338 for ( i = 0; i < nc; i++ ) 2339 design[i] = coords[i]; 2340 2341 for ( ; i < num_coords; i++ ) 2342 design[i] = 0; 2343 2344 if ( blend->avar_table && blend->avar_table->avar_segment ) 2345 { 2346 GX_AVarSegment av = blend->avar_table->avar_segment; 2347 2348 2349 FT_TRACE5(( "design coordinates" 2350 " after removing `avar' distortion:\n" )); 2351 2352 for ( i = 0; i < nc; i++, av++ ) 2353 { 2354 for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) 2355 { 2356 if ( design[i] < av->correspondence[j].toCoord ) 2357 { 2358 design[i] = 2359 FT_MulDiv( design[i] - av->correspondence[j - 1].toCoord, 2360 av->correspondence[j].fromCoord - 2361 av->correspondence[j - 1].fromCoord, 2362 av->correspondence[j].toCoord - 2363 av->correspondence[j - 1].toCoord ) + 2364 av->correspondence[j - 1].fromCoord; 2365 2366 FT_TRACE5(( " %.5f\n", (double)design[i] / 65536 )); 2367 break; 2368 } 2369 } 2370 } 2371 } 2372 2373 mmvar = blend->mmvar; 2374 a = mmvar->axis; 2375 2376 for ( i = 0; i < nc; i++, a++ ) 2377 { 2378 if ( design[i] < 0 ) 2379 design[i] = a->def + FT_MulFix( design[i], 2380 a->def - a->minimum ); 2381 else if ( design[i] > 0 ) 2382 design[i] = a->def + FT_MulFix( design[i], 2383 a->maximum - a->def ); 2384 else 2385 design[i] = a->def; 2386 } 2387 } 2388 2389 2390 /*************************************************************************/ 2391 /*************************************************************************/ 2392 /***** *****/ 2393 /***** MULTIPLE MASTERS SERVICE FUNCTIONS *****/ 2394 /***** *****/ 2395 /*************************************************************************/ 2396 /*************************************************************************/ 2397 2398 2399 typedef struct GX_FVar_Head_ 2400 { 2401 FT_Long version; 2402 FT_UShort offsetToData; 2403 FT_UShort axisCount; 2404 FT_UShort axisSize; 2405 FT_UShort instanceCount; 2406 FT_UShort instanceSize; 2407 2408 } GX_FVar_Head; 2409 2410 2411 typedef struct fvar_axis_ 2412 { 2413 FT_ULong axisTag; 2414 FT_Fixed minValue; 2415 FT_Fixed defaultValue; 2416 FT_Fixed maxValue; 2417 FT_UShort flags; 2418 FT_UShort nameID; 2419 2420 } GX_FVar_Axis; 2421 2422 2423 /************************************************************************** 2424 * 2425 * @Function: 2426 * TT_Get_MM_Var 2427 * 2428 * @Description: 2429 * Check that the font's `fvar' table is valid, parse it, and return 2430 * those data. It also loads (and parses) the `MVAR' table, if 2431 * possible. 2432 * 2433 * @InOut: 2434 * face :: 2435 * The font face. 2436 * TT_Get_MM_Var initializes the blend structure. 2437 * 2438 * @Output: 2439 * master :: 2440 * The `fvar' data (must be freed by caller). Can be NULL, 2441 * which makes this function simply load MM support. 2442 * 2443 * @Return: 2444 * FreeType error code. 0 means success. 2445 */ 2446 FT_LOCAL_DEF( FT_Error ) 2447 TT_Get_MM_Var( FT_Face face, /* TT_Face */ 2448 FT_MM_Var* *master ) 2449 { 2450 TT_Face ttface = (TT_Face)face; 2451 FT_Stream stream = FT_FACE_STREAM( face ); 2452 FT_Memory memory = FT_FACE_MEMORY( face ); 2453 FT_ULong table_len; 2454 FT_Error error = FT_Err_Ok; 2455 FT_ULong fvar_start = 0; 2456 FT_UInt i, j; 2457 FT_MM_Var* mmvar = NULL; 2458 FT_Fixed* next_coords; 2459 FT_Fixed* nsc; 2460 FT_String* next_name; 2461 FT_Var_Axis* a; 2462 FT_Fixed* c; 2463 FT_Var_Named_Style* ns; 2464 GX_FVar_Head fvar_head = { 0, 0, 0, 0, 0, 0 }; 2465 FT_Bool usePsName = 0; 2466 FT_UInt num_instances; 2467 FT_UInt num_axes; 2468 FT_UShort* axis_flags; 2469 2470 FT_Offset mmvar_size; 2471 FT_Offset axis_flags_size; 2472 FT_Offset axis_size; 2473 FT_Offset namedstyle_size; 2474 FT_Offset next_coords_size; 2475 FT_Offset next_name_size; 2476 2477 FT_Bool need_init; 2478 2479 static const FT_Frame_Field fvar_fields[] = 2480 { 2481 2482 #undef FT_STRUCTURE 2483 #define FT_STRUCTURE GX_FVar_Head 2484 2485 FT_FRAME_START( 16 ), 2486 FT_FRAME_LONG ( version ), 2487 FT_FRAME_USHORT ( offsetToData ), 2488 FT_FRAME_SKIP_SHORT, 2489 FT_FRAME_USHORT ( axisCount ), 2490 FT_FRAME_USHORT ( axisSize ), 2491 FT_FRAME_USHORT ( instanceCount ), 2492 FT_FRAME_USHORT ( instanceSize ), 2493 FT_FRAME_END 2494 }; 2495 2496 static const FT_Frame_Field fvaraxis_fields[] = 2497 { 2498 2499 #undef FT_STRUCTURE 2500 #define FT_STRUCTURE GX_FVar_Axis 2501 2502 FT_FRAME_START( 20 ), 2503 FT_FRAME_ULONG ( axisTag ), 2504 FT_FRAME_LONG ( minValue ), 2505 FT_FRAME_LONG ( defaultValue ), 2506 FT_FRAME_LONG ( maxValue ), 2507 FT_FRAME_USHORT( flags ), 2508 FT_FRAME_USHORT( nameID ), 2509 FT_FRAME_END 2510 }; 2511 2512 /* `num_instances` holds the number of all named instances including */ 2513 /* the default instance, which might be missing in the table of named */ 2514 /* instances (in 'fvar'). This value is validated in `sfobjs.c` and */ 2515 /* may be reset to 0 if consistency checks fail. */ 2516 num_instances = (FT_UInt)face->style_flags >> 16; 2517 2518 /* read the font data and set up the internal representation */ 2519 /* if not already done */ 2520 2521 need_init = !ttface->blend; 2522 2523 if ( need_init ) 2524 { 2525 FT_TRACE2(( "FVAR " )); 2526 2527 if ( FT_SET_ERROR( ttface->goto_table( ttface, TTAG_fvar, 2528 stream, &table_len ) ) ) 2529 { 2530 FT_TRACE1(( "is missing\n" )); 2531 goto Exit; 2532 } 2533 2534 fvar_start = FT_STREAM_POS( ); 2535 2536 /* the validity of the `fvar' header data was already checked */ 2537 /* in function `sfnt_init_face' */ 2538 if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) ) 2539 goto Exit; 2540 2541 /* If `num_instances` is larger, synthetization of the default */ 2542 /* instance is required. If `num_instances` is smaller, */ 2543 /* however, the value has been reset to 0 in `sfnt_init_face` */ 2544 /* (in `sfobjs.c`); in this case we have underallocated `mmvar` */ 2545 /* structs. */ 2546 if ( num_instances < fvar_head.instanceCount ) 2547 { 2548 error = FT_THROW( Invalid_Table ); 2549 goto Exit; 2550 } 2551 2552 usePsName = FT_BOOL( fvar_head.instanceSize == 2553 6 + 4 * fvar_head.axisCount ); 2554 2555 FT_TRACE2(( "loaded\n" )); 2556 2557 FT_TRACE5(( "%d variation ax%s\n", 2558 fvar_head.axisCount, 2559 fvar_head.axisCount == 1 ? "is" : "es" )); 2560 2561 if ( FT_NEW( ttface->blend ) ) 2562 goto Exit; 2563 2564 num_axes = fvar_head.axisCount; 2565 ttface->blend->num_axis = num_axes; 2566 } 2567 else 2568 num_axes = ttface->blend->num_axis; 2569 2570 /* prepare storage area for MM data; this cannot overflow */ 2571 /* 32-bit arithmetic because of the size limits used in the */ 2572 /* `fvar' table validity check in `sfnt_init_face' */ 2573 2574 /* the various `*_size' variables, which we also use as */ 2575 /* offsets into the `mmvar' array, must be multiples of the */ 2576 /* pointer size (except the last one); without such an */ 2577 /* alignment there might be runtime errors due to */ 2578 /* misaligned addresses */ 2579 #undef ALIGN_SIZE 2580 #define ALIGN_SIZE( n ) \ 2581 ( ( (n) + sizeof (void*) - 1 ) & ~( sizeof (void*) - 1 ) ) 2582 2583 mmvar_size = ALIGN_SIZE( sizeof ( FT_MM_Var ) ); 2584 axis_flags_size = ALIGN_SIZE( num_axes * 2585 sizeof ( FT_UShort ) ); 2586 axis_size = ALIGN_SIZE( num_axes * 2587 sizeof ( FT_Var_Axis ) ); 2588 namedstyle_size = ALIGN_SIZE( num_instances * 2589 sizeof ( FT_Var_Named_Style ) ); 2590 next_coords_size = ALIGN_SIZE( num_instances * 2591 num_axes * 2592 sizeof ( FT_Fixed ) ); 2593 next_name_size = num_axes * 5; 2594 2595 if ( need_init ) 2596 { 2597 ttface->blend->mmvar_len = mmvar_size + 2598 axis_flags_size + 2599 axis_size + 2600 namedstyle_size + 2601 next_coords_size + 2602 next_name_size; 2603 2604 if ( FT_ALLOC( mmvar, ttface->blend->mmvar_len ) ) 2605 goto Exit; 2606 ttface->blend->mmvar = mmvar; 2607 2608 /* set up pointers and offsets into the `mmvar' array; */ 2609 /* the data gets filled in later on */ 2610 2611 mmvar->num_axis = 2612 num_axes; 2613 mmvar->num_designs = 2614 ~0U; /* meaningless in this context; each glyph */ 2615 /* may have a different number of designs */ 2616 /* (or tuples, as called by Apple) */ 2617 mmvar->num_namedstyles = 2618 num_instances; 2619 2620 /* alas, no public field in `FT_Var_Axis' for axis flags */ 2621 axis_flags = 2622 (FT_UShort*)( (char*)mmvar + mmvar_size ); 2623 mmvar->axis = 2624 (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size ); 2625 mmvar->namedstyle = 2626 (FT_Var_Named_Style*)( (char*)mmvar->axis + axis_size ); 2627 2628 next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle + 2629 namedstyle_size ); 2630 for ( i = 0; i < num_instances; i++ ) 2631 { 2632 mmvar->namedstyle[i].coords = next_coords; 2633 next_coords += num_axes; 2634 } 2635 2636 next_name = (FT_String*)( (char*)mmvar->namedstyle + 2637 namedstyle_size + next_coords_size ); 2638 for ( i = 0; i < num_axes; i++ ) 2639 { 2640 mmvar->axis[i].name = next_name; 2641 next_name += 5; 2642 } 2643 2644 /* now fill in the data */ 2645 2646 if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) ) 2647 goto Exit; 2648 2649 a = mmvar->axis; 2650 for ( i = 0; i < num_axes; i++ ) 2651 { 2652 GX_FVar_Axis axis_rec; 2653 2654 #ifdef FT_DEBUG_LEVEL_TRACE 2655 int invalid = 0; 2656 #endif 2657 2658 2659 if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) ) 2660 goto Exit; 2661 a->tag = axis_rec.axisTag; 2662 a->minimum = axis_rec.minValue; 2663 a->def = axis_rec.defaultValue; 2664 a->maximum = axis_rec.maxValue; 2665 a->strid = axis_rec.nameID; 2666 2667 a->name[0] = (FT_String)( a->tag >> 24 ); 2668 a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF ); 2669 a->name[2] = (FT_String)( ( a->tag >> 8 ) & 0xFF ); 2670 a->name[3] = (FT_String)( ( a->tag ) & 0xFF ); 2671 a->name[4] = '\0'; 2672 2673 *axis_flags = axis_rec.flags; 2674 2675 if ( a->minimum > a->def || 2676 a->def > a->maximum ) 2677 { 2678 a->minimum = a->def; 2679 a->maximum = a->def; 2680 2681 #ifdef FT_DEBUG_LEVEL_TRACE 2682 invalid = 1; 2683 #endif 2684 } 2685 2686 #ifdef FT_DEBUG_LEVEL_TRACE 2687 if ( i == 0 ) 2688 FT_TRACE5(( " idx tag " 2689 /* " XXX `XXXX'" */ 2690 " minimum default maximum flags\n" )); 2691 /* " XXXX.XXXXX XXXX.XXXXX XXXX.XXXXX 0xXXXX" */ 2692 2693 FT_TRACE5(( " %3u `%s'" 2694 " %10.5f %10.5f %10.5f 0x%04X%s\n", 2695 i, 2696 a->name, 2697 (double)a->minimum / 65536, 2698 (double)a->def / 65536, 2699 (double)a->maximum / 65536, 2700 *axis_flags, 2701 invalid ? " (invalid, disabled)" : "" )); 2702 #endif 2703 2704 a++; 2705 axis_flags++; 2706 } 2707 2708 FT_TRACE5(( "\n" )); 2709 2710 /* named instance coordinates are stored as design coordinates; */ 2711 /* we have to convert them to normalized coordinates also */ 2712 if ( FT_NEW_ARRAY( ttface->blend->normalized_stylecoords, 2713 num_axes * num_instances ) ) 2714 goto Exit; 2715 2716 if ( fvar_head.instanceCount && !ttface->blend->avar_loaded ) 2717 { 2718 FT_ULong offset = FT_STREAM_POS(); 2719 2720 2721 ft_var_load_avar( ttface ); 2722 2723 if ( FT_STREAM_SEEK( offset ) ) 2724 goto Exit; 2725 } 2726 2727 FT_TRACE5(( "%d named instance%s\n", 2728 fvar_head.instanceCount, 2729 fvar_head.instanceCount == 1 ? "" : "s" )); 2730 2731 ns = mmvar->namedstyle; 2732 nsc = ttface->blend->normalized_stylecoords; 2733 for ( i = 0; i < fvar_head.instanceCount; i++, ns++ ) 2734 { 2735 /* PostScript names add 2 bytes to the instance record size */ 2736 if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) + 2737 4L * num_axes ) ) 2738 goto Exit; 2739 2740 ns->strid = FT_GET_USHORT(); 2741 (void) /* flags = */ FT_GET_USHORT(); 2742 2743 c = ns->coords; 2744 for ( j = 0; j < num_axes; j++, c++ ) 2745 *c = FT_GET_LONG(); 2746 2747 /* valid psid values are 6, [256;32767], and 0xFFFF */ 2748 if ( usePsName ) 2749 ns->psid = FT_GET_USHORT(); 2750 else 2751 ns->psid = 0xFFFF; 2752 2753 #ifdef FT_DEBUG_LEVEL_TRACE 2754 { 2755 SFNT_Service sfnt = (SFNT_Service)ttface->sfnt; 2756 2757 FT_String* strname = NULL; 2758 FT_String* psname = NULL; 2759 2760 FT_ULong pos; 2761 2762 2763 pos = FT_STREAM_POS(); 2764 2765 if ( ns->strid != 0xFFFF ) 2766 { 2767 (void)sfnt->get_name( ttface, 2768 (FT_UShort)ns->strid, 2769 &strname ); 2770 if ( strname && !ft_strcmp( strname, ".notdef" ) ) 2771 strname = NULL; 2772 } 2773 2774 if ( ns->psid != 0xFFFF ) 2775 { 2776 (void)sfnt->get_name( ttface, 2777 (FT_UShort)ns->psid, 2778 &psname ); 2779 if ( psname && !ft_strcmp( psname, ".notdef" ) ) 2780 psname = NULL; 2781 } 2782 2783 (void)FT_STREAM_SEEK( pos ); 2784 2785 FT_TRACE5(( " named instance %u (%s%s%s, %s%s%s)\n", 2786 i, 2787 strname ? "name: `" : "", 2788 strname ? strname : "unnamed", 2789 strname ? "'" : "", 2790 psname ? "PS name: `" : "", 2791 psname ? psname : "no PS name", 2792 psname ? "'" : "" )); 2793 2794 FT_FREE( strname ); 2795 FT_FREE( psname ); 2796 } 2797 #endif /* FT_DEBUG_LEVEL_TRACE */ 2798 2799 ft_var_to_normalized( ttface, num_axes, ns->coords, nsc ); 2800 nsc += num_axes; 2801 2802 FT_FRAME_EXIT(); 2803 } 2804 2805 if ( num_instances != fvar_head.instanceCount ) 2806 { 2807 SFNT_Service sfnt = (SFNT_Service)ttface->sfnt; 2808 2809 FT_Int found, dummy1, dummy2; 2810 FT_UInt strid = ~0U; 2811 2812 2813 /* The default instance is missing in the array */ 2814 /* of named instances; try to synthesize an entry. */ 2815 /* If this fails, `default_named_instance` remains */ 2816 /* at value zero, which doesn't do any harm. */ 2817 found = sfnt->get_name_id( ttface, 2818 TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY, 2819 &dummy1, 2820 &dummy2 ); 2821 if ( found ) 2822 strid = TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY; 2823 else 2824 { 2825 found = sfnt->get_name_id( ttface, 2826 TT_NAME_ID_FONT_SUBFAMILY, 2827 &dummy1, 2828 &dummy2 ); 2829 if ( found ) 2830 strid = TT_NAME_ID_FONT_SUBFAMILY; 2831 } 2832 2833 if ( found ) 2834 { 2835 found = sfnt->get_name_id( ttface, 2836 TT_NAME_ID_PS_NAME, 2837 &dummy1, 2838 &dummy2 ); 2839 if ( found ) 2840 { 2841 FT_TRACE5(( "TT_Get_MM_Var:" 2842 " Adding default instance to named instances\n" )); 2843 2844 /* named instance indices start with value 1 */ 2845 ttface->var_default_named_instance = num_instances; 2846 2847 ns = &mmvar->namedstyle[fvar_head.instanceCount]; 2848 2849 ns->strid = strid; 2850 ns->psid = TT_NAME_ID_PS_NAME; 2851 2852 a = mmvar->axis; 2853 c = ns->coords; 2854 for ( j = 0; j < num_axes; j++, a++, c++ ) 2855 *c = a->def; 2856 } 2857 } 2858 } 2859 2860 ft_var_load_mvar( ttface ); 2861 } 2862 2863 /* fill the output array if requested */ 2864 2865 if ( master ) 2866 { 2867 FT_UInt n; 2868 2869 2870 if ( FT_DUP( mmvar, ttface->blend->mmvar, ttface->blend->mmvar_len ) ) 2871 goto Exit; 2872 2873 axis_flags = 2874 (FT_UShort*)( (char*)mmvar + mmvar_size ); 2875 mmvar->axis = 2876 (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size ); 2877 mmvar->namedstyle = 2878 (FT_Var_Named_Style*)( (char*)mmvar->axis+ axis_size ); 2879 2880 next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle + 2881 namedstyle_size ); 2882 for ( n = 0; n < mmvar->num_namedstyles; n++ ) 2883 { 2884 mmvar->namedstyle[n].coords = next_coords; 2885 next_coords += num_axes; 2886 } 2887 2888 a = mmvar->axis; 2889 next_name = (FT_String*)( (char*)mmvar->namedstyle + 2890 namedstyle_size + next_coords_size ); 2891 for ( n = 0; n < num_axes; n++ ) 2892 { 2893 a->name = next_name; 2894 2895 /* standard PostScript names for some standard apple tags */ 2896 if ( a->tag == TTAG_wght ) 2897 a->name = (char*)"Weight"; 2898 else if ( a->tag == TTAG_wdth ) 2899 a->name = (char*)"Width"; 2900 else if ( a->tag == TTAG_opsz ) 2901 a->name = (char*)"OpticalSize"; 2902 else if ( a->tag == TTAG_slnt ) 2903 a->name = (char*)"Slant"; 2904 else if ( a->tag == TTAG_ital ) 2905 a->name = (char*)"Italic"; 2906 2907 next_name += 5; 2908 a++; 2909 } 2910 2911 *master = mmvar; 2912 } 2913 2914 Exit: 2915 return error; 2916 } 2917 2918 2919 static FT_Error 2920 tt_set_mm_blend( TT_Face face, 2921 FT_UInt num_coords, 2922 FT_Fixed* coords, 2923 FT_Bool set_design_coords ) 2924 { 2925 FT_Error error = FT_Err_Ok; 2926 GX_Blend blend; 2927 FT_MM_Var* mmvar; 2928 FT_UInt i; 2929 2930 FT_Bool all_design_coords = FALSE; 2931 2932 FT_Memory memory = face->root.memory; 2933 2934 enum 2935 { 2936 mcvt_retain, 2937 mcvt_modify, 2938 mcvt_load 2939 2940 } manageCvt; 2941 2942 2943 if ( !face->blend ) 2944 { 2945 face->doblend = FALSE; 2946 for ( i = 0; i < num_coords; i++ ) 2947 if ( coords[i] ) 2948 { 2949 face->doblend = TRUE; 2950 break; 2951 } 2952 if ( !face->doblend ) 2953 goto Exit; 2954 2955 if ( FT_SET_ERROR( TT_Get_MM_Var( FT_FACE( face ), NULL ) ) ) 2956 goto Exit; 2957 } 2958 2959 blend = face->blend; 2960 mmvar = blend->mmvar; 2961 2962 if ( num_coords > mmvar->num_axis ) 2963 { 2964 FT_TRACE2(( "TT_Set_MM_Blend:" 2965 " only using first %u of %u coordinates\n", 2966 mmvar->num_axis, num_coords )); 2967 num_coords = mmvar->num_axis; 2968 } 2969 2970 FT_TRACE5(( "TT_Set_MM_Blend:\n" )); 2971 FT_TRACE5(( " normalized design coordinates:\n" )); 2972 2973 for ( i = 0; i < num_coords; i++ ) 2974 { 2975 FT_TRACE5(( " %.5f\n", (double)coords[i] / 65536 )); 2976 if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L ) 2977 { 2978 FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n", 2979 (double)coords[i] / 65536 )); 2980 FT_TRACE1(( " is out of range [-1;1]\n" )); 2981 error = FT_THROW( Invalid_Argument ); 2982 goto Exit; 2983 } 2984 } 2985 2986 FT_TRACE5(( "\n" )); 2987 2988 if ( !face->is_cff2 && !blend->glyphoffsets ) 2989 { 2990 /* While a missing 'gvar' table is acceptable, for example for */ 2991 /* fonts that only vary metrics information or 'COLR' v1 */ 2992 /* `PaintVar*` tables, an incorrect SFNT table offset or size */ 2993 /* for 'gvar', or an inconsistent 'gvar' table is not. */ 2994 error = ft_var_load_gvar( face ); 2995 if ( error != FT_Err_Table_Missing && error != FT_Err_Ok ) 2996 goto Exit; 2997 error = FT_Err_Ok; 2998 } 2999 3000 if ( !blend->coords ) 3001 { 3002 if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) ) 3003 goto Exit; 3004 3005 /* the first time we have to compute all design coordinates */ 3006 all_design_coords = TRUE; 3007 } 3008 3009 if ( !blend->normalizedcoords ) 3010 { 3011 if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) ) 3012 goto Exit; 3013 3014 manageCvt = mcvt_modify; 3015 3016 /* If we have not set the blend coordinates before this, then the */ 3017 /* cvt table will still be what we read from the `cvt ' table and */ 3018 /* we don't need to reload it. We may need to change it though... */ 3019 } 3020 else 3021 { 3022 FT_Bool have_diff = 0; 3023 FT_UInt j; 3024 FT_Fixed* c; 3025 FT_Fixed* n; 3026 3027 3028 manageCvt = mcvt_retain; 3029 3030 for ( i = 0; i < num_coords; i++ ) 3031 { 3032 if ( blend->normalizedcoords[i] != coords[i] ) 3033 { 3034 manageCvt = mcvt_load; 3035 have_diff = 1; 3036 break; 3037 } 3038 } 3039 3040 if ( !have_diff ) 3041 { 3042 if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ) 3043 { 3044 FT_UInt instance_index = (FT_UInt)face->root.face_index >> 16; 3045 3046 3047 c = blend->normalizedcoords + i; 3048 n = blend->normalized_stylecoords + 3049 ( instance_index - 1 ) * mmvar->num_axis + 3050 i; 3051 3052 for ( j = i; j < mmvar->num_axis; j++, n++, c++ ) 3053 if ( *c != *n ) 3054 have_diff = 1; 3055 } 3056 else 3057 { 3058 c = blend->normalizedcoords + i; 3059 for ( j = i; j < mmvar->num_axis; j++, c++ ) 3060 if ( *c != 0 ) 3061 have_diff = 1; 3062 } 3063 } 3064 3065 /* return value -1 indicates `no change' */ 3066 if ( !have_diff ) 3067 return -1; 3068 3069 for ( ; i < mmvar->num_axis; i++ ) 3070 { 3071 if ( blend->normalizedcoords[i] != 0 ) 3072 { 3073 manageCvt = mcvt_load; 3074 break; 3075 } 3076 } 3077 3078 /* If we don't change the blend coords then we don't need to do */ 3079 /* anything to the cvt table. It will be correct. Otherwise we */ 3080 /* no longer have the original cvt (it was modified when we set */ 3081 /* the blend last time), so we must reload and then modify it. */ 3082 } 3083 3084 blend->num_axis = mmvar->num_axis; 3085 if ( coords ) 3086 FT_MEM_COPY( blend->normalizedcoords, 3087 coords, 3088 num_coords * sizeof ( FT_Fixed ) ); 3089 3090 if ( set_design_coords ) 3091 ft_var_to_design( face, 3092 all_design_coords ? blend->num_axis : num_coords, 3093 blend->normalizedcoords, 3094 blend->coords ); 3095 3096 face->doblend = FALSE; 3097 for ( i = 0; i < blend->num_axis; i++ ) 3098 { 3099 if ( blend->normalizedcoords[i] ) 3100 { 3101 face->doblend = TRUE; 3102 break; 3103 } 3104 } 3105 3106 if ( face->cvt ) 3107 { 3108 switch ( manageCvt ) 3109 { 3110 case mcvt_load: 3111 /* The cvt table has been loaded already; every time we change the */ 3112 /* blend we may need to reload and remodify the cvt table. */ 3113 FT_FREE( face->cvt ); 3114 3115 error = tt_face_load_cvt( face, face->root.stream ); 3116 break; 3117 3118 case mcvt_modify: 3119 /* The original cvt table is in memory. All we need to do is */ 3120 /* apply the `cvar' table (if any). */ 3121 error = tt_face_vary_cvt( face, face->root.stream ); 3122 break; 3123 3124 case mcvt_retain: 3125 /* The cvt table is correct for this set of coordinates. */ 3126 break; 3127 } 3128 } 3129 3130 for ( i = 0 ; i < blend->tuplecount ; i++ ) 3131 blend->tuplescalars[i] = (FT_Fixed)-0x20000L; 3132 3133 Exit: 3134 return error; 3135 } 3136 3137 3138 /************************************************************************** 3139 * 3140 * @Function: 3141 * TT_Set_MM_Blend 3142 * 3143 * @Description: 3144 * Set the blend (normalized) coordinates for this instance of the 3145 * font. Check that the `gvar' table is reasonable and does some 3146 * initial preparation. 3147 * 3148 * @InOut: 3149 * face :: 3150 * The font. 3151 * Initialize the blend structure with `gvar' data. 3152 * 3153 * @Input: 3154 * num_coords :: 3155 * The number of available coordinates. If it is 3156 * larger than the number of axes, ignore the excess 3157 * values. If it is smaller than the number of axes, 3158 * use the default value (0) for the remaining axes. 3159 * 3160 * coords :: 3161 * An array of `num_coords', each between [-1,1]. 3162 * 3163 * @Return: 3164 * FreeType error code. 0 means success, -1 means success and unchanged 3165 * axis values. 3166 */ 3167 FT_LOCAL_DEF( FT_Error ) 3168 TT_Set_MM_Blend( FT_Face face, /* TT_Face */ 3169 FT_UInt num_coords, 3170 FT_Fixed* coords ) 3171 { 3172 FT_Error error; 3173 3174 3175 error = tt_set_mm_blend( (TT_Face)face, num_coords, coords, 1 ); 3176 if ( error == FT_Err_Ok ) 3177 { 3178 FT_UInt i; 3179 3180 3181 for ( i = 0; i < num_coords; i++ ) 3182 if ( coords[i] ) 3183 { 3184 error = -2; /* -2 means is_variable. */ 3185 break; 3186 } 3187 } 3188 3189 return error; 3190 } 3191 3192 3193 /************************************************************************** 3194 * 3195 * @Function: 3196 * TT_Get_MM_Blend 3197 * 3198 * @Description: 3199 * Get the blend (normalized) coordinates for this instance of the 3200 * font. 3201 * 3202 * @InOut: 3203 * face :: 3204 * The font. 3205 * Initialize the blend structure with `gvar' data. 3206 * 3207 * @Input: 3208 * num_coords :: 3209 * The number of available coordinates. If it is 3210 * larger than the number of axes, set the excess 3211 * values to 0. 3212 * 3213 * coords :: 3214 * An array of `num_coords', each between [-1,1]. 3215 * 3216 * @Return: 3217 * FreeType error code. 0 means success, -1 means success and unchanged 3218 * axis values. 3219 */ 3220 FT_LOCAL_DEF( FT_Error ) 3221 TT_Get_MM_Blend( FT_Face face, /* TT_Face */ 3222 FT_UInt num_coords, 3223 FT_Fixed* coords ) 3224 { 3225 TT_Face ttface = (TT_Face)face; 3226 3227 FT_Error error = FT_Err_Ok; 3228 GX_Blend blend; 3229 FT_UInt i, nc; 3230 3231 3232 if ( !ttface->blend ) 3233 { 3234 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 3235 return error; 3236 } 3237 3238 blend = ttface->blend; 3239 3240 if ( !blend->coords ) 3241 { 3242 /* select default instance coordinates */ 3243 /* if no instance is selected yet */ 3244 if ( FT_SET_ERROR( tt_set_mm_blend( ttface, 0, NULL, 1 ) ) ) 3245 return error; 3246 } 3247 3248 nc = num_coords; 3249 if ( num_coords > blend->num_axis ) 3250 { 3251 FT_TRACE2(( "TT_Get_MM_Blend:" 3252 " only using first %u of %u coordinates\n", 3253 blend->num_axis, num_coords )); 3254 nc = blend->num_axis; 3255 } 3256 3257 if ( ttface->doblend ) 3258 { 3259 for ( i = 0; i < nc; i++ ) 3260 coords[i] = blend->normalizedcoords[i]; 3261 } 3262 else 3263 { 3264 for ( i = 0; i < nc; i++ ) 3265 coords[i] = 0; 3266 } 3267 3268 for ( ; i < num_coords; i++ ) 3269 coords[i] = 0; 3270 3271 return FT_Err_Ok; 3272 } 3273 3274 3275 /************************************************************************** 3276 * 3277 * @Function: 3278 * TT_Set_Var_Design 3279 * 3280 * @Description: 3281 * Set the coordinates for the instance, measured in the user 3282 * coordinate system. Parse the `avar' table (if present) to convert 3283 * from user to normalized coordinates. 3284 * 3285 * @InOut: 3286 * face :: 3287 * The font face. 3288 * Initialize the blend struct with `gvar' data. 3289 * 3290 * @Input: 3291 * num_coords :: 3292 * The number of available coordinates. If it is 3293 * larger than the number of axes, ignore the excess 3294 * values. If it is smaller than the number of axes, 3295 * use the default values for the remaining axes. 3296 * 3297 * coords :: 3298 * A coordinate array with `num_coords' elements. 3299 * 3300 * @Return: 3301 * FreeType error code. 0 means success. 3302 */ 3303 FT_LOCAL_DEF( FT_Error ) 3304 TT_Set_Var_Design( FT_Face face, /* TT_Face */ 3305 FT_UInt num_coords, 3306 FT_Fixed* coords ) 3307 { 3308 TT_Face ttface = (TT_Face)face; 3309 FT_Error error = FT_Err_Ok; 3310 GX_Blend blend; 3311 FT_MM_Var* mmvar; 3312 FT_UInt i; 3313 FT_Memory memory = FT_FACE_MEMORY( face ); 3314 3315 FT_Fixed* c; 3316 FT_Fixed* n; 3317 FT_Fixed* normalized = NULL; 3318 3319 FT_Bool have_diff = 0; 3320 3321 3322 if ( !ttface->blend ) 3323 { 3324 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 3325 goto Exit; 3326 } 3327 3328 blend = ttface->blend; 3329 mmvar = blend->mmvar; 3330 3331 if ( num_coords > mmvar->num_axis ) 3332 { 3333 FT_TRACE2(( "TT_Set_Var_Design:" 3334 " only using first %u of %u coordinates\n", 3335 mmvar->num_axis, num_coords )); 3336 num_coords = mmvar->num_axis; 3337 } 3338 3339 if ( !blend->coords ) 3340 { 3341 if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) ) 3342 goto Exit; 3343 } 3344 3345 c = blend->coords; 3346 n = coords; 3347 for ( i = 0; i < num_coords; i++, n++, c++ ) 3348 { 3349 if ( *c != *n ) 3350 { 3351 *c = *n; 3352 have_diff = 1; 3353 } 3354 } 3355 3356 if ( FT_IS_NAMED_INSTANCE( face ) ) 3357 { 3358 FT_UInt instance_index; 3359 FT_Var_Named_Style* named_style; 3360 3361 3362 instance_index = (FT_UInt)face->face_index >> 16; 3363 named_style = mmvar->namedstyle + instance_index - 1; 3364 3365 n = named_style->coords + num_coords; 3366 for ( ; i < mmvar->num_axis; i++, n++, c++ ) 3367 { 3368 if ( *c != *n ) 3369 { 3370 *c = *n; 3371 have_diff = 1; 3372 } 3373 } 3374 } 3375 else 3376 { 3377 FT_Var_Axis* a; 3378 3379 3380 a = mmvar->axis + num_coords; 3381 for ( ; i < mmvar->num_axis; i++, a++, c++ ) 3382 { 3383 if ( *c != a->def ) 3384 { 3385 *c = a->def; 3386 have_diff = 1; 3387 } 3388 } 3389 } 3390 3391 /* return value -1 indicates `no change'; */ 3392 /* we can exit early if `normalizedcoords' is already computed */ 3393 if ( blend->normalizedcoords && !have_diff ) 3394 return -1; 3395 3396 if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) ) 3397 goto Exit; 3398 3399 if ( !ttface->blend->avar_loaded ) 3400 ft_var_load_avar( ttface ); 3401 3402 FT_TRACE5(( "TT_Set_Var_Design:\n" )); 3403 FT_TRACE5(( " normalized design coordinates:\n" )); 3404 ft_var_to_normalized( ttface, num_coords, blend->coords, normalized ); 3405 3406 error = tt_set_mm_blend( ttface, mmvar->num_axis, normalized, 0 ); 3407 if ( error ) 3408 goto Exit; 3409 3410 for ( i = 0; i < num_coords; i++ ) 3411 { 3412 if ( normalized[i] ) 3413 { 3414 error = -2; /* -2 means is_variable. */ 3415 break; 3416 } 3417 } 3418 3419 Exit: 3420 FT_FREE( normalized ); 3421 return error; 3422 } 3423 3424 3425 /************************************************************************** 3426 * 3427 * @Function: 3428 * TT_Get_Var_Design 3429 * 3430 * @Description: 3431 * Get the design coordinates of the currently selected interpolated 3432 * font. 3433 * 3434 * @Input: 3435 * face :: 3436 * A handle to the source face. 3437 * 3438 * num_coords :: 3439 * The number of design coordinates to retrieve. If it 3440 * is larger than the number of axes, set the excess 3441 * values to~0. 3442 * 3443 * @Output: 3444 * coords :: 3445 * The design coordinates array. 3446 * 3447 * @Return: 3448 * FreeType error code. 0~means success. 3449 */ 3450 FT_LOCAL_DEF( FT_Error ) 3451 TT_Get_Var_Design( FT_Face face, /* TT_Face */ 3452 FT_UInt num_coords, 3453 FT_Fixed* coords ) 3454 { 3455 TT_Face ttface = (TT_Face)face; 3456 FT_Error error = FT_Err_Ok; 3457 GX_Blend blend; 3458 FT_MM_Var* mmvar; 3459 FT_Var_Axis* a; 3460 FT_UInt i, nc; 3461 3462 3463 if ( !ttface->blend ) 3464 { 3465 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 3466 return error; 3467 } 3468 3469 blend = ttface->blend; 3470 3471 if ( !blend->coords ) 3472 { 3473 /* select default instance coordinates */ 3474 /* if no instance is selected yet */ 3475 if ( FT_SET_ERROR( tt_set_mm_blend( ttface, 0, NULL, 1 ) ) ) 3476 return error; 3477 } 3478 3479 nc = num_coords; 3480 if ( num_coords > blend->num_axis ) 3481 { 3482 FT_TRACE2(( "TT_Get_Var_Design:" 3483 " only using first %u of %u coordinates\n", 3484 blend->num_axis, num_coords )); 3485 nc = blend->num_axis; 3486 } 3487 3488 mmvar = blend->mmvar; 3489 a = mmvar->axis; 3490 if ( ttface->doblend ) 3491 { 3492 for ( i = 0; i < nc; i++, a++ ) 3493 coords[i] = blend->coords[i]; 3494 } 3495 else 3496 { 3497 for ( i = 0; i < nc; i++, a++ ) 3498 coords[i] = a->def; 3499 } 3500 3501 for ( ; i < num_coords; i++, a++ ) 3502 coords[i] = a->def; 3503 3504 return FT_Err_Ok; 3505 } 3506 3507 3508 /************************************************************************** 3509 * 3510 * @Function: 3511 * TT_Set_Named_Instance 3512 * 3513 * @Description: 3514 * Set the given named instance, also resetting any further 3515 * variation. 3516 * 3517 * @Input: 3518 * face :: 3519 * A handle to the source face. 3520 * 3521 * instance_index :: 3522 * The instance index, starting with value 1. 3523 * Value 0 indicates to not use an instance. 3524 * 3525 * @Return: 3526 * FreeType error code. 0~means success, -1 means success and unchanged 3527 * axis values. 3528 */ 3529 FT_LOCAL_DEF( FT_Error ) 3530 TT_Set_Named_Instance( FT_Face face, /* TT_Face */ 3531 FT_UInt instance_index ) 3532 { 3533 TT_Face ttface = (TT_Face)face; 3534 FT_Error error; 3535 GX_Blend blend; 3536 FT_MM_Var* mmvar; 3537 3538 FT_Memory memory = FT_FACE_MEMORY( face ); 3539 3540 FT_UInt num_instances; 3541 3542 3543 if ( !ttface->blend ) 3544 { 3545 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 3546 goto Exit; 3547 } 3548 3549 blend = ttface->blend; 3550 mmvar = blend->mmvar; 3551 3552 num_instances = (FT_UInt)face->style_flags >> 16; 3553 3554 /* `instance_index' starts with value 1, thus `>' */ 3555 if ( instance_index > num_instances ) 3556 { 3557 error = FT_ERR( Invalid_Argument ); 3558 goto Exit; 3559 } 3560 3561 if ( instance_index > 0 ) 3562 { 3563 SFNT_Service sfnt = (SFNT_Service)ttface->sfnt; 3564 3565 FT_Var_Named_Style* named_style; 3566 FT_String* style_name; 3567 3568 3569 named_style = mmvar->namedstyle + instance_index - 1; 3570 3571 error = sfnt->get_name( ttface, 3572 (FT_UShort)named_style->strid, 3573 &style_name ); 3574 if ( error ) 3575 goto Exit; 3576 3577 /* set (or replace) style name */ 3578 FT_FREE( face->style_name ); 3579 face->style_name = style_name; 3580 3581 /* finally, select the named instance */ 3582 error = TT_Set_Var_Design( face, 3583 mmvar->num_axis, 3584 named_style->coords ); 3585 } 3586 else 3587 { 3588 /* restore non-VF style name */ 3589 FT_FREE( face->style_name ); 3590 if ( FT_STRDUP( face->style_name, ttface->non_var_style_name ) ) 3591 goto Exit; 3592 error = TT_Set_Var_Design( face, 0, NULL ); 3593 } 3594 3595 if ( error == -1 || error == -2 ) 3596 error = FT_Err_Ok; 3597 3598 Exit: 3599 return error; 3600 } 3601 3602 3603 /************************************************************************** 3604 * 3605 * @Function: 3606 * TT_Get_Default_Named_Instance 3607 * 3608 * @Description: 3609 * Get the default named instance. 3610 * 3611 * @Input: 3612 * face :: 3613 * A handle to the source face. 3614 * 3615 * @Output: 3616 * instance_index :: 3617 * The default named instance index. 3618 * 3619 * @Return: 3620 * FreeType error code. 0~means success. 3621 */ 3622 FT_LOCAL_DEF( FT_Error ) 3623 TT_Get_Default_Named_Instance( FT_Face face, 3624 FT_UInt *instance_index ) 3625 { 3626 TT_Face ttface = (TT_Face)face; 3627 FT_Error error = FT_Err_Ok; 3628 3629 3630 if ( !ttface->blend ) 3631 { 3632 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 3633 goto Exit; 3634 } 3635 3636 *instance_index = ttface->var_default_named_instance; 3637 3638 Exit: 3639 return error; 3640 } 3641 3642 3643 /* This function triggers (lazy) recomputation of the `postscript_name` */ 3644 /* field in `TT_Face`. */ 3645 3646 FT_LOCAL_DEF( void ) 3647 tt_construct_ps_name( FT_Face face ) 3648 { 3649 TT_Face ttface = (TT_Face)face; 3650 FT_Memory memory = FT_FACE_MEMORY( face ); 3651 3652 3653 FT_FREE( ttface->postscript_name ); 3654 } 3655 3656 3657 /*************************************************************************/ 3658 /*************************************************************************/ 3659 /***** *****/ 3660 /***** GX VAR PARSING ROUTINES *****/ 3661 /***** *****/ 3662 /*************************************************************************/ 3663 /*************************************************************************/ 3664 3665 3666 #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 3667 3668 static FT_Error 3669 tt_cvt_ready_iterator( FT_ListNode node, 3670 void* user ) 3671 { 3672 TT_Size size = (TT_Size)node->data; 3673 3674 FT_UNUSED( user ); 3675 3676 3677 size->cvt_ready = -1; 3678 3679 return FT_Err_Ok; 3680 } 3681 3682 #endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ 3683 3684 3685 3686 /************************************************************************** 3687 * 3688 * @Function: 3689 * tt_face_vary_cvt 3690 * 3691 * @Description: 3692 * Modify the loaded cvt table according to the `cvar' table and the 3693 * font's blend. 3694 * 3695 * @InOut: 3696 * face :: 3697 * A handle to the target face object. 3698 * 3699 * @Input: 3700 * stream :: 3701 * A handle to the input stream. 3702 * 3703 * @Return: 3704 * FreeType error code. 0 means success. 3705 * 3706 * Most errors are ignored. It is perfectly valid not to have a 3707 * `cvar' table even if there is a `gvar' and `fvar' table. 3708 */ 3709 FT_LOCAL_DEF( FT_Error ) 3710 tt_face_vary_cvt( TT_Face face, 3711 FT_Stream stream ) 3712 { 3713 #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 3714 3715 FT_Error error; 3716 FT_Memory memory = stream->memory; 3717 3718 FT_Face root = &face->root; 3719 3720 FT_ULong table_start; 3721 FT_ULong table_len; 3722 3723 FT_UInt tupleCount; 3724 FT_ULong offsetToData; 3725 3726 FT_ULong here; 3727 FT_UInt i, j; 3728 3729 FT_Fixed* peak_coords = NULL; 3730 FT_Fixed* tuple_coords; 3731 FT_Fixed* im_start_coords; 3732 FT_Fixed* im_end_coords; 3733 3734 GX_Blend blend = face->blend; 3735 3736 FT_UInt point_count; 3737 FT_UInt spoint_count = 0; 3738 3739 FT_UShort* sharedpoints = NULL; 3740 FT_UShort* localpoints = NULL; 3741 FT_UShort* points; 3742 3743 FT_Fixed* deltas = NULL; 3744 FT_Fixed* cvt_deltas = NULL; 3745 3746 3747 FT_TRACE2(( "CVAR " )); 3748 3749 if ( !blend ) 3750 { 3751 FT_TRACE2(( "\n" )); 3752 FT_TRACE2(( "tt_face_vary_cvt: no blend specified\n" )); 3753 3754 return FT_Err_Ok; 3755 } 3756 3757 if ( !face->cvt ) 3758 { 3759 FT_TRACE2(( "\n" )); 3760 FT_TRACE2(( "tt_face_vary_cvt: no `cvt ' table\n" )); 3761 3762 return FT_Err_Ok; 3763 } 3764 3765 error = face->goto_table( face, TTAG_cvar, stream, &table_len ); 3766 if ( error ) 3767 { 3768 FT_TRACE2(( "is missing\n" )); 3769 3770 return FT_Err_Ok; 3771 } 3772 3773 if ( FT_FRAME_ENTER( table_len ) ) 3774 return FT_Err_Ok; 3775 3776 table_start = FT_Stream_FTell( stream ); 3777 if ( FT_GET_LONG() != 0x00010000L ) 3778 { 3779 FT_TRACE2(( "bad table version\n" )); 3780 3781 error = FT_Err_Ok; 3782 goto FExit; 3783 } 3784 3785 FT_TRACE2(( "loaded\n" )); 3786 3787 tupleCount = FT_GET_USHORT(); 3788 offsetToData = FT_GET_USHORT(); 3789 3790 /* rough sanity test */ 3791 if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 > 3792 table_len ) 3793 { 3794 FT_TRACE2(( "tt_face_vary_cvt:" 3795 " invalid CVT variation array header\n" )); 3796 3797 error = FT_THROW( Invalid_Table ); 3798 goto FExit; 3799 } 3800 3801 offsetToData += table_start; 3802 3803 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS ) 3804 { 3805 here = FT_Stream_FTell( stream ); 3806 3807 FT_Stream_SeekSet( stream, offsetToData ); 3808 3809 sharedpoints = ft_var_readpackedpoints( stream, &spoint_count ); 3810 3811 offsetToData = FT_Stream_FTell( stream ); 3812 3813 FT_Stream_SeekSet( stream, here ); 3814 } 3815 3816 FT_TRACE5(( "cvar: there %s %u tuple%s:\n", 3817 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are", 3818 tupleCount & GX_TC_TUPLE_COUNT_MASK, 3819 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" )); 3820 3821 if ( FT_QNEW_ARRAY( peak_coords, 3 * blend->num_axis ) || 3822 FT_NEW_ARRAY( cvt_deltas, face->cvt_size ) ) 3823 goto Exit; 3824 3825 im_start_coords = peak_coords + blend->num_axis; 3826 im_end_coords = im_start_coords + blend->num_axis; 3827 3828 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ ) 3829 { 3830 FT_UInt tupleDataSize; 3831 FT_UInt tupleIndex; 3832 FT_Fixed apply; 3833 3834 3835 FT_TRACE6(( " tuple %u:\n", i )); 3836 3837 tupleDataSize = FT_GET_USHORT(); 3838 tupleIndex = FT_GET_USHORT(); 3839 3840 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 3841 { 3842 for ( j = 0; j < blend->num_axis; j++ ) 3843 peak_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() ); 3844 tuple_coords = peak_coords; 3845 } 3846 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) < blend->tuplecount ) 3847 tuple_coords = blend->tuplecoords + 3848 ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis; 3849 else 3850 { 3851 FT_TRACE2(( "tt_face_vary_cvt:" 3852 " invalid tuple index\n" )); 3853 3854 error = FT_THROW( Invalid_Table ); 3855 goto Exit; 3856 } 3857 3858 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 3859 { 3860 for ( j = 0; j < blend->num_axis; j++ ) 3861 im_start_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() ); 3862 for ( j = 0; j < blend->num_axis; j++ ) 3863 im_end_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() ); 3864 } 3865 3866 apply = ft_var_apply_tuple( blend, 3867 (FT_UShort)tupleIndex, 3868 tuple_coords, 3869 im_start_coords, 3870 im_end_coords ); 3871 3872 if ( apply == 0 ) /* tuple isn't active for our blend */ 3873 { 3874 offsetToData += tupleDataSize; 3875 continue; 3876 } 3877 3878 here = FT_Stream_FTell( stream ); 3879 3880 FT_Stream_SeekSet( stream, offsetToData ); 3881 3882 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) 3883 { 3884 localpoints = ft_var_readpackedpoints( stream, &point_count ); 3885 points = localpoints; 3886 } 3887 else 3888 { 3889 localpoints = NULL; 3890 points = sharedpoints; 3891 point_count = spoint_count; 3892 } 3893 3894 deltas = ft_var_readpackeddeltas( stream, 3895 point_count == 0 ? face->cvt_size 3896 : point_count ); 3897 3898 if ( !points || !deltas ) 3899 ; /* failure, ignore it */ 3900 3901 else if ( points == ALL_POINTS ) 3902 { 3903 #ifdef FT_DEBUG_LEVEL_TRACE 3904 int count = 0; 3905 #endif 3906 3907 3908 FT_TRACE7(( " CVT deltas:\n" )); 3909 3910 /* this means that there are deltas for every entry in cvt */ 3911 for ( j = 0; j < face->cvt_size; j++ ) 3912 { 3913 FT_Fixed old_cvt_delta; 3914 3915 3916 old_cvt_delta = cvt_deltas[j]; 3917 cvt_deltas[j] = old_cvt_delta + FT_MulFix( deltas[j], apply ); 3918 3919 #ifdef FT_DEBUG_LEVEL_TRACE 3920 if ( old_cvt_delta != cvt_deltas[j] ) 3921 { 3922 FT_TRACE7(( " %u: %f -> %f\n", 3923 j, 3924 (double)( FT_fdot6ToFixed( face->cvt[j] ) + 3925 old_cvt_delta ) / 65536, 3926 (double)( FT_fdot6ToFixed( face->cvt[j] ) + 3927 cvt_deltas[j] ) / 65536 )); 3928 count++; 3929 } 3930 #endif 3931 } 3932 3933 #ifdef FT_DEBUG_LEVEL_TRACE 3934 if ( !count ) 3935 FT_TRACE7(( " none\n" )); 3936 #endif 3937 } 3938 3939 else 3940 { 3941 #ifdef FT_DEBUG_LEVEL_TRACE 3942 int count = 0; 3943 #endif 3944 3945 3946 FT_TRACE7(( " CVT deltas:\n" )); 3947 3948 for ( j = 0; j < point_count; j++ ) 3949 { 3950 int pindex; 3951 FT_Fixed old_cvt_delta; 3952 3953 3954 pindex = points[j]; 3955 if ( (FT_ULong)pindex >= face->cvt_size ) 3956 continue; 3957 3958 old_cvt_delta = cvt_deltas[pindex]; 3959 cvt_deltas[pindex] = old_cvt_delta + FT_MulFix( deltas[j], apply ); 3960 3961 #ifdef FT_DEBUG_LEVEL_TRACE 3962 if ( old_cvt_delta != cvt_deltas[pindex] ) 3963 { 3964 FT_TRACE7(( " %d: %f -> %f\n", 3965 pindex, 3966 (double)( FT_fdot6ToFixed( face->cvt[pindex] ) + 3967 old_cvt_delta ) / 65536, 3968 (double)( FT_fdot6ToFixed( face->cvt[pindex] ) + 3969 cvt_deltas[pindex] ) / 65536 )); 3970 count++; 3971 } 3972 #endif 3973 } 3974 3975 #ifdef FT_DEBUG_LEVEL_TRACE 3976 if ( !count ) 3977 FT_TRACE7(( " none\n" )); 3978 #endif 3979 } 3980 3981 if ( localpoints != ALL_POINTS ) 3982 FT_FREE( localpoints ); 3983 FT_FREE( deltas ); 3984 3985 offsetToData += tupleDataSize; 3986 3987 FT_Stream_SeekSet( stream, here ); 3988 } 3989 3990 FT_TRACE5(( "\n" )); 3991 3992 for ( i = 0; i < face->cvt_size; i++ ) 3993 face->cvt[i] += FT_fixedToFdot6( cvt_deltas[i] ); 3994 3995 /* Iterate over all `FT_Size` objects and set `cvt_ready` to -1 */ 3996 /* to trigger rescaling of all CVT values. */ 3997 FT_List_Iterate( &root->sizes_list, 3998 tt_cvt_ready_iterator, 3999 NULL ); 4000 4001 Exit: 4002 if ( sharedpoints != ALL_POINTS ) 4003 FT_FREE( sharedpoints ); 4004 FT_FREE( cvt_deltas ); 4005 FT_FREE( peak_coords ); 4006 4007 FExit: 4008 FT_FRAME_EXIT(); 4009 4010 return error; 4011 4012 #else /* !TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ 4013 4014 FT_UNUSED( face ); 4015 FT_UNUSED( stream ); 4016 4017 return FT_Err_Ok; 4018 4019 #endif /* !TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ 4020 4021 } 4022 4023 4024 /* Shift the original coordinates of all points between indices `p1' */ 4025 /* and `p2', using the same difference as given by index `ref'. */ 4026 4027 /* modeled after `af_iup_shift' */ 4028 4029 static void 4030 tt_delta_shift( int p1, 4031 int p2, 4032 int ref, 4033 FT_Vector* in_points, 4034 FT_Vector* out_points ) 4035 { 4036 int p; 4037 FT_Vector delta; 4038 4039 4040 delta.x = out_points[ref].x - in_points[ref].x; 4041 delta.y = out_points[ref].y - in_points[ref].y; 4042 4043 if ( delta.x == 0 && delta.y == 0 ) 4044 return; 4045 4046 for ( p = p1; p < ref; p++ ) 4047 { 4048 out_points[p].x += delta.x; 4049 out_points[p].y += delta.y; 4050 } 4051 4052 for ( p = ref + 1; p <= p2; p++ ) 4053 { 4054 out_points[p].x += delta.x; 4055 out_points[p].y += delta.y; 4056 } 4057 } 4058 4059 4060 /* Interpolate the original coordinates of all points with indices */ 4061 /* between `p1' and `p2', using `ref1' and `ref2' as the reference */ 4062 /* point indices. */ 4063 4064 /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */ 4065 /* `Ins_IUP' with spec differences in handling ill-defined cases. */ 4066 static void 4067 tt_delta_interpolate( int p1, 4068 int p2, 4069 int ref1, 4070 int ref2, 4071 FT_Vector* in_points, 4072 FT_Vector* out_points ) 4073 { 4074 int p, i; 4075 4076 FT_Pos out, in1, in2, out1, out2, d1, d2; 4077 4078 4079 if ( p1 > p2 ) 4080 return; 4081 4082 /* handle both horizontal and vertical coordinates */ 4083 for ( i = 0; i <= 1; i++ ) 4084 { 4085 /* shift array pointers so that we can access `foo.y' as `foo.x' */ 4086 in_points = (FT_Vector*)( (FT_Pos*)in_points + i ); 4087 out_points = (FT_Vector*)( (FT_Pos*)out_points + i ); 4088 4089 if ( in_points[ref1].x > in_points[ref2].x ) 4090 { 4091 p = ref1; 4092 ref1 = ref2; 4093 ref2 = p; 4094 } 4095 4096 in1 = in_points[ref1].x; 4097 in2 = in_points[ref2].x; 4098 out1 = out_points[ref1].x; 4099 out2 = out_points[ref2].x; 4100 d1 = out1 - in1; 4101 d2 = out2 - in2; 4102 4103 /* If the reference points have the same coordinate but different */ 4104 /* delta, inferred delta is zero. Otherwise interpolate. */ 4105 if ( in1 != in2 || out1 == out2 ) 4106 { 4107 FT_Fixed scale = in1 != in2 ? FT_DivFix( out2 - out1, in2 - in1 ) 4108 : 0; 4109 4110 4111 for ( p = p1; p <= p2; p++ ) 4112 { 4113 out = in_points[p].x; 4114 4115 if ( out <= in1 ) 4116 out += d1; 4117 else if ( out >= in2 ) 4118 out += d2; 4119 else 4120 out = out1 + FT_MulFix( out - in1, scale ); 4121 4122 out_points[p].x = out; 4123 } 4124 } 4125 } 4126 } 4127 4128 4129 /* Interpolate points without delta values, similar to */ 4130 /* the `IUP' hinting instruction. */ 4131 4132 /* modeled after `Ins_IUP */ 4133 4134 static void 4135 tt_interpolate_deltas( FT_Outline* outline, 4136 FT_Vector* out_points, 4137 FT_Vector* in_points, 4138 FT_Bool* has_delta ) 4139 { 4140 FT_Int first_point; 4141 FT_Int end_point; 4142 4143 FT_Int first_delta; 4144 FT_Int cur_delta; 4145 4146 FT_Int point; 4147 FT_Short contour; 4148 4149 4150 /* ignore empty outlines */ 4151 if ( !outline->n_contours ) 4152 return; 4153 4154 contour = 0; 4155 point = 0; 4156 4157 do 4158 { 4159 end_point = outline->contours[contour]; 4160 first_point = point; 4161 4162 /* search first point that has a delta */ 4163 while ( point <= end_point && !has_delta[point] ) 4164 point++; 4165 4166 if ( point <= end_point ) 4167 { 4168 first_delta = point; 4169 cur_delta = point; 4170 4171 point++; 4172 4173 while ( point <= end_point ) 4174 { 4175 /* search next point that has a delta */ 4176 /* and interpolate intermediate points */ 4177 if ( has_delta[point] ) 4178 { 4179 tt_delta_interpolate( cur_delta + 1, 4180 point - 1, 4181 cur_delta, 4182 point, 4183 in_points, 4184 out_points ); 4185 cur_delta = point; 4186 } 4187 4188 point++; 4189 } 4190 4191 /* shift contour if we only have a single delta */ 4192 if ( cur_delta == first_delta ) 4193 tt_delta_shift( first_point, 4194 end_point, 4195 cur_delta, 4196 in_points, 4197 out_points ); 4198 else 4199 { 4200 /* otherwise handle remaining points */ 4201 /* at the end and beginning of the contour */ 4202 tt_delta_interpolate( cur_delta + 1, 4203 end_point, 4204 cur_delta, 4205 first_delta, 4206 in_points, 4207 out_points ); 4208 4209 if ( first_delta > 0 ) 4210 tt_delta_interpolate( first_point, 4211 first_delta - 1, 4212 cur_delta, 4213 first_delta, 4214 in_points, 4215 out_points ); 4216 } 4217 } 4218 contour++; 4219 4220 } while ( contour < outline->n_contours ); 4221 } 4222 4223 4224 /************************************************************************** 4225 * 4226 * @Function: 4227 * TT_Vary_Apply_Glyph_Deltas 4228 * 4229 * @Description: 4230 * Apply the appropriate deltas to the current glyph. 4231 * 4232 * @InOut: 4233 * loader :: 4234 * A handle to the loader object. 4235 * 4236 * outline :: 4237 * The outline to change, with appended phantom points. 4238 * 4239 * @Output: 4240 * unrounded :: 4241 * An array with `n_points' elements that is filled with unrounded 4242 * point coordinates (in 26.6 format). 4243 * 4244 * @Return: 4245 * FreeType error code. 0 means success. 4246 */ 4247 FT_LOCAL_DEF( FT_Error ) 4248 TT_Vary_Apply_Glyph_Deltas( TT_Loader loader, 4249 FT_Outline* outline, 4250 FT_Vector* unrounded ) 4251 { 4252 FT_Error error = FT_Err_Ok; 4253 TT_Face face = loader->face; 4254 FT_Stream stream = face->root.stream; 4255 FT_Memory memory = stream->memory; 4256 FT_UInt glyph_index = loader->glyph_index; 4257 FT_UInt n_points = (FT_UInt)outline->n_points + 4; 4258 4259 FT_Vector* points_org = NULL; /* coordinates in 16.16 format */ 4260 FT_Vector* points_out = NULL; /* coordinates in 16.16 format */ 4261 FT_Bool* has_delta = NULL; 4262 4263 FT_ULong glyph_start; 4264 4265 FT_UInt tupleCount; 4266 FT_ULong offsetToData; 4267 FT_ULong dataSize; 4268 4269 FT_ULong here; 4270 FT_UInt i, j; 4271 4272 FT_UInt peak_coords_size; 4273 FT_UInt point_deltas_x_size; 4274 FT_UInt points_org_size; 4275 FT_UInt points_out_size; 4276 FT_UInt has_delta_size; 4277 FT_UInt pool_size; 4278 FT_Byte* pool = NULL; 4279 FT_Byte* p; 4280 4281 FT_Fixed* peak_coords = NULL; 4282 FT_Fixed* tuple_coords; 4283 FT_Fixed* im_start_coords; 4284 FT_Fixed* im_end_coords; 4285 4286 GX_Blend blend = face->blend; 4287 4288 FT_UInt point_count; 4289 FT_UInt spoint_count = 0; 4290 4291 FT_UShort* sharedpoints = NULL; 4292 FT_UShort* localpoints = NULL; 4293 FT_UShort* points; 4294 4295 FT_Fixed* deltas_x = NULL; 4296 FT_Fixed* deltas_y = NULL; 4297 FT_Fixed* point_deltas_x = NULL; 4298 FT_Fixed* point_deltas_y = NULL; 4299 4300 4301 for ( i = 0; i < n_points; i++ ) 4302 { 4303 unrounded[i].x = INT_TO_F26DOT6( outline->points[i].x ); 4304 unrounded[i].y = INT_TO_F26DOT6( outline->points[i].y ); 4305 } 4306 4307 if ( !face->doblend ) 4308 goto Exit; 4309 4310 if ( !blend ) 4311 return FT_THROW( Invalid_Argument ); 4312 4313 if ( glyph_index >= blend->gv_glyphcnt || 4314 blend->glyphoffsets[glyph_index] == 4315 blend->glyphoffsets[glyph_index + 1] ) 4316 { 4317 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 4318 " no variation data for glyph %u\n", glyph_index )); 4319 return FT_Err_Ok; 4320 } 4321 4322 dataSize = blend->glyphoffsets[glyph_index + 1] - 4323 blend->glyphoffsets[glyph_index]; 4324 4325 if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) || 4326 FT_FRAME_ENTER( dataSize ) ) 4327 return error; 4328 4329 glyph_start = FT_Stream_FTell( stream ); 4330 4331 /* each set of glyph variation data is formatted similarly to `cvar' */ 4332 4333 tupleCount = FT_GET_USHORT(); 4334 offsetToData = FT_GET_USHORT(); 4335 4336 /* rough sanity test */ 4337 if ( offsetToData > dataSize || 4338 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 > dataSize ) 4339 { 4340 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 4341 " invalid glyph variation array header\n" )); 4342 4343 error = FT_THROW( Invalid_Table ); 4344 goto FExit; 4345 } 4346 4347 offsetToData += glyph_start; 4348 4349 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS ) 4350 { 4351 here = FT_Stream_FTell( stream ); 4352 4353 FT_Stream_SeekSet( stream, offsetToData ); 4354 4355 sharedpoints = ft_var_readpackedpoints( stream, &spoint_count ); 4356 4357 offsetToData = FT_Stream_FTell( stream ); 4358 4359 FT_Stream_SeekSet( stream, here ); 4360 } 4361 4362 FT_TRACE5(( "gvar: there %s %u tuple%s:\n", 4363 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are", 4364 tupleCount & GX_TC_TUPLE_COUNT_MASK, 4365 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" )); 4366 4367 peak_coords_size = ALIGN_SIZE( 3 * blend->num_axis * 4368 sizeof ( *peak_coords ) ); 4369 point_deltas_x_size = ALIGN_SIZE( 2 * n_points * 4370 sizeof ( *point_deltas_x ) ); 4371 points_org_size = ALIGN_SIZE( n_points * sizeof ( *points_org ) ); 4372 points_out_size = ALIGN_SIZE( n_points * sizeof ( *points_out ) ); 4373 has_delta_size = ALIGN_SIZE( n_points * sizeof ( *has_delta ) ); 4374 4375 pool_size = peak_coords_size + 4376 point_deltas_x_size + 4377 points_org_size + 4378 points_out_size + 4379 has_delta_size; 4380 4381 if ( FT_ALLOC( pool, pool_size ) ) 4382 goto Exit; 4383 4384 p = pool; 4385 peak_coords = (FT_Fixed*)p; 4386 p += peak_coords_size; 4387 point_deltas_x = (FT_Fixed*)p; 4388 p += point_deltas_x_size; 4389 points_org = (FT_Vector*)p; 4390 p += points_org_size; 4391 points_out = (FT_Vector*)p; 4392 p += points_out_size; 4393 has_delta = (FT_Bool*)p; 4394 4395 FT_ARRAY_ZERO( point_deltas_x, 2 * n_points ); 4396 4397 im_start_coords = peak_coords + blend->num_axis; 4398 im_end_coords = im_start_coords + blend->num_axis; 4399 point_deltas_y = point_deltas_x + n_points; 4400 4401 for ( j = 0; j < n_points; j++ ) 4402 { 4403 points_org[j].x = FT_intToFixed( outline->points[j].x ); 4404 points_org[j].y = FT_intToFixed( outline->points[j].y ); 4405 } 4406 4407 p = stream->cursor; 4408 4409 tupleCount &= GX_TC_TUPLE_COUNT_MASK; 4410 for ( i = 0; i < tupleCount; i++ ) 4411 { 4412 FT_UInt tupleDataSize; 4413 FT_UInt tupleIndex; 4414 FT_Fixed apply; 4415 FT_Fixed* tupleScalars; 4416 4417 4418 FT_TRACE6(( " tuple %u:\n", i )); 4419 4420 tupleScalars = blend->tuplescalars; 4421 4422 /* Enter frame for four bytes. */ 4423 if ( 4 > stream->limit - p ) 4424 { 4425 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 4426 " invalid glyph variation array header\n" )); 4427 error = FT_THROW( Invalid_Table ); 4428 goto Exit; 4429 } 4430 4431 tupleDataSize = FT_NEXT_USHORT( p ); 4432 tupleIndex = FT_NEXT_USHORT( p ); 4433 4434 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 4435 tupleScalars = NULL; 4436 4437 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 4438 { 4439 if ( 2 * blend->num_axis > (FT_UInt)( stream->limit - p ) ) 4440 { 4441 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 4442 " invalid glyph variation array header\n" )); 4443 error = FT_THROW( Invalid_Table ); 4444 goto Exit; 4445 } 4446 4447 for ( j = 0; j < blend->num_axis; j++ ) 4448 peak_coords[j] = FT_fdot14ToFixed( FT_NEXT_SHORT( p ) ); 4449 4450 tuple_coords = peak_coords; 4451 tupleScalars = NULL; 4452 } 4453 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) < blend->tuplecount ) 4454 { 4455 FT_Fixed scalar = 4456 tupleScalars 4457 ? tupleScalars[tupleIndex & GX_TI_TUPLE_INDEX_MASK] 4458 : (FT_Fixed)-0x20000; 4459 4460 4461 if ( scalar != (FT_Fixed)-0x20000 ) 4462 { 4463 apply = scalar; 4464 goto apply_found; 4465 } 4466 4467 tuple_coords = blend->tuplecoords + 4468 ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * 4469 blend->num_axis; 4470 } 4471 else 4472 { 4473 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 4474 " invalid tuple index\n" )); 4475 4476 error = FT_THROW( Invalid_Table ); 4477 goto Exit; 4478 } 4479 4480 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 4481 { 4482 if ( 4 * blend->num_axis > (FT_UInt)( stream->limit - p ) ) 4483 { 4484 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 4485 " invalid glyph variation array header\n" )); 4486 error = FT_THROW( Invalid_Table ); 4487 goto Exit; 4488 } 4489 4490 for ( j = 0; j < blend->num_axis; j++ ) 4491 im_start_coords[j] = FT_fdot14ToFixed( FT_NEXT_SHORT( p ) ); 4492 for ( j = 0; j < blend->num_axis; j++ ) 4493 im_end_coords[j] = FT_fdot14ToFixed( FT_NEXT_SHORT( p ) ); 4494 } 4495 4496 apply = ft_var_apply_tuple( blend, 4497 (FT_UShort)tupleIndex, 4498 tuple_coords, 4499 im_start_coords, 4500 im_end_coords ); 4501 4502 if ( tupleScalars ) 4503 tupleScalars[tupleIndex & GX_TI_TUPLE_INDEX_MASK] = apply; 4504 4505 apply_found: 4506 4507 if ( apply == 0 ) /* tuple isn't active for our blend */ 4508 { 4509 offsetToData += tupleDataSize; 4510 continue; 4511 } 4512 4513 here = FT_Stream_FTell( stream ); 4514 4515 FT_Stream_SeekSet( stream, offsetToData ); 4516 4517 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) 4518 { 4519 localpoints = ft_var_readpackedpoints( stream, &point_count ); 4520 points = localpoints; 4521 } 4522 else 4523 { 4524 points = sharedpoints; 4525 point_count = spoint_count; 4526 } 4527 4528 deltas_x = ft_var_readpackeddeltas( stream, 4529 point_count == 0 ? n_points 4530 : point_count ); 4531 deltas_y = ft_var_readpackeddeltas( stream, 4532 point_count == 0 ? n_points 4533 : point_count ); 4534 4535 if ( !points || !deltas_y || !deltas_x ) 4536 ; /* failure, ignore it */ 4537 4538 else if ( points == ALL_POINTS ) 4539 { 4540 #ifdef FT_DEBUG_LEVEL_TRACE 4541 int count = 0; 4542 #endif 4543 4544 4545 FT_TRACE7(( " point deltas:\n" )); 4546 4547 /* this means that there are deltas for every point in the glyph */ 4548 for ( j = 0; j < n_points; j++ ) 4549 { 4550 FT_Fixed old_point_delta_x = point_deltas_x[j]; 4551 FT_Fixed old_point_delta_y = point_deltas_y[j]; 4552 4553 FT_Fixed point_delta_x = FT_MulFix( deltas_x[j], apply ); 4554 FT_Fixed point_delta_y = FT_MulFix( deltas_y[j], apply ); 4555 4556 4557 point_deltas_x[j] = old_point_delta_x + point_delta_x; 4558 point_deltas_y[j] = old_point_delta_y + point_delta_y; 4559 4560 #ifdef FT_DEBUG_LEVEL_TRACE 4561 if ( point_delta_x || point_delta_y ) 4562 { 4563 FT_TRACE7(( " %u: (%f, %f) -> (%f, %f)\n", 4564 j, 4565 (double)( FT_intToFixed( outline->points[j].x ) + 4566 old_point_delta_x ) / 65536, 4567 (double)( FT_intToFixed( outline->points[j].y ) + 4568 old_point_delta_y ) / 65536, 4569 (double)( FT_intToFixed( outline->points[j].x ) + 4570 point_deltas_x[j] ) / 65536, 4571 (double)( FT_intToFixed( outline->points[j].y ) + 4572 point_deltas_y[j] ) / 65536 )); 4573 count++; 4574 } 4575 #endif 4576 } 4577 4578 #ifdef FT_DEBUG_LEVEL_TRACE 4579 if ( !count ) 4580 FT_TRACE7(( " none\n" )); 4581 #endif 4582 } 4583 4584 else 4585 { 4586 #ifdef FT_DEBUG_LEVEL_TRACE 4587 int count = 0; 4588 #endif 4589 4590 4591 /* we have to interpolate the missing deltas similar to the */ 4592 /* IUP bytecode instruction */ 4593 for ( j = 0; j < n_points; j++ ) 4594 { 4595 has_delta[j] = FALSE; 4596 points_out[j] = points_org[j]; 4597 } 4598 4599 for ( j = 0; j < point_count; j++ ) 4600 { 4601 FT_UShort idx = points[j]; 4602 4603 4604 if ( idx >= n_points ) 4605 continue; 4606 4607 has_delta[idx] = TRUE; 4608 4609 points_out[idx].x += FT_MulFix( deltas_x[j], apply ); 4610 points_out[idx].y += FT_MulFix( deltas_y[j], apply ); 4611 } 4612 4613 /* no need to handle phantom points here, */ 4614 /* since solitary points can't be interpolated */ 4615 tt_interpolate_deltas( outline, 4616 points_out, 4617 points_org, 4618 has_delta ); 4619 4620 FT_TRACE7(( " point deltas:\n" )); 4621 4622 for ( j = 0; j < n_points; j++ ) 4623 { 4624 FT_Fixed old_point_delta_x = point_deltas_x[j]; 4625 FT_Fixed old_point_delta_y = point_deltas_y[j]; 4626 4627 FT_Pos point_delta_x = points_out[j].x - points_org[j].x; 4628 FT_Pos point_delta_y = points_out[j].y - points_org[j].y; 4629 4630 4631 point_deltas_x[j] = old_point_delta_x + point_delta_x; 4632 point_deltas_y[j] = old_point_delta_y + point_delta_y; 4633 4634 #ifdef FT_DEBUG_LEVEL_TRACE 4635 if ( point_delta_x || point_delta_y ) 4636 { 4637 FT_TRACE7(( " %u: (%f, %f) -> (%f, %f)\n", 4638 j, 4639 (double)( FT_intToFixed( outline->points[j].x ) + 4640 old_point_delta_x ) / 65536, 4641 (double)( FT_intToFixed( outline->points[j].y ) + 4642 old_point_delta_y ) / 65536, 4643 (double)( FT_intToFixed( outline->points[j].x ) + 4644 point_deltas_x[j] ) / 65536, 4645 (double)( FT_intToFixed( outline->points[j].y ) + 4646 point_deltas_y[j] ) / 65536 )); 4647 count++; 4648 } 4649 #endif 4650 } 4651 4652 #ifdef FT_DEBUG_LEVEL_TRACE 4653 if ( !count ) 4654 FT_TRACE7(( " none\n" )); 4655 #endif 4656 } 4657 4658 if ( localpoints != ALL_POINTS ) 4659 FT_FREE( localpoints ); 4660 FT_FREE( deltas_x ); 4661 FT_FREE( deltas_y ); 4662 4663 offsetToData += tupleDataSize; 4664 4665 FT_Stream_SeekSet( stream, here ); 4666 } 4667 4668 FT_TRACE5(( "\n" )); 4669 4670 /* To avoid double adjustment of advance width or height, */ 4671 /* do not move phantom points if there is HVAR or VVAR */ 4672 /* support, respectively. */ 4673 if ( face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) 4674 { 4675 point_deltas_x[n_points - 4] = 0; 4676 point_deltas_y[n_points - 4] = 0; 4677 point_deltas_x[n_points - 3] = 0; 4678 point_deltas_y[n_points - 3] = 0; 4679 } 4680 if ( face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) 4681 { 4682 point_deltas_x[n_points - 2] = 0; 4683 point_deltas_y[n_points - 2] = 0; 4684 point_deltas_x[n_points - 1] = 0; 4685 point_deltas_y[n_points - 1] = 0; 4686 } 4687 4688 for ( i = 0; i < n_points; i++ ) 4689 { 4690 unrounded[i].x += FT_fixedToFdot6( point_deltas_x[i] ); 4691 unrounded[i].y += FT_fixedToFdot6( point_deltas_y[i] ); 4692 4693 outline->points[i].x += FT_fixedToInt( point_deltas_x[i] ); 4694 outline->points[i].y += FT_fixedToInt( point_deltas_y[i] ); 4695 } 4696 4697 /* To avoid double adjustment of advance width or height, */ 4698 /* adjust phantom points only if there is no HVAR or VVAR */ 4699 /* support, respectively. */ 4700 if ( !( face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) ) 4701 { 4702 loader->pp1 = outline->points[n_points - 4]; 4703 loader->pp2 = outline->points[n_points - 3]; 4704 loader->linear = FT_PIX_ROUND( unrounded[n_points - 3].x - 4705 unrounded[n_points - 4].x ) / 64; 4706 } 4707 if ( !( face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) ) 4708 { 4709 loader->pp3 = outline->points[n_points - 2]; 4710 loader->pp4 = outline->points[n_points - 1]; 4711 loader->vadvance = FT_PIX_ROUND( unrounded[n_points - 1].y - 4712 unrounded[n_points - 2].y ) / 64; 4713 } 4714 4715 Exit: 4716 if ( sharedpoints != ALL_POINTS ) 4717 FT_FREE( sharedpoints ); 4718 FT_FREE( pool ); 4719 4720 FExit: 4721 FT_FRAME_EXIT(); 4722 4723 return error; 4724 } 4725 4726 4727 /************************************************************************** 4728 * 4729 * @Function: 4730 * tt_get_var_blend 4731 * 4732 * @Description: 4733 * An extended internal version of `TT_Get_MM_Blend' that returns 4734 * pointers instead of copying data, without any initialization of 4735 * the MM machinery in case it isn't loaded yet. 4736 */ 4737 FT_LOCAL_DEF( FT_Error ) 4738 tt_get_var_blend( FT_Face face, /* TT_Face */ 4739 FT_UInt *num_coords, 4740 FT_Fixed* *coords, 4741 FT_Fixed* *normalizedcoords, 4742 FT_MM_Var* *mm_var ) 4743 { 4744 TT_Face ttface = (TT_Face)face; 4745 4746 4747 if ( ttface->blend ) 4748 { 4749 if ( num_coords ) 4750 *num_coords = ttface->blend->num_axis; 4751 if ( coords ) 4752 *coords = ttface->blend->coords; 4753 if ( normalizedcoords ) 4754 *normalizedcoords = ttface->blend->normalizedcoords; 4755 if ( mm_var ) 4756 *mm_var = ttface->blend->mmvar; 4757 } 4758 else 4759 { 4760 if ( num_coords ) 4761 *num_coords = 0; 4762 if ( coords ) 4763 *coords = NULL; 4764 if ( mm_var ) 4765 *mm_var = NULL; 4766 } 4767 4768 return FT_Err_Ok; 4769 } 4770 4771 4772 FT_LOCAL_DEF( void ) 4773 tt_var_done_item_variation_store( FT_Face face, 4774 GX_ItemVarStore itemStore ) 4775 { 4776 FT_Memory memory = FT_FACE_MEMORY( face ); 4777 FT_UInt i; 4778 4779 4780 if ( itemStore->varData ) 4781 { 4782 for ( i = 0; i < itemStore->dataCount; i++ ) 4783 { 4784 FT_FREE( itemStore->varData[i].regionIndices ); 4785 FT_FREE( itemStore->varData[i].deltaSet ); 4786 } 4787 4788 FT_FREE( itemStore->varData ); 4789 } 4790 4791 if ( itemStore->varRegionList ) 4792 { 4793 for ( i = 0; i < itemStore->regionCount; i++ ) 4794 FT_FREE( itemStore->varRegionList[i].axisList ); 4795 4796 FT_FREE( itemStore->varRegionList ); 4797 } 4798 } 4799 4800 4801 FT_LOCAL_DEF( void ) 4802 tt_var_done_delta_set_index_map( FT_Face face, 4803 GX_DeltaSetIdxMap deltaSetIdxMap ) 4804 { 4805 FT_Memory memory = FT_FACE_MEMORY( face ); 4806 4807 4808 FT_FREE( deltaSetIdxMap->innerIndex ); 4809 FT_FREE( deltaSetIdxMap->outerIndex ); 4810 } 4811 4812 4813 /************************************************************************** 4814 * 4815 * @Function: 4816 * tt_done_blend 4817 * 4818 * @Description: 4819 * Free the blend internal data structure. 4820 */ 4821 FT_LOCAL_DEF( void ) 4822 tt_done_blend( FT_Face face ) 4823 { 4824 TT_Face ttface = (TT_Face)face; 4825 FT_Memory memory = FT_FACE_MEMORY( face ); 4826 GX_Blend blend = ttface->blend; 4827 4828 4829 if ( blend ) 4830 { 4831 FT_UInt i, num_axes; 4832 4833 4834 /* blend->num_axis might not be set up yet */ 4835 num_axes = blend->mmvar->num_axis; 4836 4837 FT_FREE( blend->coords ); 4838 FT_FREE( blend->normalizedcoords ); 4839 FT_FREE( blend->normalized_stylecoords ); 4840 FT_FREE( blend->mmvar ); 4841 4842 if ( blend->avar_table ) 4843 { 4844 if ( blend->avar_table->avar_segment ) 4845 { 4846 for ( i = 0; i < num_axes; i++ ) 4847 FT_FREE( blend->avar_table->avar_segment[i].correspondence ); 4848 FT_FREE( blend->avar_table->avar_segment ); 4849 } 4850 4851 tt_var_done_item_variation_store( face, 4852 &blend->avar_table->itemStore ); 4853 4854 tt_var_done_delta_set_index_map( face, 4855 &blend->avar_table->axisMap ); 4856 4857 FT_FREE( blend->avar_table ); 4858 } 4859 4860 if ( blend->hvar_table ) 4861 { 4862 tt_var_done_item_variation_store( face, 4863 &blend->hvar_table->itemStore ); 4864 4865 tt_var_done_delta_set_index_map( face, 4866 &blend->hvar_table->widthMap ); 4867 FT_FREE( blend->hvar_table ); 4868 } 4869 4870 if ( blend->vvar_table ) 4871 { 4872 tt_var_done_item_variation_store( face, 4873 &blend->vvar_table->itemStore ); 4874 4875 tt_var_done_delta_set_index_map( face, 4876 &blend->vvar_table->widthMap ); 4877 FT_FREE( blend->vvar_table ); 4878 } 4879 4880 if ( blend->mvar_table ) 4881 { 4882 tt_var_done_item_variation_store( face, 4883 &blend->mvar_table->itemStore ); 4884 4885 FT_FREE( blend->mvar_table->values ); 4886 FT_FREE( blend->mvar_table ); 4887 } 4888 4889 FT_FREE( blend->tuplescalars ); 4890 FT_FREE( blend->tuplecoords ); 4891 FT_FREE( blend->glyphoffsets ); 4892 FT_FREE( blend ); 4893 } 4894 } 4895 4896 #else /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */ 4897 4898 /* ANSI C doesn't like empty source files */ 4899 typedef int tt_gxvar_dummy_; 4900 4901 #endif /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */ 4902 4903 4904 /* END */