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