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