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