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