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