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