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