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