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 ft_size_reset_iterator(FT_ListNode node,void * user)1384 ft_size_reset_iterator( FT_ListNode node, 1385 void* user ) 1386 { 1387 FT_Size size = (FT_Size)node->data; 1388 FT_Service_MetricsVariations var = (FT_Service_MetricsVariations)user; 1389 1390 1391 var->size_reset( size ); 1392 1393 return FT_Err_Ok; 1394 } 1395 1396 1397 /************************************************************************** 1398 * 1399 * @Function: 1400 * tt_apply_mvar 1401 * 1402 * @Description: 1403 * Apply `MVAR' table adjustments. 1404 * 1405 * @InOut: 1406 * face :: 1407 * The font face. 1408 */ 1409 FT_LOCAL_DEF( void ) tt_apply_mvar(TT_Face face)1410 tt_apply_mvar( TT_Face face ) 1411 { 1412 GX_Blend blend = face->blend; 1413 GX_Value value, limit; 1414 FT_Short mvar_hasc_delta = 0; 1415 FT_Short mvar_hdsc_delta = 0; 1416 FT_Short mvar_hlgp_delta = 0; 1417 1418 1419 if ( !( face->variation_support & TT_FACE_FLAG_VAR_MVAR ) ) 1420 return; 1421 1422 value = blend->mvar_table->values; 1423 limit = value + blend->mvar_table->valueCount; 1424 1425 for ( ; value < limit; value++ ) 1426 { 1427 FT_Short* p = ft_var_get_value_pointer( face, value->tag ); 1428 FT_Int delta; 1429 1430 1431 delta = ft_var_get_item_delta( face, 1432 &blend->mvar_table->itemStore, 1433 value->outerIndex, 1434 value->innerIndex ); 1435 1436 if ( p ) 1437 { 1438 FT_TRACE5(( "value %c%c%c%c (%d unit%s) adjusted by %d unit%s (MVAR)\n", 1439 (FT_Char)( value->tag >> 24 ), 1440 (FT_Char)( value->tag >> 16 ), 1441 (FT_Char)( value->tag >> 8 ), 1442 (FT_Char)( value->tag ), 1443 value->unmodified, 1444 value->unmodified == 1 ? "" : "s", 1445 delta, 1446 delta == 1 ? "" : "s" )); 1447 1448 /* since we handle both signed and unsigned values as FT_Short, */ 1449 /* ensure proper overflow arithmetic */ 1450 *p = (FT_Short)( value->unmodified + (FT_Short)delta ); 1451 1452 /* Treat hasc, hdsc and hlgp specially, see below. */ 1453 if ( value->tag == MVAR_TAG_HASC ) 1454 mvar_hasc_delta = (FT_Short)delta; 1455 else if ( value->tag == MVAR_TAG_HDSC ) 1456 mvar_hdsc_delta = (FT_Short)delta; 1457 else if ( value->tag == MVAR_TAG_HLGP ) 1458 mvar_hlgp_delta = (FT_Short)delta; 1459 } 1460 } 1461 1462 /* adjust all derived values */ 1463 { 1464 FT_Service_MetricsVariations var = 1465 (FT_Service_MetricsVariations)face->face_var; 1466 1467 FT_Face root = &face->root; 1468 1469 /* 1470 * Apply the deltas of hasc, hdsc and hlgp to the FT_Face's ascender, 1471 * descender and height attributes, no matter how they were originally 1472 * computed. 1473 * 1474 * (Code that ignores those and accesses the font's metrics values 1475 * directly is already served by the delta application code above.) 1476 * 1477 * The MVAR table supports variations for both typo and win metrics. 1478 * According to Behdad Esfahbod, the thinking of the working group was 1479 * that no one uses win metrics anymore for setting line metrics (the 1480 * specification even calls these metrics "horizontal clipping 1481 * ascent/descent", probably for their role on the Windows platform in 1482 * computing clipping boxes), and new fonts should use typo metrics, so 1483 * typo deltas should be applied to whatever sfnt_load_face decided the 1484 * line metrics should be. 1485 * 1486 * Before, the following led to different line metrics between default 1487 * outline and instances, visible when e.g. the default outlines were 1488 * used as the regular face and instances for everything else: 1489 * 1490 * 1. sfnt_load_face applied the hhea metrics by default. 1491 * 2. This code later applied the typo metrics by default, regardless of 1492 * whether they were actually changed or the font had the OS/2 table's 1493 * fsSelection's bit 7 (USE_TYPO_METRICS) set. 1494 */ 1495 FT_Short current_line_gap = root->height - root->ascender + 1496 root->descender; 1497 1498 1499 root->ascender = root->ascender + mvar_hasc_delta; 1500 root->descender = root->descender + mvar_hdsc_delta; 1501 root->height = root->ascender - root->descender + 1502 current_line_gap + mvar_hlgp_delta; 1503 1504 root->underline_position = face->postscript.underlinePosition - 1505 face->postscript.underlineThickness / 2; 1506 root->underline_thickness = face->postscript.underlineThickness; 1507 1508 /* iterate over all FT_Size objects and call `var->size_reset' */ 1509 /* to propagate the metrics changes */ 1510 if ( var && var->size_reset ) 1511 FT_List_Iterate( &root->sizes_list, 1512 ft_size_reset_iterator, 1513 (void*)var ); 1514 } 1515 } 1516 1517 1518 typedef struct GX_GVar_Head_ 1519 { 1520 FT_Long version; 1521 FT_UShort axisCount; 1522 FT_UShort globalCoordCount; 1523 FT_ULong offsetToCoord; 1524 FT_UShort glyphCount; 1525 FT_UShort flags; 1526 FT_ULong offsetToData; 1527 1528 } GX_GVar_Head; 1529 1530 1531 /************************************************************************** 1532 * 1533 * @Function: 1534 * ft_var_load_gvar 1535 * 1536 * @Description: 1537 * Parse the `gvar' table if present. If `fvar' is there, `gvar' had 1538 * better be there too. 1539 * 1540 * @InOut: 1541 * face :: 1542 * The font face. 1543 * 1544 * @Return: 1545 * FreeType error code. 0 means success. 1546 */ 1547 static FT_Error ft_var_load_gvar(TT_Face face)1548 ft_var_load_gvar( TT_Face face ) 1549 { 1550 FT_Stream stream = FT_FACE_STREAM( face ); 1551 FT_Memory memory = stream->memory; 1552 GX_Blend blend = face->blend; 1553 FT_Error error; 1554 FT_UInt i, j; 1555 FT_ULong table_len; 1556 FT_ULong gvar_start; 1557 FT_ULong offsetToData; 1558 FT_ULong offsets_len; 1559 GX_GVar_Head gvar_head; 1560 1561 static const FT_Frame_Field gvar_fields[] = 1562 { 1563 1564 #undef FT_STRUCTURE 1565 #define FT_STRUCTURE GX_GVar_Head 1566 1567 FT_FRAME_START( 20 ), 1568 FT_FRAME_LONG ( version ), 1569 FT_FRAME_USHORT( axisCount ), 1570 FT_FRAME_USHORT( globalCoordCount ), 1571 FT_FRAME_ULONG ( offsetToCoord ), 1572 FT_FRAME_USHORT( glyphCount ), 1573 FT_FRAME_USHORT( flags ), 1574 FT_FRAME_ULONG ( offsetToData ), 1575 FT_FRAME_END 1576 }; 1577 1578 1579 FT_TRACE2(( "GVAR " )); 1580 1581 if ( FT_SET_ERROR( face->goto_table( face, 1582 TTAG_gvar, 1583 stream, 1584 &table_len ) ) ) 1585 { 1586 FT_TRACE2(( "is missing\n" )); 1587 goto Exit; 1588 } 1589 1590 gvar_start = FT_STREAM_POS( ); 1591 if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) ) 1592 goto Exit; 1593 1594 if ( gvar_head.version != 0x00010000L ) 1595 { 1596 FT_TRACE1(( "bad table version\n" )); 1597 error = FT_THROW( Invalid_Table ); 1598 goto Exit; 1599 } 1600 1601 if ( gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis ) 1602 { 1603 FT_TRACE1(( "ft_var_load_gvar:" 1604 " number of axes in `gvar' and `cvar'\n" )); 1605 FT_TRACE1(( " table are different\n" )); 1606 error = FT_THROW( Invalid_Table ); 1607 goto Exit; 1608 } 1609 1610 /* rough sanity check, ignoring offsets */ 1611 if ( (FT_ULong)gvar_head.globalCoordCount * gvar_head.axisCount > 1612 table_len / 2 ) 1613 { 1614 FT_TRACE1(( "ft_var_load_gvar:" 1615 " invalid number of global coordinates\n" )); 1616 error = FT_THROW( Invalid_Table ); 1617 goto Exit; 1618 } 1619 1620 /* offsets can be either 2 or 4 bytes */ 1621 /* (one more offset than glyphs, to mark size of last) */ 1622 offsets_len = ( gvar_head.glyphCount + 1 ) * 1623 ( ( gvar_head.flags & 1 ) ? 4L : 2L ); 1624 1625 /* rough sanity check */ 1626 if (offsets_len > table_len ) 1627 { 1628 FT_TRACE1(( "ft_var_load_gvar: invalid number of glyphs\n" )); 1629 error = FT_THROW( Invalid_Table ); 1630 goto Exit; 1631 } 1632 1633 FT_TRACE2(( "loaded\n" )); 1634 1635 blend->gvar_size = table_len; 1636 offsetToData = gvar_start + gvar_head.offsetToData; 1637 1638 FT_TRACE5(( "gvar: there %s %d shared coordinate%s:\n", 1639 gvar_head.globalCoordCount == 1 ? "is" : "are", 1640 gvar_head.globalCoordCount, 1641 gvar_head.globalCoordCount == 1 ? "" : "s" )); 1642 1643 if ( FT_FRAME_ENTER( offsets_len ) ) 1644 goto Exit; 1645 1646 /* offsets (one more offset than glyphs, to mark size of last) */ 1647 if ( FT_QNEW_ARRAY( blend->glyphoffsets, gvar_head.glyphCount + 1 ) ) 1648 goto Fail2; 1649 1650 if ( gvar_head.flags & 1 ) 1651 { 1652 FT_ULong limit = gvar_start + table_len; 1653 FT_ULong max_offset = 0; 1654 1655 1656 for ( i = 0; i <= gvar_head.glyphCount; i++ ) 1657 { 1658 blend->glyphoffsets[i] = offsetToData + FT_GET_ULONG(); 1659 1660 if ( max_offset <= blend->glyphoffsets[i] ) 1661 max_offset = blend->glyphoffsets[i]; 1662 else 1663 { 1664 FT_TRACE2(( "ft_var_load_gvar:" 1665 " glyph variation data offset %d not monotonic\n", 1666 i )); 1667 blend->glyphoffsets[i] = max_offset; 1668 } 1669 1670 /* use `<', not `<=' */ 1671 if ( limit < blend->glyphoffsets[i] ) 1672 { 1673 FT_TRACE2(( "ft_var_load_gvar:" 1674 " glyph variation data offset %d out of range\n", 1675 i )); 1676 blend->glyphoffsets[i] = limit; 1677 } 1678 } 1679 } 1680 else 1681 { 1682 FT_ULong limit = gvar_start + table_len; 1683 FT_ULong max_offset = 0; 1684 1685 1686 for ( i = 0; i <= gvar_head.glyphCount; i++ ) 1687 { 1688 blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2; 1689 1690 if ( max_offset <= blend->glyphoffsets[i] ) 1691 max_offset = blend->glyphoffsets[i]; 1692 else 1693 { 1694 FT_TRACE2(( "ft_var_load_gvar:" 1695 " glyph variation data offset %d not monotonic\n", 1696 i )); 1697 blend->glyphoffsets[i] = max_offset; 1698 } 1699 1700 /* use `<', not `<=' */ 1701 if ( limit < blend->glyphoffsets[i] ) 1702 { 1703 FT_TRACE2(( "ft_var_load_gvar:" 1704 " glyph variation data offset %d out of range\n", 1705 i )); 1706 blend->glyphoffsets[i] = limit; 1707 } 1708 } 1709 } 1710 1711 blend->gv_glyphcnt = gvar_head.glyphCount; 1712 1713 FT_FRAME_EXIT(); 1714 1715 if ( gvar_head.globalCoordCount != 0 ) 1716 { 1717 if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord ) || 1718 FT_FRAME_ENTER( gvar_head.globalCoordCount * 1719 gvar_head.axisCount * 2L ) ) 1720 { 1721 FT_TRACE2(( "ft_var_load_gvar:" 1722 " glyph variation shared tuples missing\n" )); 1723 goto Fail; 1724 } 1725 1726 if ( FT_QNEW_ARRAY( blend->tuplecoords, 1727 gvar_head.axisCount * gvar_head.globalCoordCount ) ) 1728 goto Fail2; 1729 1730 for ( i = 0; i < gvar_head.globalCoordCount; i++ ) 1731 { 1732 FT_TRACE5(( " [ " )); 1733 for ( j = 0; j < (FT_UInt)gvar_head.axisCount; j++ ) 1734 { 1735 blend->tuplecoords[i * gvar_head.axisCount + j] = 1736 FT_fdot14ToFixed( FT_GET_SHORT() ); 1737 FT_TRACE5(( "%.5f ", 1738 blend->tuplecoords[i * gvar_head.axisCount + j] / 65536.0 )); 1739 } 1740 FT_TRACE5(( "]\n" )); 1741 } 1742 1743 blend->tuplecount = gvar_head.globalCoordCount; 1744 1745 FT_TRACE5(( "\n" )); 1746 1747 FT_FRAME_EXIT(); 1748 } 1749 1750 Exit: 1751 return error; 1752 1753 Fail2: 1754 FT_FRAME_EXIT(); 1755 1756 Fail: 1757 FT_FREE( blend->glyphoffsets ); 1758 blend->gv_glyphcnt = 0; 1759 goto Exit; 1760 } 1761 1762 1763 /************************************************************************** 1764 * 1765 * @Function: 1766 * ft_var_apply_tuple 1767 * 1768 * @Description: 1769 * Figure out whether a given tuple (design) applies to the current 1770 * blend, and if so, what is the scaling factor. 1771 * 1772 * @Input: 1773 * blend :: 1774 * The current blend of the font. 1775 * 1776 * tupleIndex :: 1777 * A flag saying whether this is an intermediate 1778 * tuple or not. 1779 * 1780 * tuple_coords :: 1781 * The coordinates of the tuple in normalized axis 1782 * units. 1783 * 1784 * im_start_coords :: 1785 * The initial coordinates where this tuple starts 1786 * to apply (for intermediate coordinates). 1787 * 1788 * im_end_coords :: 1789 * The final coordinates after which this tuple no 1790 * longer applies (for intermediate coordinates). 1791 * 1792 * @Return: 1793 * An FT_Fixed value containing the scaling factor. 1794 */ 1795 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)1796 ft_var_apply_tuple( GX_Blend blend, 1797 FT_UShort tupleIndex, 1798 FT_Fixed* tuple_coords, 1799 FT_Fixed* im_start_coords, 1800 FT_Fixed* im_end_coords ) 1801 { 1802 FT_UInt i; 1803 FT_Fixed apply = 0x10000L; 1804 1805 1806 for ( i = 0; i < blend->num_axis; i++ ) 1807 { 1808 FT_TRACE6(( " axis %d coordinate %.5f:\n", 1809 i, blend->normalizedcoords[i] / 65536.0 )); 1810 1811 /* It's not clear why (for intermediate tuples) we don't need */ 1812 /* to check against start/end -- the documentation says we don't. */ 1813 /* Similarly, it's unclear why we don't need to scale along the */ 1814 /* axis. */ 1815 1816 if ( tuple_coords[i] == 0 ) 1817 { 1818 FT_TRACE6(( " tuple coordinate is zero, ignore\n" )); 1819 continue; 1820 } 1821 1822 if ( blend->normalizedcoords[i] == 0 ) 1823 { 1824 FT_TRACE6(( " axis coordinate is zero, stop\n" )); 1825 apply = 0; 1826 break; 1827 } 1828 1829 if ( blend->normalizedcoords[i] == tuple_coords[i] ) 1830 { 1831 FT_TRACE6(( " tuple coordinate %.5f fits perfectly\n", 1832 tuple_coords[i] / 65536.0 )); 1833 /* `apply' does not change */ 1834 continue; 1835 } 1836 1837 if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) ) 1838 { 1839 /* not an intermediate tuple */ 1840 1841 if ( blend->normalizedcoords[i] < FT_MIN( 0, tuple_coords[i] ) || 1842 blend->normalizedcoords[i] > FT_MAX( 0, tuple_coords[i] ) ) 1843 { 1844 FT_TRACE6(( " tuple coordinate %.5f is exceeded, stop\n", 1845 tuple_coords[i] / 65536.0 )); 1846 apply = 0; 1847 break; 1848 } 1849 1850 FT_TRACE6(( " tuple coordinate %.5f fits\n", 1851 tuple_coords[i] / 65536.0 )); 1852 apply = FT_MulDiv( apply, 1853 blend->normalizedcoords[i], 1854 tuple_coords[i] ); 1855 } 1856 else 1857 { 1858 /* intermediate tuple */ 1859 1860 if ( blend->normalizedcoords[i] <= im_start_coords[i] || 1861 blend->normalizedcoords[i] >= im_end_coords[i] ) 1862 { 1863 FT_TRACE6(( " intermediate tuple range ]%.5f;%.5f[ is exceeded," 1864 " stop\n", 1865 im_start_coords[i] / 65536.0, 1866 im_end_coords[i] / 65536.0 )); 1867 apply = 0; 1868 break; 1869 } 1870 1871 FT_TRACE6(( " intermediate tuple range ]%.5f;%.5f[ fits\n", 1872 im_start_coords[i] / 65536.0, 1873 im_end_coords[i] / 65536.0 )); 1874 if ( blend->normalizedcoords[i] < tuple_coords[i] ) 1875 apply = FT_MulDiv( apply, 1876 blend->normalizedcoords[i] - im_start_coords[i], 1877 tuple_coords[i] - im_start_coords[i] ); 1878 else 1879 apply = FT_MulDiv( apply, 1880 im_end_coords[i] - blend->normalizedcoords[i], 1881 im_end_coords[i] - tuple_coords[i] ); 1882 } 1883 } 1884 1885 FT_TRACE6(( " apply factor is %.5f\n", apply / 65536.0 )); 1886 1887 return apply; 1888 } 1889 1890 1891 /* convert from design coordinates to normalized coordinates */ 1892 1893 static void ft_var_to_normalized(TT_Face face,FT_UInt num_coords,FT_Fixed * coords,FT_Fixed * normalized)1894 ft_var_to_normalized( TT_Face face, 1895 FT_UInt num_coords, 1896 FT_Fixed* coords, 1897 FT_Fixed* normalized ) 1898 { 1899 GX_Blend blend; 1900 FT_MM_Var* mmvar; 1901 FT_UInt i, j; 1902 FT_Var_Axis* a; 1903 GX_AVarSegment av; 1904 1905 1906 blend = face->blend; 1907 mmvar = blend->mmvar; 1908 1909 if ( num_coords > mmvar->num_axis ) 1910 { 1911 FT_TRACE2(( "ft_var_to_normalized:" 1912 " only using first %d of %d coordinates\n", 1913 mmvar->num_axis, num_coords )); 1914 num_coords = mmvar->num_axis; 1915 } 1916 1917 /* Axis normalization is a two-stage process. First we normalize */ 1918 /* based on the [min,def,max] values for the axis to be [-1,0,1]. */ 1919 /* Then, if there's an `avar' table, we renormalize this range. */ 1920 1921 a = mmvar->axis; 1922 for ( i = 0; i < num_coords; i++, a++ ) 1923 { 1924 FT_Fixed coord = coords[i]; 1925 1926 1927 FT_TRACE5(( " %d: %.5f\n", i, coord / 65536.0 )); 1928 if ( coord > a->maximum || coord < a->minimum ) 1929 { 1930 FT_TRACE1(( "ft_var_to_normalized: design coordinate %.5f\n", 1931 coord / 65536.0 )); 1932 FT_TRACE1(( " is out of range [%.5f;%.5f];" 1933 " clamping\n", 1934 a->minimum / 65536.0, 1935 a->maximum / 65536.0 )); 1936 } 1937 1938 if ( coord > a->def ) 1939 normalized[i] = coord >= a->maximum ? 0x10000L : 1940 FT_DivFix( SUB_LONG( coord, a->def ), 1941 SUB_LONG( a->maximum, a->def ) ); 1942 else if ( coord < a->def ) 1943 normalized[i] = coord <= a->minimum ? -0x10000L : 1944 FT_DivFix( SUB_LONG( coord, a->def ), 1945 SUB_LONG( a->def, a->minimum ) ); 1946 else 1947 normalized[i] = 0; 1948 } 1949 1950 FT_TRACE5(( "\n" )); 1951 1952 for ( ; i < mmvar->num_axis; i++ ) 1953 normalized[i] = 0; 1954 1955 if ( blend->avar_segment ) 1956 { 1957 FT_TRACE5(( "normalized design coordinates" 1958 " before applying `avar' data:\n" )); 1959 1960 av = blend->avar_segment; 1961 for ( i = 0; i < mmvar->num_axis; i++, av++ ) 1962 { 1963 for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) 1964 { 1965 if ( normalized[i] < av->correspondence[j].fromCoord ) 1966 { 1967 FT_TRACE5(( " %.5f\n", normalized[i] / 65536.0 )); 1968 1969 normalized[i] = 1970 FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord, 1971 av->correspondence[j].toCoord - 1972 av->correspondence[j - 1].toCoord, 1973 av->correspondence[j].fromCoord - 1974 av->correspondence[j - 1].fromCoord ) + 1975 av->correspondence[j - 1].toCoord; 1976 break; 1977 } 1978 } 1979 } 1980 } 1981 } 1982 1983 1984 /* convert from normalized coordinates to design coordinates */ 1985 1986 static void ft_var_to_design(TT_Face face,FT_UInt num_coords,FT_Fixed * coords,FT_Fixed * design)1987 ft_var_to_design( TT_Face face, 1988 FT_UInt num_coords, 1989 FT_Fixed* coords, 1990 FT_Fixed* design ) 1991 { 1992 GX_Blend blend; 1993 FT_MM_Var* mmvar; 1994 FT_Var_Axis* a; 1995 1996 FT_UInt i, j, nc; 1997 1998 1999 blend = face->blend; 2000 2001 nc = num_coords; 2002 if ( num_coords > blend->num_axis ) 2003 { 2004 FT_TRACE2(( "ft_var_to_design:" 2005 " only using first %d of %d coordinates\n", 2006 blend->num_axis, num_coords )); 2007 nc = blend->num_axis; 2008 } 2009 2010 for ( i = 0; i < nc; i++ ) 2011 design[i] = coords[i]; 2012 2013 for ( ; i < num_coords; i++ ) 2014 design[i] = 0; 2015 2016 if ( blend->avar_segment ) 2017 { 2018 GX_AVarSegment av = blend->avar_segment; 2019 2020 2021 FT_TRACE5(( "design coordinates" 2022 " after removing `avar' distortion:\n" )); 2023 2024 for ( i = 0; i < nc; i++, av++ ) 2025 { 2026 for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) 2027 { 2028 if ( design[i] < av->correspondence[j].toCoord ) 2029 { 2030 design[i] = 2031 FT_MulDiv( design[i] - av->correspondence[j - 1].toCoord, 2032 av->correspondence[j].fromCoord - 2033 av->correspondence[j - 1].fromCoord, 2034 av->correspondence[j].toCoord - 2035 av->correspondence[j - 1].toCoord ) + 2036 av->correspondence[j - 1].fromCoord; 2037 2038 FT_TRACE5(( " %.5f\n", design[i] / 65536.0 )); 2039 break; 2040 } 2041 } 2042 } 2043 } 2044 2045 mmvar = blend->mmvar; 2046 a = mmvar->axis; 2047 2048 for ( i = 0; i < nc; i++, a++ ) 2049 { 2050 if ( design[i] < 0 ) 2051 design[i] = a->def + FT_MulFix( design[i], 2052 a->def - a->minimum ); 2053 else if ( design[i] > 0 ) 2054 design[i] = a->def + FT_MulFix( design[i], 2055 a->maximum - a->def ); 2056 else 2057 design[i] = a->def; 2058 } 2059 } 2060 2061 2062 /*************************************************************************/ 2063 /*************************************************************************/ 2064 /***** *****/ 2065 /***** MULTIPLE MASTERS SERVICE FUNCTIONS *****/ 2066 /***** *****/ 2067 /*************************************************************************/ 2068 /*************************************************************************/ 2069 2070 2071 typedef struct GX_FVar_Head_ 2072 { 2073 FT_Long version; 2074 FT_UShort offsetToData; 2075 FT_UShort axisCount; 2076 FT_UShort axisSize; 2077 FT_UShort instanceCount; 2078 FT_UShort instanceSize; 2079 2080 } GX_FVar_Head; 2081 2082 2083 typedef struct fvar_axis_ 2084 { 2085 FT_ULong axisTag; 2086 FT_Fixed minValue; 2087 FT_Fixed defaultValue; 2088 FT_Fixed maxValue; 2089 FT_UShort flags; 2090 FT_UShort nameID; 2091 2092 } GX_FVar_Axis; 2093 2094 2095 /************************************************************************** 2096 * 2097 * @Function: 2098 * TT_Get_MM_Var 2099 * 2100 * @Description: 2101 * Check that the font's `fvar' table is valid, parse it, and return 2102 * those data. It also loads (and parses) the `MVAR' table, if 2103 * possible. 2104 * 2105 * @InOut: 2106 * face :: 2107 * The font face. 2108 * TT_Get_MM_Var initializes the blend structure. 2109 * 2110 * @Output: 2111 * master :: 2112 * The `fvar' data (must be freed by caller). Can be NULL, 2113 * which makes this function simply load MM support. 2114 * 2115 * @Return: 2116 * FreeType error code. 0 means success. 2117 */ 2118 FT_LOCAL_DEF( FT_Error ) TT_Get_MM_Var(TT_Face face,FT_MM_Var ** master)2119 TT_Get_MM_Var( TT_Face face, 2120 FT_MM_Var* *master ) 2121 { 2122 FT_Stream stream = face->root.stream; 2123 FT_Memory memory = face->root.memory; 2124 FT_ULong table_len; 2125 FT_Error error = FT_Err_Ok; 2126 FT_ULong fvar_start = 0; 2127 FT_UInt i, j; 2128 FT_MM_Var* mmvar = NULL; 2129 FT_Fixed* next_coords; 2130 FT_Fixed* nsc; 2131 FT_String* next_name; 2132 FT_Var_Axis* a; 2133 FT_Fixed* c; 2134 FT_Var_Named_Style* ns; 2135 GX_FVar_Head fvar_head = { 0, 0, 0, 0, 0, 0 }; 2136 FT_Bool usePsName = 0; 2137 FT_UInt num_instances; 2138 FT_UInt num_axes; 2139 FT_UShort* axis_flags; 2140 2141 FT_Offset mmvar_size; 2142 FT_Offset axis_flags_size; 2143 FT_Offset axis_size; 2144 FT_Offset namedstyle_size; 2145 FT_Offset next_coords_size; 2146 FT_Offset next_name_size; 2147 2148 FT_Bool need_init; 2149 2150 static const FT_Frame_Field fvar_fields[] = 2151 { 2152 2153 #undef FT_STRUCTURE 2154 #define FT_STRUCTURE GX_FVar_Head 2155 2156 FT_FRAME_START( 16 ), 2157 FT_FRAME_LONG ( version ), 2158 FT_FRAME_USHORT ( offsetToData ), 2159 FT_FRAME_SKIP_SHORT, 2160 FT_FRAME_USHORT ( axisCount ), 2161 FT_FRAME_USHORT ( axisSize ), 2162 FT_FRAME_USHORT ( instanceCount ), 2163 FT_FRAME_USHORT ( instanceSize ), 2164 FT_FRAME_END 2165 }; 2166 2167 static const FT_Frame_Field fvaraxis_fields[] = 2168 { 2169 2170 #undef FT_STRUCTURE 2171 #define FT_STRUCTURE GX_FVar_Axis 2172 2173 FT_FRAME_START( 20 ), 2174 FT_FRAME_ULONG ( axisTag ), 2175 FT_FRAME_LONG ( minValue ), 2176 FT_FRAME_LONG ( defaultValue ), 2177 FT_FRAME_LONG ( maxValue ), 2178 FT_FRAME_USHORT( flags ), 2179 FT_FRAME_USHORT( nameID ), 2180 FT_FRAME_END 2181 }; 2182 2183 2184 /* read the font data and set up the internal representation */ 2185 /* if not already done */ 2186 2187 need_init = !face->blend; 2188 2189 if ( need_init ) 2190 { 2191 FT_TRACE2(( "FVAR " )); 2192 2193 /* both `fvar' and `gvar' must be present */ 2194 if ( FT_SET_ERROR( face->goto_table( face, TTAG_gvar, 2195 stream, &table_len ) ) ) 2196 { 2197 /* CFF2 is an alternate to gvar here */ 2198 if ( FT_SET_ERROR( face->goto_table( face, TTAG_CFF2, 2199 stream, &table_len ) ) ) 2200 { 2201 FT_TRACE1(( "\n" )); 2202 FT_TRACE1(( "TT_Get_MM_Var: `gvar' or `CFF2' table is missing\n" )); 2203 goto Exit; 2204 } 2205 } 2206 2207 if ( FT_SET_ERROR( face->goto_table( face, TTAG_fvar, 2208 stream, &table_len ) ) ) 2209 { 2210 FT_TRACE1(( "is missing\n" )); 2211 goto Exit; 2212 } 2213 2214 fvar_start = FT_STREAM_POS( ); 2215 2216 /* the validity of the `fvar' header data was already checked */ 2217 /* in function `sfnt_init_face' */ 2218 if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) ) 2219 goto Exit; 2220 2221 usePsName = FT_BOOL( fvar_head.instanceSize == 2222 6 + 4 * fvar_head.axisCount ); 2223 2224 FT_TRACE2(( "loaded\n" )); 2225 2226 FT_TRACE5(( "%d variation ax%s\n", 2227 fvar_head.axisCount, 2228 fvar_head.axisCount == 1 ? "is" : "es" )); 2229 2230 if ( FT_NEW( face->blend ) ) 2231 goto Exit; 2232 2233 num_axes = fvar_head.axisCount; 2234 face->blend->num_axis = num_axes; 2235 } 2236 else 2237 num_axes = face->blend->num_axis; 2238 2239 /* `num_instances' holds the number of all named instances, */ 2240 /* including the default instance which might be missing */ 2241 /* in fvar's table of named instances */ 2242 num_instances = (FT_UInt)face->root.style_flags >> 16; 2243 2244 /* prepare storage area for MM data; this cannot overflow */ 2245 /* 32-bit arithmetic because of the size limits used in the */ 2246 /* `fvar' table validity check in `sfnt_init_face' */ 2247 2248 /* the various `*_size' variables, which we also use as */ 2249 /* offsets into the `mmvar' array, must be multiples of the */ 2250 /* pointer size (except the last one); without such an */ 2251 /* alignment there might be runtime errors due to */ 2252 /* misaligned addresses */ 2253 #undef ALIGN_SIZE 2254 #define ALIGN_SIZE( n ) \ 2255 ( ( (n) + sizeof (void*) - 1 ) & ~( sizeof (void*) - 1 ) ) 2256 2257 mmvar_size = ALIGN_SIZE( sizeof ( FT_MM_Var ) ); 2258 axis_flags_size = ALIGN_SIZE( num_axes * 2259 sizeof ( FT_UShort ) ); 2260 axis_size = ALIGN_SIZE( num_axes * 2261 sizeof ( FT_Var_Axis ) ); 2262 namedstyle_size = ALIGN_SIZE( num_instances * 2263 sizeof ( FT_Var_Named_Style ) ); 2264 next_coords_size = ALIGN_SIZE( num_instances * 2265 num_axes * 2266 sizeof ( FT_Fixed ) ); 2267 next_name_size = num_axes * 5; 2268 2269 if ( need_init ) 2270 { 2271 face->blend->mmvar_len = mmvar_size + 2272 axis_flags_size + 2273 axis_size + 2274 namedstyle_size + 2275 next_coords_size + 2276 next_name_size; 2277 2278 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) ) 2279 goto Exit; 2280 face->blend->mmvar = mmvar; 2281 2282 /* set up pointers and offsets into the `mmvar' array; */ 2283 /* the data gets filled in later on */ 2284 2285 mmvar->num_axis = 2286 num_axes; 2287 mmvar->num_designs = 2288 ~0U; /* meaningless in this context; each glyph */ 2289 /* may have a different number of designs */ 2290 /* (or tuples, as called by Apple) */ 2291 mmvar->num_namedstyles = 2292 num_instances; 2293 2294 /* alas, no public field in `FT_Var_Axis' for axis flags */ 2295 axis_flags = 2296 (FT_UShort*)( (char*)mmvar + mmvar_size ); 2297 mmvar->axis = 2298 (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size ); 2299 mmvar->namedstyle = 2300 (FT_Var_Named_Style*)( (char*)mmvar->axis + axis_size ); 2301 2302 next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle + 2303 namedstyle_size ); 2304 for ( i = 0; i < num_instances; i++ ) 2305 { 2306 mmvar->namedstyle[i].coords = next_coords; 2307 next_coords += num_axes; 2308 } 2309 2310 next_name = (FT_String*)( (char*)mmvar->namedstyle + 2311 namedstyle_size + next_coords_size ); 2312 for ( i = 0; i < num_axes; i++ ) 2313 { 2314 mmvar->axis[i].name = next_name; 2315 next_name += 5; 2316 } 2317 2318 /* now fill in the data */ 2319 2320 if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) ) 2321 goto Exit; 2322 2323 a = mmvar->axis; 2324 for ( i = 0; i < num_axes; i++ ) 2325 { 2326 GX_FVar_Axis axis_rec; 2327 2328 #ifdef FT_DEBUG_LEVEL_TRACE 2329 int invalid = 0; 2330 #endif 2331 2332 2333 if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) ) 2334 goto Exit; 2335 a->tag = axis_rec.axisTag; 2336 a->minimum = axis_rec.minValue; 2337 a->def = axis_rec.defaultValue; 2338 a->maximum = axis_rec.maxValue; 2339 a->strid = axis_rec.nameID; 2340 2341 a->name[0] = (FT_String)( a->tag >> 24 ); 2342 a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF ); 2343 a->name[2] = (FT_String)( ( a->tag >> 8 ) & 0xFF ); 2344 a->name[3] = (FT_String)( ( a->tag ) & 0xFF ); 2345 a->name[4] = '\0'; 2346 2347 *axis_flags = axis_rec.flags; 2348 2349 if ( a->minimum > a->def || 2350 a->def > a->maximum ) 2351 { 2352 a->minimum = a->def; 2353 a->maximum = a->def; 2354 2355 #ifdef FT_DEBUG_LEVEL_TRACE 2356 invalid = 1; 2357 #endif 2358 } 2359 2360 #ifdef FT_DEBUG_LEVEL_TRACE 2361 if ( i == 0 ) 2362 FT_TRACE5(( " idx tag " 2363 /* " XXX `XXXX'" */ 2364 " minimum default maximum flags\n" )); 2365 /* " XXXX.XXXXX XXXX.XXXXX XXXX.XXXXX 0xXXXX" */ 2366 2367 FT_TRACE5(( " %3d `%s'" 2368 " %10.5f %10.5f %10.5f 0x%04X%s\n", 2369 i, 2370 a->name, 2371 a->minimum / 65536.0, 2372 a->def / 65536.0, 2373 a->maximum / 65536.0, 2374 *axis_flags, 2375 invalid ? " (invalid, disabled)" : "" )); 2376 #endif 2377 2378 a++; 2379 axis_flags++; 2380 } 2381 2382 FT_TRACE5(( "\n" )); 2383 2384 /* named instance coordinates are stored as design coordinates; */ 2385 /* we have to convert them to normalized coordinates also */ 2386 if ( FT_NEW_ARRAY( face->blend->normalized_stylecoords, 2387 num_axes * num_instances ) ) 2388 goto Exit; 2389 2390 if ( fvar_head.instanceCount && !face->blend->avar_loaded ) 2391 { 2392 FT_ULong offset = FT_STREAM_POS(); 2393 2394 2395 ft_var_load_avar( face ); 2396 2397 if ( FT_STREAM_SEEK( offset ) ) 2398 goto Exit; 2399 } 2400 2401 FT_TRACE5(( "%d instance%s\n", 2402 fvar_head.instanceCount, 2403 fvar_head.instanceCount == 1 ? "" : "s" )); 2404 2405 ns = mmvar->namedstyle; 2406 nsc = face->blend->normalized_stylecoords; 2407 for ( i = 0; i < fvar_head.instanceCount; i++, ns++ ) 2408 { 2409 /* PostScript names add 2 bytes to the instance record size */ 2410 if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) + 2411 4L * num_axes ) ) 2412 goto Exit; 2413 2414 ns->strid = FT_GET_USHORT(); 2415 (void) /* flags = */ FT_GET_USHORT(); 2416 2417 c = ns->coords; 2418 for ( j = 0; j < num_axes; j++, c++ ) 2419 *c = FT_GET_LONG(); 2420 2421 /* valid psid values are 6, [256;32767], and 0xFFFF */ 2422 if ( usePsName ) 2423 ns->psid = FT_GET_USHORT(); 2424 else 2425 ns->psid = 0xFFFF; 2426 2427 #ifdef FT_DEBUG_LEVEL_TRACE 2428 { 2429 SFNT_Service sfnt = (SFNT_Service)face->sfnt; 2430 2431 FT_String* strname = NULL; 2432 FT_String* psname = NULL; 2433 2434 FT_ULong pos; 2435 2436 2437 pos = FT_STREAM_POS(); 2438 2439 if ( ns->strid != 0xFFFF ) 2440 { 2441 (void)sfnt->get_name( face, 2442 (FT_UShort)ns->strid, 2443 &strname ); 2444 if ( strname && !ft_strcmp( strname, ".notdef" ) ) 2445 strname = NULL; 2446 } 2447 2448 if ( ns->psid != 0xFFFF ) 2449 { 2450 (void)sfnt->get_name( face, 2451 (FT_UShort)ns->psid, 2452 &psname ); 2453 if ( psname && !ft_strcmp( psname, ".notdef" ) ) 2454 psname = NULL; 2455 } 2456 2457 (void)FT_STREAM_SEEK( pos ); 2458 2459 FT_TRACE5(( " instance %d (%s%s%s, %s%s%s)\n", 2460 i, 2461 strname ? "name: `" : "", 2462 strname ? strname : "unnamed", 2463 strname ? "'" : "", 2464 psname ? "PS name: `" : "", 2465 psname ? psname : "no PS name", 2466 psname ? "'" : "" )); 2467 2468 FT_FREE( strname ); 2469 FT_FREE( psname ); 2470 } 2471 #endif /* FT_DEBUG_LEVEL_TRACE */ 2472 2473 ft_var_to_normalized( face, num_axes, ns->coords, nsc ); 2474 nsc += num_axes; 2475 2476 FT_FRAME_EXIT(); 2477 } 2478 2479 if ( num_instances != fvar_head.instanceCount ) 2480 { 2481 SFNT_Service sfnt = (SFNT_Service)face->sfnt; 2482 2483 FT_Int found, dummy1, dummy2; 2484 FT_UInt strid = ~0U; 2485 2486 2487 /* the default instance is missing in array the */ 2488 /* of named instances; try to synthesize an entry */ 2489 found = sfnt->get_name_id( face, 2490 TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY, 2491 &dummy1, 2492 &dummy2 ); 2493 if ( found ) 2494 strid = TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY; 2495 else 2496 { 2497 found = sfnt->get_name_id( face, 2498 TT_NAME_ID_FONT_SUBFAMILY, 2499 &dummy1, 2500 &dummy2 ); 2501 if ( found ) 2502 strid = TT_NAME_ID_FONT_SUBFAMILY; 2503 } 2504 2505 if ( found ) 2506 { 2507 found = sfnt->get_name_id( face, 2508 TT_NAME_ID_PS_NAME, 2509 &dummy1, 2510 &dummy2 ); 2511 if ( found ) 2512 { 2513 FT_TRACE5(( "TT_Get_MM_Var:" 2514 " Adding default instance to named instances\n" )); 2515 2516 ns = &mmvar->namedstyle[fvar_head.instanceCount]; 2517 2518 ns->strid = strid; 2519 ns->psid = TT_NAME_ID_PS_NAME; 2520 2521 a = mmvar->axis; 2522 c = ns->coords; 2523 for ( j = 0; j < num_axes; j++, a++, c++ ) 2524 *c = a->def; 2525 } 2526 } 2527 } 2528 2529 ft_var_load_mvar( face ); 2530 } 2531 2532 /* fill the output array if requested */ 2533 2534 if ( master ) 2535 { 2536 FT_UInt n; 2537 2538 2539 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) ) 2540 goto Exit; 2541 FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len ); 2542 2543 axis_flags = 2544 (FT_UShort*)( (char*)mmvar + mmvar_size ); 2545 mmvar->axis = 2546 (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size ); 2547 mmvar->namedstyle = 2548 (FT_Var_Named_Style*)( (char*)mmvar->axis+ axis_size ); 2549 2550 next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle + 2551 namedstyle_size ); 2552 for ( n = 0; n < mmvar->num_namedstyles; n++ ) 2553 { 2554 mmvar->namedstyle[n].coords = next_coords; 2555 next_coords += num_axes; 2556 } 2557 2558 a = mmvar->axis; 2559 next_name = (FT_String*)( (char*)mmvar->namedstyle + 2560 namedstyle_size + next_coords_size ); 2561 for ( n = 0; n < num_axes; n++ ) 2562 { 2563 a->name = next_name; 2564 2565 /* standard PostScript names for some standard apple tags */ 2566 if ( a->tag == TTAG_wght ) 2567 a->name = (char*)"Weight"; 2568 else if ( a->tag == TTAG_wdth ) 2569 a->name = (char*)"Width"; 2570 else if ( a->tag == TTAG_opsz ) 2571 a->name = (char*)"OpticalSize"; 2572 else if ( a->tag == TTAG_slnt ) 2573 a->name = (char*)"Slant"; 2574 2575 next_name += 5; 2576 a++; 2577 } 2578 2579 *master = mmvar; 2580 } 2581 2582 Exit: 2583 return error; 2584 } 2585 2586 2587 static FT_Error tt_set_mm_blend(TT_Face face,FT_UInt num_coords,FT_Fixed * coords,FT_Bool set_design_coords)2588 tt_set_mm_blend( TT_Face face, 2589 FT_UInt num_coords, 2590 FT_Fixed* coords, 2591 FT_Bool set_design_coords ) 2592 { 2593 FT_Error error = FT_Err_Ok; 2594 GX_Blend blend; 2595 FT_MM_Var* mmvar; 2596 FT_UInt i; 2597 2598 FT_Bool all_design_coords = FALSE; 2599 2600 FT_Memory memory = face->root.memory; 2601 2602 enum 2603 { 2604 mcvt_retain, 2605 mcvt_modify, 2606 mcvt_load 2607 2608 } manageCvt; 2609 2610 2611 face->doblend = FALSE; 2612 2613 if ( !face->blend ) 2614 { 2615 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 2616 goto Exit; 2617 } 2618 2619 blend = face->blend; 2620 mmvar = blend->mmvar; 2621 2622 if ( num_coords > mmvar->num_axis ) 2623 { 2624 FT_TRACE2(( "TT_Set_MM_Blend:" 2625 " only using first %d of %d coordinates\n", 2626 mmvar->num_axis, num_coords )); 2627 num_coords = mmvar->num_axis; 2628 } 2629 2630 FT_TRACE5(( "TT_Set_MM_Blend:\n" )); 2631 FT_TRACE5(( " normalized design coordinates:\n" )); 2632 2633 for ( i = 0; i < num_coords; i++ ) 2634 { 2635 FT_TRACE5(( " %.5f\n", coords[i] / 65536.0 )); 2636 if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L ) 2637 { 2638 FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n", 2639 coords[i] / 65536.0 )); 2640 FT_TRACE1(( " is out of range [-1;1]\n" )); 2641 error = FT_THROW( Invalid_Argument ); 2642 goto Exit; 2643 } 2644 } 2645 2646 FT_TRACE5(( "\n" )); 2647 2648 if ( !face->is_cff2 && !blend->glyphoffsets ) 2649 if ( FT_SET_ERROR( ft_var_load_gvar( face ) ) ) 2650 goto Exit; 2651 2652 if ( !blend->coords ) 2653 { 2654 if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) ) 2655 goto Exit; 2656 2657 /* the first time we have to compute all design coordinates */ 2658 all_design_coords = TRUE; 2659 } 2660 2661 if ( !blend->normalizedcoords ) 2662 { 2663 if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) ) 2664 goto Exit; 2665 2666 manageCvt = mcvt_modify; 2667 2668 /* If we have not set the blend coordinates before this, then the */ 2669 /* cvt table will still be what we read from the `cvt ' table and */ 2670 /* we don't need to reload it. We may need to change it though... */ 2671 } 2672 else 2673 { 2674 FT_Bool have_diff = 0; 2675 FT_UInt j; 2676 FT_Fixed* c; 2677 FT_Fixed* n; 2678 2679 2680 manageCvt = mcvt_retain; 2681 2682 for ( i = 0; i < num_coords; i++ ) 2683 { 2684 if ( blend->normalizedcoords[i] != coords[i] ) 2685 { 2686 manageCvt = mcvt_load; 2687 have_diff = 1; 2688 break; 2689 } 2690 } 2691 2692 if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ) 2693 { 2694 FT_UInt instance_index = (FT_UInt)face->root.face_index >> 16; 2695 2696 2697 c = blend->normalizedcoords + i; 2698 n = blend->normalized_stylecoords + 2699 ( instance_index - 1 ) * mmvar->num_axis + 2700 i; 2701 2702 for ( j = i; j < mmvar->num_axis; j++, n++, c++ ) 2703 if ( *c != *n ) 2704 have_diff = 1; 2705 } 2706 else 2707 { 2708 c = blend->normalizedcoords + i; 2709 for ( j = i; j < mmvar->num_axis; j++, c++ ) 2710 if ( *c != 0 ) 2711 have_diff = 1; 2712 } 2713 2714 /* return value -1 indicates `no change' */ 2715 if ( !have_diff ) 2716 { 2717 face->doblend = TRUE; 2718 2719 return -1; 2720 } 2721 2722 for ( ; i < mmvar->num_axis; i++ ) 2723 { 2724 if ( blend->normalizedcoords[i] != 0 ) 2725 { 2726 manageCvt = mcvt_load; 2727 break; 2728 } 2729 } 2730 2731 /* If we don't change the blend coords then we don't need to do */ 2732 /* anything to the cvt table. It will be correct. Otherwise we */ 2733 /* no longer have the original cvt (it was modified when we set */ 2734 /* the blend last time), so we must reload and then modify it. */ 2735 } 2736 2737 blend->num_axis = mmvar->num_axis; 2738 if ( coords ) 2739 FT_MEM_COPY( blend->normalizedcoords, 2740 coords, 2741 num_coords * sizeof ( FT_Fixed ) ); 2742 2743 if ( set_design_coords ) 2744 ft_var_to_design( face, 2745 all_design_coords ? blend->num_axis : num_coords, 2746 blend->normalizedcoords, 2747 blend->coords ); 2748 2749 face->doblend = TRUE; 2750 2751 if ( face->cvt ) 2752 { 2753 switch ( manageCvt ) 2754 { 2755 case mcvt_load: 2756 /* The cvt table has been loaded already; every time we change the */ 2757 /* blend we may need to reload and remodify the cvt table. */ 2758 FT_FREE( face->cvt ); 2759 face->cvt = NULL; 2760 2761 error = tt_face_load_cvt( face, face->root.stream ); 2762 break; 2763 2764 case mcvt_modify: 2765 /* The original cvt table is in memory. All we need to do is */ 2766 /* apply the `cvar' table (if any). */ 2767 error = tt_face_vary_cvt( face, face->root.stream ); 2768 break; 2769 2770 case mcvt_retain: 2771 /* The cvt table is correct for this set of coordinates. */ 2772 break; 2773 } 2774 } 2775 2776 /* enforce recomputation of the PostScript name; */ 2777 FT_FREE( face->postscript_name ); 2778 face->postscript_name = NULL; 2779 2780 Exit: 2781 return error; 2782 } 2783 2784 2785 /************************************************************************** 2786 * 2787 * @Function: 2788 * TT_Set_MM_Blend 2789 * 2790 * @Description: 2791 * Set the blend (normalized) coordinates for this instance of the 2792 * font. Check that the `gvar' table is reasonable and does some 2793 * initial preparation. 2794 * 2795 * @InOut: 2796 * face :: 2797 * The font. 2798 * Initialize the blend structure with `gvar' data. 2799 * 2800 * @Input: 2801 * num_coords :: 2802 * The number of available coordinates. If it is 2803 * larger than the number of axes, ignore the excess 2804 * values. If it is smaller than the number of axes, 2805 * use the default value (0) for the remaining axes. 2806 * 2807 * coords :: 2808 * An array of `num_coords', each between [-1,1]. 2809 * 2810 * @Return: 2811 * FreeType error code. 0 means success. 2812 */ 2813 FT_LOCAL_DEF( FT_Error ) TT_Set_MM_Blend(TT_Face face,FT_UInt num_coords,FT_Fixed * coords)2814 TT_Set_MM_Blend( TT_Face face, 2815 FT_UInt num_coords, 2816 FT_Fixed* coords ) 2817 { 2818 FT_Error error; 2819 2820 2821 error = tt_set_mm_blend( face, num_coords, coords, 1 ); 2822 if ( error ) 2823 return error; 2824 2825 if ( num_coords ) 2826 face->root.face_flags |= FT_FACE_FLAG_VARIATION; 2827 else 2828 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION; 2829 2830 return FT_Err_Ok; 2831 } 2832 2833 2834 /************************************************************************** 2835 * 2836 * @Function: 2837 * TT_Get_MM_Blend 2838 * 2839 * @Description: 2840 * Get the blend (normalized) coordinates for this instance of the 2841 * font. 2842 * 2843 * @InOut: 2844 * face :: 2845 * The font. 2846 * Initialize the blend structure with `gvar' data. 2847 * 2848 * @Input: 2849 * num_coords :: 2850 * The number of available coordinates. If it is 2851 * larger than the number of axes, set the excess 2852 * values to 0. 2853 * 2854 * coords :: 2855 * An array of `num_coords', each between [-1,1]. 2856 * 2857 * @Return: 2858 * FreeType error code. 0 means success. 2859 */ 2860 FT_LOCAL_DEF( FT_Error ) TT_Get_MM_Blend(TT_Face face,FT_UInt num_coords,FT_Fixed * coords)2861 TT_Get_MM_Blend( TT_Face face, 2862 FT_UInt num_coords, 2863 FT_Fixed* coords ) 2864 { 2865 FT_Error error = FT_Err_Ok; 2866 GX_Blend blend; 2867 FT_UInt i, nc; 2868 2869 2870 if ( !face->blend ) 2871 { 2872 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 2873 return error; 2874 } 2875 2876 blend = face->blend; 2877 2878 if ( !blend->coords ) 2879 { 2880 /* select default instance coordinates */ 2881 /* if no instance is selected yet */ 2882 if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) ) 2883 return error; 2884 } 2885 2886 nc = num_coords; 2887 if ( num_coords > blend->num_axis ) 2888 { 2889 FT_TRACE2(( "TT_Get_MM_Blend:" 2890 " only using first %d of %d coordinates\n", 2891 blend->num_axis, num_coords )); 2892 nc = blend->num_axis; 2893 } 2894 2895 if ( face->doblend ) 2896 { 2897 for ( i = 0; i < nc; i++ ) 2898 coords[i] = blend->normalizedcoords[i]; 2899 } 2900 else 2901 { 2902 for ( i = 0; i < nc; i++ ) 2903 coords[i] = 0; 2904 } 2905 2906 for ( ; i < num_coords; i++ ) 2907 coords[i] = 0; 2908 2909 return FT_Err_Ok; 2910 } 2911 2912 2913 /************************************************************************** 2914 * 2915 * @Function: 2916 * TT_Set_Var_Design 2917 * 2918 * @Description: 2919 * Set the coordinates for the instance, measured in the user 2920 * coordinate system. Parse the `avar' table (if present) to convert 2921 * from user to normalized coordinates. 2922 * 2923 * @InOut: 2924 * face :: 2925 * The font face. 2926 * Initialize the blend struct with `gvar' data. 2927 * 2928 * @Input: 2929 * num_coords :: 2930 * The number of available coordinates. If it is 2931 * larger than the number of axes, ignore the excess 2932 * values. If it is smaller than the number of axes, 2933 * use the default values for the remaining axes. 2934 * 2935 * coords :: 2936 * A coordinate array with `num_coords' elements. 2937 * 2938 * @Return: 2939 * FreeType error code. 0 means success. 2940 */ 2941 FT_LOCAL_DEF( FT_Error ) TT_Set_Var_Design(TT_Face face,FT_UInt num_coords,FT_Fixed * coords)2942 TT_Set_Var_Design( TT_Face face, 2943 FT_UInt num_coords, 2944 FT_Fixed* coords ) 2945 { 2946 FT_Error error = FT_Err_Ok; 2947 GX_Blend blend; 2948 FT_MM_Var* mmvar; 2949 FT_UInt i; 2950 FT_Memory memory = face->root.memory; 2951 2952 FT_Fixed* c; 2953 FT_Fixed* n; 2954 FT_Fixed* normalized = NULL; 2955 2956 FT_Bool have_diff = 0; 2957 2958 2959 if ( !face->blend ) 2960 { 2961 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 2962 goto Exit; 2963 } 2964 2965 blend = face->blend; 2966 mmvar = blend->mmvar; 2967 2968 if ( num_coords > mmvar->num_axis ) 2969 { 2970 FT_TRACE2(( "TT_Set_Var_Design:" 2971 " only using first %d of %d coordinates\n", 2972 mmvar->num_axis, num_coords )); 2973 num_coords = mmvar->num_axis; 2974 } 2975 2976 if ( !blend->coords ) 2977 { 2978 if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) ) 2979 goto Exit; 2980 } 2981 2982 c = blend->coords; 2983 n = coords; 2984 for ( i = 0; i < num_coords; i++, n++, c++ ) 2985 { 2986 if ( *c != *n ) 2987 { 2988 *c = *n; 2989 have_diff = 1; 2990 } 2991 } 2992 2993 if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ) 2994 { 2995 FT_UInt instance_index; 2996 FT_Var_Named_Style* named_style; 2997 2998 2999 instance_index = (FT_UInt)face->root.face_index >> 16; 3000 named_style = mmvar->namedstyle + instance_index - 1; 3001 3002 n = named_style->coords + num_coords; 3003 for ( ; i < mmvar->num_axis; i++, n++, c++ ) 3004 { 3005 if ( *c != *n ) 3006 { 3007 *c = *n; 3008 have_diff = 1; 3009 } 3010 } 3011 } 3012 else 3013 { 3014 FT_Var_Axis* a; 3015 3016 3017 a = mmvar->axis + num_coords; 3018 for ( ; i < mmvar->num_axis; i++, a++, c++ ) 3019 { 3020 if ( *c != a->def ) 3021 { 3022 *c = a->def; 3023 have_diff = 1; 3024 } 3025 } 3026 } 3027 3028 /* return value -1 indicates `no change'; */ 3029 /* we can exit early if `normalizedcoords' is already computed */ 3030 if ( blend->normalizedcoords && !have_diff ) 3031 return -1; 3032 3033 if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) ) 3034 goto Exit; 3035 3036 if ( !face->blend->avar_loaded ) 3037 ft_var_load_avar( face ); 3038 3039 FT_TRACE5(( "TT_Set_Var_Design:\n" )); 3040 FT_TRACE5(( " normalized design coordinates:\n" )); 3041 ft_var_to_normalized( face, num_coords, blend->coords, normalized ); 3042 3043 error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 ); 3044 if ( error ) 3045 goto Exit; 3046 3047 if ( num_coords ) 3048 face->root.face_flags |= FT_FACE_FLAG_VARIATION; 3049 else 3050 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION; 3051 3052 Exit: 3053 FT_FREE( normalized ); 3054 return error; 3055 } 3056 3057 3058 /************************************************************************** 3059 * 3060 * @Function: 3061 * TT_Get_Var_Design 3062 * 3063 * @Description: 3064 * Get the design coordinates of the currently selected interpolated 3065 * font. 3066 * 3067 * @Input: 3068 * face :: 3069 * A handle to the source face. 3070 * 3071 * num_coords :: 3072 * The number of design coordinates to retrieve. If it 3073 * is larger than the number of axes, set the excess 3074 * values to~0. 3075 * 3076 * @Output: 3077 * coords :: 3078 * The design coordinates array. 3079 * 3080 * @Return: 3081 * FreeType error code. 0~means success. 3082 */ 3083 FT_LOCAL_DEF( FT_Error ) TT_Get_Var_Design(TT_Face face,FT_UInt num_coords,FT_Fixed * coords)3084 TT_Get_Var_Design( TT_Face face, 3085 FT_UInt num_coords, 3086 FT_Fixed* coords ) 3087 { 3088 FT_Error error = FT_Err_Ok; 3089 GX_Blend blend; 3090 FT_UInt i, nc; 3091 3092 3093 if ( !face->blend ) 3094 { 3095 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 3096 return error; 3097 } 3098 3099 blend = face->blend; 3100 3101 if ( !blend->coords ) 3102 { 3103 /* select default instance coordinates */ 3104 /* if no instance is selected yet */ 3105 if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) ) 3106 return error; 3107 } 3108 3109 nc = num_coords; 3110 if ( num_coords > blend->num_axis ) 3111 { 3112 FT_TRACE2(( "TT_Get_Var_Design:" 3113 " only using first %d of %d coordinates\n", 3114 blend->num_axis, num_coords )); 3115 nc = blend->num_axis; 3116 } 3117 3118 if ( face->doblend ) 3119 { 3120 for ( i = 0; i < nc; i++ ) 3121 coords[i] = blend->coords[i]; 3122 } 3123 else 3124 { 3125 for ( i = 0; i < nc; i++ ) 3126 coords[i] = 0; 3127 } 3128 3129 for ( ; i < num_coords; i++ ) 3130 coords[i] = 0; 3131 3132 return FT_Err_Ok; 3133 } 3134 3135 3136 /************************************************************************** 3137 * 3138 * @Function: 3139 * TT_Set_Named_Instance 3140 * 3141 * @Description: 3142 * Set the given named instance, also resetting any further 3143 * variation. 3144 * 3145 * @Input: 3146 * face :: 3147 * A handle to the source face. 3148 * 3149 * instance_index :: 3150 * The instance index, starting with value 1. 3151 * Value 0 indicates to not use an instance. 3152 * 3153 * @Return: 3154 * FreeType error code. 0~means success. 3155 */ 3156 FT_LOCAL_DEF( FT_Error ) TT_Set_Named_Instance(TT_Face face,FT_UInt instance_index)3157 TT_Set_Named_Instance( TT_Face face, 3158 FT_UInt instance_index ) 3159 { 3160 FT_Error error; 3161 GX_Blend blend; 3162 FT_MM_Var* mmvar; 3163 3164 FT_UInt num_instances; 3165 3166 3167 if ( !face->blend ) 3168 { 3169 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 3170 goto Exit; 3171 } 3172 3173 blend = face->blend; 3174 mmvar = blend->mmvar; 3175 3176 num_instances = (FT_UInt)face->root.style_flags >> 16; 3177 3178 /* `instance_index' starts with value 1, thus `>' */ 3179 if ( instance_index > num_instances ) 3180 { 3181 error = FT_ERR( Invalid_Argument ); 3182 goto Exit; 3183 } 3184 3185 if ( instance_index > 0 ) 3186 { 3187 FT_Memory memory = face->root.memory; 3188 SFNT_Service sfnt = (SFNT_Service)face->sfnt; 3189 3190 FT_Var_Named_Style* named_style; 3191 FT_String* style_name; 3192 3193 3194 named_style = mmvar->namedstyle + instance_index - 1; 3195 3196 error = sfnt->get_name( face, 3197 (FT_UShort)named_style->strid, 3198 &style_name ); 3199 if ( error ) 3200 goto Exit; 3201 3202 /* set (or replace) style name */ 3203 FT_FREE( face->root.style_name ); 3204 face->root.style_name = style_name; 3205 3206 /* finally, select the named instance */ 3207 error = TT_Set_Var_Design( face, 3208 mmvar->num_axis, 3209 named_style->coords ); 3210 if ( error ) 3211 { 3212 /* internal error code -1 means `no change' */ 3213 if ( error == -1 ) 3214 error = FT_Err_Ok; 3215 goto Exit; 3216 } 3217 } 3218 else 3219 error = TT_Set_Var_Design( face, 0, NULL ); 3220 3221 face->root.face_index = ( instance_index << 16 ) | 3222 ( face->root.face_index & 0xFFFFL ); 3223 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION; 3224 3225 Exit: 3226 return error; 3227 } 3228 3229 3230 /*************************************************************************/ 3231 /*************************************************************************/ 3232 /***** *****/ 3233 /***** GX VAR PARSING ROUTINES *****/ 3234 /***** *****/ 3235 /*************************************************************************/ 3236 /*************************************************************************/ 3237 3238 3239 #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 3240 3241 static FT_Error tt_cvt_ready_iterator(FT_ListNode node,void * user)3242 tt_cvt_ready_iterator( FT_ListNode node, 3243 void* user ) 3244 { 3245 TT_Size size = (TT_Size)node->data; 3246 3247 FT_UNUSED( user ); 3248 3249 3250 size->cvt_ready = -1; 3251 3252 return FT_Err_Ok; 3253 } 3254 3255 #endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ 3256 3257 3258 3259 /************************************************************************** 3260 * 3261 * @Function: 3262 * tt_face_vary_cvt 3263 * 3264 * @Description: 3265 * Modify the loaded cvt table according to the `cvar' table and the 3266 * font's blend. 3267 * 3268 * @InOut: 3269 * face :: 3270 * A handle to the target face object. 3271 * 3272 * @Input: 3273 * stream :: 3274 * A handle to the input stream. 3275 * 3276 * @Return: 3277 * FreeType error code. 0 means success. 3278 * 3279 * Most errors are ignored. It is perfectly valid not to have a 3280 * `cvar' table even if there is a `gvar' and `fvar' table. 3281 */ 3282 FT_LOCAL_DEF( FT_Error ) tt_face_vary_cvt(TT_Face face,FT_Stream stream)3283 tt_face_vary_cvt( TT_Face face, 3284 FT_Stream stream ) 3285 { 3286 #ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER 3287 3288 FT_Error error; 3289 FT_Memory memory = stream->memory; 3290 3291 FT_Face root = &face->root; 3292 3293 FT_ULong table_start; 3294 FT_ULong table_len; 3295 3296 FT_UInt tupleCount; 3297 FT_ULong offsetToData; 3298 3299 FT_ULong here; 3300 FT_UInt i, j; 3301 3302 FT_Fixed* tuple_coords = NULL; 3303 FT_Fixed* im_start_coords = NULL; 3304 FT_Fixed* im_end_coords = NULL; 3305 3306 GX_Blend blend = face->blend; 3307 3308 FT_UInt point_count; 3309 FT_UInt spoint_count = 0; 3310 3311 FT_UShort* sharedpoints = NULL; 3312 FT_UShort* localpoints = NULL; 3313 FT_UShort* points; 3314 3315 FT_Fixed* deltas = NULL; 3316 FT_Fixed* cvt_deltas = NULL; 3317 3318 3319 FT_TRACE2(( "CVAR " )); 3320 3321 if ( !blend ) 3322 { 3323 FT_TRACE2(( "\n" )); 3324 FT_TRACE2(( "tt_face_vary_cvt: no blend specified\n" )); 3325 error = FT_Err_Ok; 3326 goto Exit; 3327 } 3328 3329 if ( !face->cvt ) 3330 { 3331 FT_TRACE2(( "\n" )); 3332 FT_TRACE2(( "tt_face_vary_cvt: no `cvt ' table\n" )); 3333 error = FT_Err_Ok; 3334 goto Exit; 3335 } 3336 3337 error = face->goto_table( face, TTAG_cvar, stream, &table_len ); 3338 if ( error ) 3339 { 3340 FT_TRACE2(( "is missing\n" )); 3341 3342 error = FT_Err_Ok; 3343 goto Exit; 3344 } 3345 3346 if ( FT_FRAME_ENTER( table_len ) ) 3347 { 3348 error = FT_Err_Ok; 3349 goto Exit; 3350 } 3351 3352 table_start = FT_Stream_FTell( stream ); 3353 if ( FT_GET_LONG() != 0x00010000L ) 3354 { 3355 FT_TRACE2(( "bad table version\n" )); 3356 3357 error = FT_Err_Ok; 3358 goto FExit; 3359 } 3360 3361 FT_TRACE2(( "loaded\n" )); 3362 3363 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || 3364 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || 3365 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) 3366 goto FExit; 3367 3368 tupleCount = FT_GET_USHORT(); 3369 offsetToData = FT_GET_USHORT(); 3370 3371 /* rough sanity test */ 3372 if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 > 3373 table_len ) 3374 { 3375 FT_TRACE2(( "tt_face_vary_cvt:" 3376 " invalid CVT variation array header\n" )); 3377 3378 error = FT_THROW( Invalid_Table ); 3379 goto FExit; 3380 } 3381 3382 offsetToData += table_start; 3383 3384 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS ) 3385 { 3386 here = FT_Stream_FTell( stream ); 3387 3388 FT_Stream_SeekSet( stream, offsetToData ); 3389 3390 sharedpoints = ft_var_readpackedpoints( stream, 3391 table_len, 3392 &spoint_count ); 3393 offsetToData = FT_Stream_FTell( stream ); 3394 3395 FT_Stream_SeekSet( stream, here ); 3396 } 3397 3398 FT_TRACE5(( "cvar: there %s %d tuple%s:\n", 3399 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are", 3400 tupleCount & GX_TC_TUPLE_COUNT_MASK, 3401 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" )); 3402 3403 if ( FT_NEW_ARRAY( cvt_deltas, face->cvt_size ) ) 3404 goto FExit; 3405 3406 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ ) 3407 { 3408 FT_UInt tupleDataSize; 3409 FT_UInt tupleIndex; 3410 FT_Fixed apply; 3411 3412 3413 FT_TRACE6(( " tuple %d:\n", i )); 3414 3415 tupleDataSize = FT_GET_USHORT(); 3416 tupleIndex = FT_GET_USHORT(); 3417 3418 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 3419 { 3420 for ( j = 0; j < blend->num_axis; j++ ) 3421 tuple_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() ); 3422 } 3423 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount ) 3424 { 3425 FT_TRACE2(( "tt_face_vary_cvt:" 3426 " invalid tuple index\n" )); 3427 3428 error = FT_THROW( Invalid_Table ); 3429 goto FExit; 3430 } 3431 else 3432 { 3433 if ( !blend->tuplecoords ) 3434 { 3435 FT_TRACE2(( "tt_face_vary_cvt:" 3436 " no valid tuple coordinates available\n" )); 3437 3438 error = FT_THROW( Invalid_Table ); 3439 goto FExit; 3440 } 3441 3442 FT_MEM_COPY( 3443 tuple_coords, 3444 blend->tuplecoords + 3445 ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis, 3446 blend->num_axis * sizeof ( FT_Fixed ) ); 3447 } 3448 3449 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 3450 { 3451 for ( j = 0; j < blend->num_axis; j++ ) 3452 im_start_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() ); 3453 for ( j = 0; j < blend->num_axis; j++ ) 3454 im_end_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() ); 3455 } 3456 3457 apply = ft_var_apply_tuple( blend, 3458 (FT_UShort)tupleIndex, 3459 tuple_coords, 3460 im_start_coords, 3461 im_end_coords ); 3462 3463 if ( apply == 0 ) /* tuple isn't active for our blend */ 3464 { 3465 offsetToData += tupleDataSize; 3466 continue; 3467 } 3468 3469 here = FT_Stream_FTell( stream ); 3470 3471 FT_Stream_SeekSet( stream, offsetToData ); 3472 3473 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) 3474 { 3475 localpoints = ft_var_readpackedpoints( stream, 3476 table_len, 3477 &point_count ); 3478 points = localpoints; 3479 } 3480 else 3481 { 3482 points = sharedpoints; 3483 point_count = spoint_count; 3484 } 3485 3486 deltas = ft_var_readpackeddeltas( stream, 3487 table_len, 3488 point_count == 0 ? face->cvt_size 3489 : point_count ); 3490 3491 if ( !points || !deltas ) 3492 ; /* failure, ignore it */ 3493 3494 else if ( localpoints == ALL_POINTS ) 3495 { 3496 #ifdef FT_DEBUG_LEVEL_TRACE 3497 int count = 0; 3498 #endif 3499 3500 3501 FT_TRACE7(( " CVT deltas:\n" )); 3502 3503 /* this means that there are deltas for every entry in cvt */ 3504 for ( j = 0; j < face->cvt_size; j++ ) 3505 { 3506 FT_Fixed old_cvt_delta; 3507 3508 3509 old_cvt_delta = cvt_deltas[j]; 3510 cvt_deltas[j] = old_cvt_delta + FT_MulFix( deltas[j], apply ); 3511 3512 #ifdef FT_DEBUG_LEVEL_TRACE 3513 if ( old_cvt_delta != cvt_deltas[j] ) 3514 { 3515 FT_TRACE7(( " %d: %f -> %f\n", 3516 j, 3517 ( FT_fdot6ToFixed( face->cvt[j] ) + 3518 old_cvt_delta ) / 65536.0, 3519 ( FT_fdot6ToFixed( face->cvt[j] ) + 3520 cvt_deltas[j] ) / 65536.0 )); 3521 count++; 3522 } 3523 #endif 3524 } 3525 3526 #ifdef FT_DEBUG_LEVEL_TRACE 3527 if ( !count ) 3528 FT_TRACE7(( " none\n" )); 3529 #endif 3530 } 3531 3532 else 3533 { 3534 #ifdef FT_DEBUG_LEVEL_TRACE 3535 int count = 0; 3536 #endif 3537 3538 3539 FT_TRACE7(( " CVT deltas:\n" )); 3540 3541 for ( j = 0; j < point_count; j++ ) 3542 { 3543 int pindex; 3544 FT_Fixed old_cvt_delta; 3545 3546 3547 pindex = points[j]; 3548 if ( (FT_ULong)pindex >= face->cvt_size ) 3549 continue; 3550 3551 old_cvt_delta = cvt_deltas[pindex]; 3552 cvt_deltas[pindex] = old_cvt_delta + FT_MulFix( deltas[j], apply ); 3553 3554 #ifdef FT_DEBUG_LEVEL_TRACE 3555 if ( old_cvt_delta != cvt_deltas[pindex] ) 3556 { 3557 FT_TRACE7(( " %d: %f -> %f\n", 3558 pindex, 3559 ( FT_fdot6ToFixed( face->cvt[pindex] ) + 3560 old_cvt_delta ) / 65536.0, 3561 ( FT_fdot6ToFixed( face->cvt[pindex] ) + 3562 cvt_deltas[pindex] ) / 65536.0 )); 3563 count++; 3564 } 3565 #endif 3566 } 3567 3568 #ifdef FT_DEBUG_LEVEL_TRACE 3569 if ( !count ) 3570 FT_TRACE7(( " none\n" )); 3571 #endif 3572 } 3573 3574 if ( localpoints != ALL_POINTS ) 3575 FT_FREE( localpoints ); 3576 FT_FREE( deltas ); 3577 3578 offsetToData += tupleDataSize; 3579 3580 FT_Stream_SeekSet( stream, here ); 3581 } 3582 3583 FT_TRACE5(( "\n" )); 3584 3585 for ( i = 0; i < face->cvt_size; i++ ) 3586 face->cvt[i] += FT_fixedToFdot6( cvt_deltas[i] ); 3587 3588 FExit: 3589 FT_FRAME_EXIT(); 3590 3591 Exit: 3592 if ( sharedpoints != ALL_POINTS ) 3593 FT_FREE( sharedpoints ); 3594 FT_FREE( tuple_coords ); 3595 FT_FREE( im_start_coords ); 3596 FT_FREE( im_end_coords ); 3597 FT_FREE( cvt_deltas ); 3598 3599 /* iterate over all FT_Size objects and set `cvt_ready' to -1 */ 3600 /* to trigger rescaling of all CVT values */ 3601 FT_List_Iterate( &root->sizes_list, 3602 tt_cvt_ready_iterator, 3603 NULL ); 3604 3605 return error; 3606 3607 #else /* !TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ 3608 3609 FT_UNUSED( face ); 3610 FT_UNUSED( stream ); 3611 3612 return FT_Err_Ok; 3613 3614 #endif /* !TT_CONFIG_OPTION_BYTECODE_INTERPRETER */ 3615 3616 } 3617 3618 3619 /* Shift the original coordinates of all points between indices `p1' */ 3620 /* and `p2', using the same difference as given by index `ref'. */ 3621 3622 /* modeled after `af_iup_shift' */ 3623 3624 static void tt_delta_shift(int p1,int p2,int ref,FT_Vector * in_points,FT_Vector * out_points)3625 tt_delta_shift( int p1, 3626 int p2, 3627 int ref, 3628 FT_Vector* in_points, 3629 FT_Vector* out_points ) 3630 { 3631 int p; 3632 FT_Vector delta; 3633 3634 3635 delta.x = out_points[ref].x - in_points[ref].x; 3636 delta.y = out_points[ref].y - in_points[ref].y; 3637 3638 if ( delta.x == 0 && delta.y == 0 ) 3639 return; 3640 3641 for ( p = p1; p < ref; p++ ) 3642 { 3643 out_points[p].x += delta.x; 3644 out_points[p].y += delta.y; 3645 } 3646 3647 for ( p = ref + 1; p <= p2; p++ ) 3648 { 3649 out_points[p].x += delta.x; 3650 out_points[p].y += delta.y; 3651 } 3652 } 3653 3654 3655 /* Interpolate the original coordinates of all points with indices */ 3656 /* between `p1' and `p2', using `ref1' and `ref2' as the reference */ 3657 /* point indices. */ 3658 3659 /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */ 3660 /* `Ins_IUP' with spec differences in handling ill-defined cases. */ 3661 static void tt_delta_interpolate(int p1,int p2,int ref1,int ref2,FT_Vector * in_points,FT_Vector * out_points)3662 tt_delta_interpolate( int p1, 3663 int p2, 3664 int ref1, 3665 int ref2, 3666 FT_Vector* in_points, 3667 FT_Vector* out_points ) 3668 { 3669 int p, i; 3670 3671 FT_Pos out, in1, in2, out1, out2, d1, d2; 3672 3673 3674 if ( p1 > p2 ) 3675 return; 3676 3677 /* handle both horizontal and vertical coordinates */ 3678 for ( i = 0; i <= 1; i++ ) 3679 { 3680 /* shift array pointers so that we can access `foo.y' as `foo.x' */ 3681 in_points = (FT_Vector*)( (FT_Pos*)in_points + i ); 3682 out_points = (FT_Vector*)( (FT_Pos*)out_points + i ); 3683 3684 if ( in_points[ref1].x > in_points[ref2].x ) 3685 { 3686 p = ref1; 3687 ref1 = ref2; 3688 ref2 = p; 3689 } 3690 3691 in1 = in_points[ref1].x; 3692 in2 = in_points[ref2].x; 3693 out1 = out_points[ref1].x; 3694 out2 = out_points[ref2].x; 3695 d1 = out1 - in1; 3696 d2 = out2 - in2; 3697 3698 /* If the reference points have the same coordinate but different */ 3699 /* delta, inferred delta is zero. Otherwise interpolate. */ 3700 if ( in1 != in2 || out1 == out2 ) 3701 { 3702 FT_Fixed scale = in1 != in2 ? FT_DivFix( out2 - out1, in2 - in1 ) 3703 : 0; 3704 3705 3706 for ( p = p1; p <= p2; p++ ) 3707 { 3708 out = in_points[p].x; 3709 3710 if ( out <= in1 ) 3711 out += d1; 3712 else if ( out >= in2 ) 3713 out += d2; 3714 else 3715 out = out1 + FT_MulFix( out - in1, scale ); 3716 3717 out_points[p].x = out; 3718 } 3719 } 3720 } 3721 } 3722 3723 3724 /* Interpolate points without delta values, similar to */ 3725 /* the `IUP' hinting instruction. */ 3726 3727 /* modeled after `Ins_IUP */ 3728 3729 static void tt_interpolate_deltas(FT_Outline * outline,FT_Vector * out_points,FT_Vector * in_points,FT_Bool * has_delta)3730 tt_interpolate_deltas( FT_Outline* outline, 3731 FT_Vector* out_points, 3732 FT_Vector* in_points, 3733 FT_Bool* has_delta ) 3734 { 3735 FT_Int first_point; 3736 FT_Int end_point; 3737 3738 FT_Int first_delta; 3739 FT_Int cur_delta; 3740 3741 FT_Int point; 3742 FT_Short contour; 3743 3744 3745 /* ignore empty outlines */ 3746 if ( !outline->n_contours ) 3747 return; 3748 3749 contour = 0; 3750 point = 0; 3751 3752 do 3753 { 3754 end_point = outline->contours[contour]; 3755 first_point = point; 3756 3757 /* search first point that has a delta */ 3758 while ( point <= end_point && !has_delta[point] ) 3759 point++; 3760 3761 if ( point <= end_point ) 3762 { 3763 first_delta = point; 3764 cur_delta = point; 3765 3766 point++; 3767 3768 while ( point <= end_point ) 3769 { 3770 /* search next point that has a delta */ 3771 /* and interpolate intermediate points */ 3772 if ( has_delta[point] ) 3773 { 3774 tt_delta_interpolate( cur_delta + 1, 3775 point - 1, 3776 cur_delta, 3777 point, 3778 in_points, 3779 out_points ); 3780 cur_delta = point; 3781 } 3782 3783 point++; 3784 } 3785 3786 /* shift contour if we only have a single delta */ 3787 if ( cur_delta == first_delta ) 3788 tt_delta_shift( first_point, 3789 end_point, 3790 cur_delta, 3791 in_points, 3792 out_points ); 3793 else 3794 { 3795 /* otherwise handle remaining points */ 3796 /* at the end and beginning of the contour */ 3797 tt_delta_interpolate( cur_delta + 1, 3798 end_point, 3799 cur_delta, 3800 first_delta, 3801 in_points, 3802 out_points ); 3803 3804 if ( first_delta > 0 ) 3805 tt_delta_interpolate( first_point, 3806 first_delta - 1, 3807 cur_delta, 3808 first_delta, 3809 in_points, 3810 out_points ); 3811 } 3812 } 3813 contour++; 3814 3815 } while ( contour < outline->n_contours ); 3816 } 3817 3818 3819 /************************************************************************** 3820 * 3821 * @Function: 3822 * TT_Vary_Apply_Glyph_Deltas 3823 * 3824 * @Description: 3825 * Apply the appropriate deltas to the current glyph. 3826 * 3827 * @Input: 3828 * face :: 3829 * A handle to the target face object. 3830 * 3831 * glyph_index :: 3832 * The index of the glyph being modified. 3833 * 3834 * n_points :: 3835 * The number of the points in the glyph, including 3836 * phantom points. 3837 * 3838 * @InOut: 3839 * outline :: 3840 * The outline to change. 3841 * 3842 * @Output: 3843 * unrounded :: 3844 * An array with `n_points' elements that is filled with unrounded 3845 * point coordinates (in 26.6 format). 3846 * 3847 * @Return: 3848 * FreeType error code. 0 means success. 3849 */ 3850 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)3851 TT_Vary_Apply_Glyph_Deltas( TT_Face face, 3852 FT_UInt glyph_index, 3853 FT_Outline* outline, 3854 FT_Vector* unrounded, 3855 FT_UInt n_points ) 3856 { 3857 FT_Error error; 3858 FT_Stream stream = face->root.stream; 3859 FT_Memory memory = stream->memory; 3860 3861 FT_Vector* points_org = NULL; /* coordinates in 16.16 format */ 3862 FT_Vector* points_out = NULL; /* coordinates in 16.16 format */ 3863 FT_Bool* has_delta = NULL; 3864 3865 FT_ULong glyph_start; 3866 3867 FT_UInt tupleCount; 3868 FT_ULong offsetToData; 3869 FT_ULong dataSize; 3870 3871 FT_ULong here; 3872 FT_UInt i, j; 3873 3874 FT_Fixed* tuple_coords = NULL; 3875 FT_Fixed* im_start_coords = NULL; 3876 FT_Fixed* im_end_coords = NULL; 3877 3878 GX_Blend blend = face->blend; 3879 3880 FT_UInt point_count; 3881 FT_UInt spoint_count = 0; 3882 3883 FT_UShort* sharedpoints = NULL; 3884 FT_UShort* localpoints = NULL; 3885 FT_UShort* points; 3886 3887 FT_Fixed* deltas_x = NULL; 3888 FT_Fixed* deltas_y = NULL; 3889 FT_Fixed* point_deltas_x = NULL; 3890 FT_Fixed* point_deltas_y = NULL; 3891 3892 3893 if ( !face->doblend || !blend ) 3894 return FT_THROW( Invalid_Argument ); 3895 3896 for ( i = 0; i < n_points; i++ ) 3897 { 3898 unrounded[i].x = INT_TO_F26DOT6( outline->points[i].x ); 3899 unrounded[i].y = INT_TO_F26DOT6( outline->points[i].y ); 3900 } 3901 3902 if ( glyph_index >= blend->gv_glyphcnt || 3903 blend->glyphoffsets[glyph_index] == 3904 blend->glyphoffsets[glyph_index + 1] ) 3905 { 3906 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 3907 " no variation data for glyph %d\n", glyph_index )); 3908 return FT_Err_Ok; 3909 } 3910 3911 if ( FT_NEW_ARRAY( points_org, n_points ) || 3912 FT_NEW_ARRAY( points_out, n_points ) || 3913 FT_NEW_ARRAY( has_delta, n_points ) ) 3914 goto Fail1; 3915 3916 dataSize = blend->glyphoffsets[glyph_index + 1] - 3917 blend->glyphoffsets[glyph_index]; 3918 3919 if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) || 3920 FT_FRAME_ENTER( dataSize ) ) 3921 goto Fail1; 3922 3923 glyph_start = FT_Stream_FTell( stream ); 3924 3925 /* each set of glyph variation data is formatted similarly to `cvar' */ 3926 3927 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || 3928 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || 3929 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) 3930 goto Fail2; 3931 3932 tupleCount = FT_GET_USHORT(); 3933 offsetToData = FT_GET_USHORT(); 3934 3935 /* rough sanity test */ 3936 if ( offsetToData > dataSize || 3937 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 > dataSize ) 3938 { 3939 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 3940 " invalid glyph variation array header\n" )); 3941 3942 error = FT_THROW( Invalid_Table ); 3943 goto Fail2; 3944 } 3945 3946 offsetToData += glyph_start; 3947 3948 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS ) 3949 { 3950 here = FT_Stream_FTell( stream ); 3951 3952 FT_Stream_SeekSet( stream, offsetToData ); 3953 3954 sharedpoints = ft_var_readpackedpoints( stream, 3955 blend->gvar_size, 3956 &spoint_count ); 3957 offsetToData = FT_Stream_FTell( stream ); 3958 3959 FT_Stream_SeekSet( stream, here ); 3960 } 3961 3962 FT_TRACE5(( "gvar: there %s %d tuple%s:\n", 3963 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are", 3964 tupleCount & GX_TC_TUPLE_COUNT_MASK, 3965 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" )); 3966 3967 if ( FT_NEW_ARRAY( point_deltas_x, n_points ) || 3968 FT_NEW_ARRAY( point_deltas_y, n_points ) ) 3969 goto Fail3; 3970 3971 for ( j = 0; j < n_points; j++ ) 3972 { 3973 points_org[j].x = FT_intToFixed( outline->points[j].x ); 3974 points_org[j].y = FT_intToFixed( outline->points[j].y ); 3975 } 3976 3977 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ ) 3978 { 3979 FT_UInt tupleDataSize; 3980 FT_UInt tupleIndex; 3981 FT_Fixed apply; 3982 3983 3984 FT_TRACE6(( " tuple %d:\n", i )); 3985 3986 tupleDataSize = FT_GET_USHORT(); 3987 tupleIndex = FT_GET_USHORT(); 3988 3989 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 3990 { 3991 for ( j = 0; j < blend->num_axis; j++ ) 3992 tuple_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() ); 3993 } 3994 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount ) 3995 { 3996 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 3997 " invalid tuple index\n" )); 3998 3999 error = FT_THROW( Invalid_Table ); 4000 goto Fail3; 4001 } 4002 else 4003 FT_MEM_COPY( 4004 tuple_coords, 4005 blend->tuplecoords + 4006 ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis, 4007 blend->num_axis * sizeof ( FT_Fixed ) ); 4008 4009 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 4010 { 4011 for ( j = 0; j < blend->num_axis; j++ ) 4012 im_start_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() ); 4013 for ( j = 0; j < blend->num_axis; j++ ) 4014 im_end_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() ); 4015 } 4016 4017 apply = ft_var_apply_tuple( blend, 4018 (FT_UShort)tupleIndex, 4019 tuple_coords, 4020 im_start_coords, 4021 im_end_coords ); 4022 4023 if ( apply == 0 ) /* tuple isn't active for our blend */ 4024 { 4025 offsetToData += tupleDataSize; 4026 continue; 4027 } 4028 4029 here = FT_Stream_FTell( stream ); 4030 4031 FT_Stream_SeekSet( stream, offsetToData ); 4032 4033 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) 4034 { 4035 localpoints = ft_var_readpackedpoints( stream, 4036 blend->gvar_size, 4037 &point_count ); 4038 points = localpoints; 4039 } 4040 else 4041 { 4042 points = sharedpoints; 4043 point_count = spoint_count; 4044 } 4045 4046 deltas_x = ft_var_readpackeddeltas( stream, 4047 blend->gvar_size, 4048 point_count == 0 ? n_points 4049 : point_count ); 4050 deltas_y = ft_var_readpackeddeltas( stream, 4051 blend->gvar_size, 4052 point_count == 0 ? n_points 4053 : point_count ); 4054 4055 if ( !points || !deltas_y || !deltas_x ) 4056 ; /* failure, ignore it */ 4057 4058 else if ( points == ALL_POINTS ) 4059 { 4060 #ifdef FT_DEBUG_LEVEL_TRACE 4061 int count = 0; 4062 #endif 4063 4064 4065 FT_TRACE7(( " point deltas:\n" )); 4066 4067 /* this means that there are deltas for every point in the glyph */ 4068 for ( j = 0; j < n_points; j++ ) 4069 { 4070 FT_Fixed old_point_delta_x = point_deltas_x[j]; 4071 FT_Fixed old_point_delta_y = point_deltas_y[j]; 4072 4073 FT_Fixed point_delta_x = FT_MulFix( deltas_x[j], apply ); 4074 FT_Fixed point_delta_y = FT_MulFix( deltas_y[j], apply ); 4075 4076 4077 if ( j < n_points - 4 ) 4078 { 4079 point_deltas_x[j] = old_point_delta_x + point_delta_x; 4080 point_deltas_y[j] = old_point_delta_y + point_delta_y; 4081 } 4082 else 4083 { 4084 /* To avoid double adjustment of advance width or height, */ 4085 /* adjust phantom points only if there is no HVAR or VVAR */ 4086 /* support, respectively. */ 4087 if ( j == ( n_points - 4 ) && 4088 !( face->variation_support & 4089 TT_FACE_FLAG_VAR_LSB ) ) 4090 point_deltas_x[j] = old_point_delta_x + point_delta_x; 4091 4092 else if ( j == ( n_points - 3 ) && 4093 !( face->variation_support & 4094 TT_FACE_FLAG_VAR_HADVANCE ) ) 4095 point_deltas_x[j] = old_point_delta_x + point_delta_x; 4096 4097 else if ( j == ( n_points - 2 ) && 4098 !( face->variation_support & 4099 TT_FACE_FLAG_VAR_TSB ) ) 4100 point_deltas_y[j] = old_point_delta_y + point_delta_y; 4101 4102 else if ( j == ( n_points - 1 ) && 4103 !( face->variation_support & 4104 TT_FACE_FLAG_VAR_VADVANCE ) ) 4105 point_deltas_y[j] = old_point_delta_y + point_delta_y; 4106 } 4107 4108 #ifdef FT_DEBUG_LEVEL_TRACE 4109 if ( point_delta_x || point_delta_y ) 4110 { 4111 FT_TRACE7(( " %d: (%f, %f) -> (%f, %f)\n", 4112 j, 4113 ( FT_intToFixed( outline->points[j].x ) + 4114 old_point_delta_x ) / 65536.0, 4115 ( FT_intToFixed( outline->points[j].y ) + 4116 old_point_delta_y ) / 65536.0, 4117 ( FT_intToFixed( outline->points[j].x ) + 4118 point_deltas_x[j] ) / 65536.0, 4119 ( FT_intToFixed( outline->points[j].y ) + 4120 point_deltas_y[j] ) / 65536.0 )); 4121 count++; 4122 } 4123 #endif 4124 } 4125 4126 #ifdef FT_DEBUG_LEVEL_TRACE 4127 if ( !count ) 4128 FT_TRACE7(( " none\n" )); 4129 #endif 4130 } 4131 4132 else 4133 { 4134 #ifdef FT_DEBUG_LEVEL_TRACE 4135 int count = 0; 4136 #endif 4137 4138 4139 /* we have to interpolate the missing deltas similar to the */ 4140 /* IUP bytecode instruction */ 4141 for ( j = 0; j < n_points; j++ ) 4142 { 4143 has_delta[j] = FALSE; 4144 points_out[j] = points_org[j]; 4145 } 4146 4147 for ( j = 0; j < point_count; j++ ) 4148 { 4149 FT_UShort idx = points[j]; 4150 4151 4152 if ( idx >= n_points ) 4153 continue; 4154 4155 has_delta[idx] = TRUE; 4156 4157 points_out[idx].x += FT_MulFix( deltas_x[j], apply ); 4158 points_out[idx].y += FT_MulFix( deltas_y[j], apply ); 4159 } 4160 4161 /* no need to handle phantom points here, */ 4162 /* since solitary points can't be interpolated */ 4163 tt_interpolate_deltas( outline, 4164 points_out, 4165 points_org, 4166 has_delta ); 4167 4168 FT_TRACE7(( " point deltas:\n" )); 4169 4170 for ( j = 0; j < n_points; j++ ) 4171 { 4172 FT_Fixed old_point_delta_x = point_deltas_x[j]; 4173 FT_Fixed old_point_delta_y = point_deltas_y[j]; 4174 4175 FT_Pos point_delta_x = points_out[j].x - points_org[j].x; 4176 FT_Pos point_delta_y = points_out[j].y - points_org[j].y; 4177 4178 4179 if ( j < n_points - 4 ) 4180 { 4181 point_deltas_x[j] = old_point_delta_x + point_delta_x; 4182 point_deltas_y[j] = old_point_delta_y + point_delta_y; 4183 } 4184 else 4185 { 4186 /* To avoid double adjustment of advance width or height, */ 4187 /* adjust phantom points only if there is no HVAR or VVAR */ 4188 /* support, respectively. */ 4189 if ( j == ( n_points - 4 ) && 4190 !( face->variation_support & 4191 TT_FACE_FLAG_VAR_LSB ) ) 4192 point_deltas_x[j] = old_point_delta_x + point_delta_x; 4193 4194 else if ( j == ( n_points - 3 ) && 4195 !( face->variation_support & 4196 TT_FACE_FLAG_VAR_HADVANCE ) ) 4197 point_deltas_x[j] = old_point_delta_x + point_delta_x; 4198 4199 else if ( j == ( n_points - 2 ) && 4200 !( face->variation_support & 4201 TT_FACE_FLAG_VAR_TSB ) ) 4202 point_deltas_y[j] = old_point_delta_y + point_delta_y; 4203 4204 else if ( j == ( n_points - 1 ) && 4205 !( face->variation_support & 4206 TT_FACE_FLAG_VAR_VADVANCE ) ) 4207 point_deltas_y[j] = old_point_delta_y + point_delta_y; 4208 } 4209 4210 #ifdef FT_DEBUG_LEVEL_TRACE 4211 if ( point_delta_x || point_delta_y ) 4212 { 4213 FT_TRACE7(( " %d: (%f, %f) -> (%f, %f)\n", 4214 j, 4215 ( FT_intToFixed( outline->points[j].x ) + 4216 old_point_delta_x ) / 65536.0, 4217 ( FT_intToFixed( outline->points[j].y ) + 4218 old_point_delta_y ) / 65536.0, 4219 ( FT_intToFixed( outline->points[j].x ) + 4220 point_deltas_x[j] ) / 65536.0, 4221 ( FT_intToFixed( outline->points[j].y ) + 4222 point_deltas_y[j] ) / 65536.0 )); 4223 count++; 4224 } 4225 #endif 4226 } 4227 4228 #ifdef FT_DEBUG_LEVEL_TRACE 4229 if ( !count ) 4230 FT_TRACE7(( " none\n" )); 4231 #endif 4232 } 4233 4234 if ( localpoints != ALL_POINTS ) 4235 FT_FREE( localpoints ); 4236 FT_FREE( deltas_x ); 4237 FT_FREE( deltas_y ); 4238 4239 offsetToData += tupleDataSize; 4240 4241 FT_Stream_SeekSet( stream, here ); 4242 } 4243 4244 FT_TRACE5(( "\n" )); 4245 4246 for ( i = 0; i < n_points; i++ ) 4247 { 4248 unrounded[i].x += FT_fixedToFdot6( point_deltas_x[i] ); 4249 unrounded[i].y += FT_fixedToFdot6( point_deltas_y[i] ); 4250 4251 outline->points[i].x += FT_fixedToInt( point_deltas_x[i] ); 4252 outline->points[i].y += FT_fixedToInt( point_deltas_y[i] ); 4253 } 4254 4255 Fail3: 4256 FT_FREE( point_deltas_x ); 4257 FT_FREE( point_deltas_y ); 4258 4259 Fail2: 4260 if ( sharedpoints != ALL_POINTS ) 4261 FT_FREE( sharedpoints ); 4262 FT_FREE( tuple_coords ); 4263 FT_FREE( im_start_coords ); 4264 FT_FREE( im_end_coords ); 4265 4266 FT_FRAME_EXIT(); 4267 4268 Fail1: 4269 FT_FREE( points_org ); 4270 FT_FREE( points_out ); 4271 FT_FREE( has_delta ); 4272 4273 return error; 4274 } 4275 4276 4277 /************************************************************************** 4278 * 4279 * @Function: 4280 * tt_get_var_blend 4281 * 4282 * @Description: 4283 * An extended internal version of `TT_Get_MM_Blend' that returns 4284 * pointers instead of copying data, without any initialization of 4285 * the MM machinery in case it isn't loaded yet. 4286 */ 4287 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)4288 tt_get_var_blend( TT_Face face, 4289 FT_UInt *num_coords, 4290 FT_Fixed* *coords, 4291 FT_Fixed* *normalizedcoords, 4292 FT_MM_Var* *mm_var ) 4293 { 4294 if ( face->blend ) 4295 { 4296 if ( num_coords ) 4297 *num_coords = face->blend->num_axis; 4298 if ( coords ) 4299 *coords = face->blend->coords; 4300 if ( normalizedcoords ) 4301 *normalizedcoords = face->blend->normalizedcoords; 4302 if ( mm_var ) 4303 *mm_var = face->blend->mmvar; 4304 } 4305 else 4306 { 4307 if ( num_coords ) 4308 *num_coords = 0; 4309 if ( coords ) 4310 *coords = NULL; 4311 if ( mm_var ) 4312 *mm_var = NULL; 4313 } 4314 4315 return FT_Err_Ok; 4316 } 4317 4318 4319 static void ft_var_done_item_variation_store(TT_Face face,GX_ItemVarStore itemStore)4320 ft_var_done_item_variation_store( TT_Face face, 4321 GX_ItemVarStore itemStore ) 4322 { 4323 FT_Memory memory = FT_FACE_MEMORY( face ); 4324 FT_UInt i; 4325 4326 4327 if ( itemStore->varData ) 4328 { 4329 for ( i = 0; i < itemStore->dataCount; i++ ) 4330 { 4331 FT_FREE( itemStore->varData[i].regionIndices ); 4332 FT_FREE( itemStore->varData[i].deltaSet ); 4333 } 4334 4335 FT_FREE( itemStore->varData ); 4336 } 4337 4338 if ( itemStore->varRegionList ) 4339 { 4340 for ( i = 0; i < itemStore->regionCount; i++ ) 4341 FT_FREE( itemStore->varRegionList[i].axisList ); 4342 4343 FT_FREE( itemStore->varRegionList ); 4344 } 4345 } 4346 4347 4348 /************************************************************************** 4349 * 4350 * @Function: 4351 * tt_done_blend 4352 * 4353 * @Description: 4354 * Free the blend internal data structure. 4355 */ 4356 FT_LOCAL_DEF( void ) tt_done_blend(TT_Face face)4357 tt_done_blend( TT_Face face ) 4358 { 4359 FT_Memory memory = FT_FACE_MEMORY( face ); 4360 GX_Blend blend = face->blend; 4361 4362 4363 if ( blend ) 4364 { 4365 FT_UInt i, num_axes; 4366 4367 4368 /* blend->num_axis might not be set up yet */ 4369 num_axes = blend->mmvar->num_axis; 4370 4371 FT_FREE( blend->coords ); 4372 FT_FREE( blend->normalizedcoords ); 4373 FT_FREE( blend->normalized_stylecoords ); 4374 FT_FREE( blend->mmvar ); 4375 4376 if ( blend->avar_segment ) 4377 { 4378 for ( i = 0; i < num_axes; i++ ) 4379 FT_FREE( blend->avar_segment[i].correspondence ); 4380 FT_FREE( blend->avar_segment ); 4381 } 4382 4383 if ( blend->hvar_table ) 4384 { 4385 ft_var_done_item_variation_store( face, 4386 &blend->hvar_table->itemStore ); 4387 4388 FT_FREE( blend->hvar_table->widthMap.innerIndex ); 4389 FT_FREE( blend->hvar_table->widthMap.outerIndex ); 4390 FT_FREE( blend->hvar_table ); 4391 } 4392 4393 if ( blend->vvar_table ) 4394 { 4395 ft_var_done_item_variation_store( face, 4396 &blend->vvar_table->itemStore ); 4397 4398 FT_FREE( blend->vvar_table->widthMap.innerIndex ); 4399 FT_FREE( blend->vvar_table->widthMap.outerIndex ); 4400 FT_FREE( blend->vvar_table ); 4401 } 4402 4403 if ( blend->mvar_table ) 4404 { 4405 ft_var_done_item_variation_store( face, 4406 &blend->mvar_table->itemStore ); 4407 4408 FT_FREE( blend->mvar_table->values ); 4409 FT_FREE( blend->mvar_table ); 4410 } 4411 4412 FT_FREE( blend->tuplecoords ); 4413 FT_FREE( blend->glyphoffsets ); 4414 FT_FREE( blend ); 4415 } 4416 } 4417 4418 #else /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */ 4419 4420 /* ANSI C doesn't like empty source files */ 4421 typedef int _tt_gxvar_dummy; 4422 4423 #endif /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */ 4424 4425 4426 /* END */ 4427