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 a = mmvar->axis; 1737 for ( i = 0; i < num_coords; i++, a++ ) 1738 { 1739 FT_Fixed coord = coords[i]; 1740 1741 1742 FT_TRACE5(( " %d: %.5f\n", i, coord / 65536.0 )); 1743 if ( coord > a->maximum || coord < a->minimum ) 1744 { 1745 FT_TRACE1(( 1746 "ft_var_to_normalized: design coordinate %.5f\n" 1747 " is out of range [%.5f;%.5f]; clamping\n", 1748 coord / 65536.0, 1749 a->minimum / 65536.0, 1750 a->maximum / 65536.0 )); 1751 1752 if ( coord > a->maximum ) 1753 coord = a->maximum; 1754 else 1755 coord = a->minimum; 1756 } 1757 1758 if ( coord < a->def ) 1759 normalized[i] = -FT_DivFix( coord - a->def, 1760 a->minimum - a->def ); 1761 else if ( coord > a->def ) 1762 normalized[i] = FT_DivFix( coord - a->def, 1763 a->maximum - a->def ); 1764 else 1765 normalized[i] = 0; 1766 } 1767 1768 FT_TRACE5(( "\n" )); 1769 1770 for ( ; i < mmvar->num_axis; i++ ) 1771 normalized[i] = 0; 1772 1773 if ( blend->avar_segment ) 1774 { 1775 FT_TRACE5(( "normalized design coordinates" 1776 " before applying `avar' data:\n" )); 1777 1778 av = blend->avar_segment; 1779 for ( i = 0; i < mmvar->num_axis; i++, av++ ) 1780 { 1781 for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) 1782 { 1783 if ( normalized[i] < av->correspondence[j].fromCoord ) 1784 { 1785 FT_TRACE5(( " %.5f\n", normalized[i] / 65536.0 )); 1786 1787 normalized[i] = 1788 FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord, 1789 av->correspondence[j].toCoord - 1790 av->correspondence[j - 1].toCoord, 1791 av->correspondence[j].fromCoord - 1792 av->correspondence[j - 1].fromCoord ) + 1793 av->correspondence[j - 1].toCoord; 1794 break; 1795 } 1796 } 1797 } 1798 } 1799 } 1800 1801 1802 /* convert from normalized coordinates to design coordinates */ 1803 1804 static void 1805 ft_var_to_design( TT_Face face, 1806 FT_UInt num_coords, 1807 FT_Fixed* coords, 1808 FT_Fixed* design ) 1809 { 1810 GX_Blend blend; 1811 FT_MM_Var* mmvar; 1812 FT_Var_Axis* a; 1813 1814 FT_UInt i, j, nc; 1815 1816 1817 blend = face->blend; 1818 1819 nc = num_coords; 1820 if ( num_coords > blend->num_axis ) 1821 { 1822 FT_TRACE2(( "ft_var_to_design:" 1823 " only using first %d of %d coordinates\n", 1824 blend->num_axis, num_coords )); 1825 nc = blend->num_axis; 1826 } 1827 1828 for ( i = 0; i < nc; i++ ) 1829 design[i] = coords[i]; 1830 1831 for ( ; i < num_coords; i++ ) 1832 design[i] = 0; 1833 1834 if ( blend->avar_segment ) 1835 { 1836 GX_AVarSegment av = blend->avar_segment; 1837 1838 1839 FT_TRACE5(( "design coordinates" 1840 " after removing `avar' distortion:\n" )); 1841 1842 for ( i = 0; i < nc; i++, av++ ) 1843 { 1844 for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) 1845 { 1846 if ( design[i] < av->correspondence[j].toCoord ) 1847 { 1848 design[i] = 1849 FT_MulDiv( design[i] - av->correspondence[j - 1].toCoord, 1850 av->correspondence[j].fromCoord - 1851 av->correspondence[j - 1].fromCoord, 1852 av->correspondence[j].toCoord - 1853 av->correspondence[j - 1].toCoord ) + 1854 av->correspondence[j - 1].fromCoord; 1855 1856 FT_TRACE5(( " %.5f\n", design[i] / 65536.0 )); 1857 break; 1858 } 1859 } 1860 } 1861 } 1862 1863 mmvar = blend->mmvar; 1864 a = mmvar->axis; 1865 1866 for ( i = 0; i < nc; i++, a++ ) 1867 { 1868 if ( design[i] < 0 ) 1869 design[i] = a->def + FT_MulFix( design[i], 1870 a->def - a->minimum ); 1871 else if ( design[i] > 0 ) 1872 design[i] = a->def + FT_MulFix( design[i], 1873 a->maximum - a->def ); 1874 else 1875 design[i] = a->def; 1876 } 1877 } 1878 1879 1880 /*************************************************************************/ 1881 /*************************************************************************/ 1882 /***** *****/ 1883 /***** MULTIPLE MASTERS SERVICE FUNCTIONS *****/ 1884 /***** *****/ 1885 /*************************************************************************/ 1886 /*************************************************************************/ 1887 1888 1889 typedef struct GX_FVar_Head_ 1890 { 1891 FT_Long version; 1892 FT_UShort offsetToData; 1893 FT_UShort axisCount; 1894 FT_UShort axisSize; 1895 FT_UShort instanceCount; 1896 FT_UShort instanceSize; 1897 1898 } GX_FVar_Head; 1899 1900 1901 typedef struct fvar_axis_ 1902 { 1903 FT_ULong axisTag; 1904 FT_Fixed minValue; 1905 FT_Fixed defaultValue; 1906 FT_Fixed maxValue; 1907 FT_UShort flags; 1908 FT_UShort nameID; 1909 1910 } GX_FVar_Axis; 1911 1912 1913 /*************************************************************************/ 1914 /* */ 1915 /* <Function> */ 1916 /* TT_Get_MM_Var */ 1917 /* */ 1918 /* <Description> */ 1919 /* Check that the font's `fvar' table is valid, parse it, and return */ 1920 /* those data. It also loads (and parses) the `MVAR' table, if */ 1921 /* possible. */ 1922 /* */ 1923 /* <InOut> */ 1924 /* face :: The font face. */ 1925 /* TT_Get_MM_Var initializes the blend structure. */ 1926 /* */ 1927 /* <Output> */ 1928 /* master :: The `fvar' data (must be freed by caller). Can be NULL, */ 1929 /* which makes this function simply load MM support. */ 1930 /* */ 1931 /* <Return> */ 1932 /* FreeType error code. 0 means success. */ 1933 /* */ 1934 FT_LOCAL_DEF( FT_Error ) 1935 TT_Get_MM_Var( TT_Face face, 1936 FT_MM_Var* *master ) 1937 { 1938 FT_Stream stream = face->root.stream; 1939 FT_Memory memory = face->root.memory; 1940 FT_ULong table_len; 1941 FT_Error error = FT_Err_Ok; 1942 FT_ULong fvar_start = 0; 1943 FT_UInt i, j; 1944 FT_MM_Var* mmvar = NULL; 1945 FT_Fixed* next_coords; 1946 FT_Fixed* nsc; 1947 FT_String* next_name; 1948 FT_Var_Axis* a; 1949 FT_Fixed* c; 1950 FT_Var_Named_Style* ns; 1951 GX_FVar_Head fvar_head; 1952 FT_Bool usePsName = 0; 1953 FT_UInt num_instances; 1954 FT_UInt num_axes; 1955 FT_UShort* axis_flags; 1956 1957 FT_Offset mmvar_size; 1958 FT_Offset axis_flags_size; 1959 FT_Offset axis_size; 1960 FT_Offset namedstyle_size; 1961 FT_Offset next_coords_size; 1962 FT_Offset next_name_size; 1963 1964 FT_Bool need_init; 1965 1966 static const FT_Frame_Field fvar_fields[] = 1967 { 1968 1969 #undef FT_STRUCTURE 1970 #define FT_STRUCTURE GX_FVar_Head 1971 1972 FT_FRAME_START( 16 ), 1973 FT_FRAME_LONG ( version ), 1974 FT_FRAME_USHORT ( offsetToData ), 1975 FT_FRAME_SKIP_SHORT, 1976 FT_FRAME_USHORT ( axisCount ), 1977 FT_FRAME_USHORT ( axisSize ), 1978 FT_FRAME_USHORT ( instanceCount ), 1979 FT_FRAME_USHORT ( instanceSize ), 1980 FT_FRAME_END 1981 }; 1982 1983 static const FT_Frame_Field fvaraxis_fields[] = 1984 { 1985 1986 #undef FT_STRUCTURE 1987 #define FT_STRUCTURE GX_FVar_Axis 1988 1989 FT_FRAME_START( 20 ), 1990 FT_FRAME_ULONG ( axisTag ), 1991 FT_FRAME_LONG ( minValue ), 1992 FT_FRAME_LONG ( defaultValue ), 1993 FT_FRAME_LONG ( maxValue ), 1994 FT_FRAME_USHORT( flags ), 1995 FT_FRAME_USHORT( nameID ), 1996 FT_FRAME_END 1997 }; 1998 1999 2000 /* read the font data and set up the internal representation */ 2001 /* if not already done */ 2002 2003 need_init = !face->blend; 2004 2005 if ( need_init ) 2006 { 2007 FT_TRACE2(( "FVAR " )); 2008 2009 /* both `fvar' and `gvar' must be present */ 2010 if ( FT_SET_ERROR( face->goto_table( face, TTAG_gvar, 2011 stream, &table_len ) ) ) 2012 { 2013 /* CFF2 is an alternate to gvar here */ 2014 if ( FT_SET_ERROR( face->goto_table( face, TTAG_CFF2, 2015 stream, &table_len ) ) ) 2016 { 2017 FT_TRACE1(( "\n" 2018 "TT_Get_MM_Var: `gvar' or `CFF2' table is missing\n" )); 2019 goto Exit; 2020 } 2021 } 2022 2023 if ( FT_SET_ERROR( face->goto_table( face, TTAG_fvar, 2024 stream, &table_len ) ) ) 2025 { 2026 FT_TRACE1(( "is missing\n" )); 2027 goto Exit; 2028 } 2029 2030 fvar_start = FT_STREAM_POS( ); 2031 2032 /* the validity of the `fvar' header data was already checked */ 2033 /* in function `sfnt_init_face' */ 2034 if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) ) 2035 goto Exit; 2036 2037 usePsName = FT_BOOL( fvar_head.instanceSize == 2038 6 + 4 * fvar_head.axisCount ); 2039 2040 FT_TRACE2(( "loaded\n" )); 2041 2042 FT_TRACE5(( "%d variation ax%s\n", 2043 fvar_head.axisCount, 2044 fvar_head.axisCount == 1 ? "is" : "es" )); 2045 2046 if ( FT_NEW( face->blend ) ) 2047 goto Exit; 2048 2049 num_axes = fvar_head.axisCount; 2050 face->blend->num_axis = num_axes; 2051 } 2052 else 2053 num_axes = face->blend->num_axis; 2054 2055 /* `num_instances' holds the number of all named instances, */ 2056 /* including the default instance which might be missing */ 2057 /* in fvar's table of named instances */ 2058 num_instances = (FT_UInt)face->root.style_flags >> 16; 2059 2060 /* prepare storage area for MM data; this cannot overflow */ 2061 /* 32-bit arithmetic because of the size limits used in the */ 2062 /* `fvar' table validity check in `sfnt_init_face' */ 2063 2064 /* the various `*_size' variables, which we also use as */ 2065 /* offsets into the `mmlen' array, must be multiples of the */ 2066 /* pointer size (except the last one); without such an */ 2067 /* alignment there might be runtime errors due to */ 2068 /* misaligned addresses */ 2069 #undef ALIGN_SIZE 2070 #define ALIGN_SIZE( n ) \ 2071 ( ( (n) + sizeof (void*) - 1 ) & ~( sizeof (void*) - 1 ) ) 2072 2073 mmvar_size = ALIGN_SIZE( sizeof ( FT_MM_Var ) ); 2074 axis_flags_size = ALIGN_SIZE( num_axes * 2075 sizeof ( FT_UShort ) ); 2076 axis_size = ALIGN_SIZE( num_axes * 2077 sizeof ( FT_Var_Axis ) ); 2078 namedstyle_size = ALIGN_SIZE( num_instances * 2079 sizeof ( FT_Var_Named_Style ) ); 2080 next_coords_size = ALIGN_SIZE( num_instances * 2081 num_axes * 2082 sizeof ( FT_Fixed ) ); 2083 next_name_size = num_axes * 5; 2084 2085 if ( need_init ) 2086 { 2087 face->blend->mmvar_len = mmvar_size + 2088 axis_flags_size + 2089 axis_size + 2090 namedstyle_size + 2091 next_coords_size + 2092 next_name_size; 2093 2094 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) ) 2095 goto Exit; 2096 face->blend->mmvar = mmvar; 2097 2098 /* set up pointers and offsets into the `mmvar' array; */ 2099 /* the data gets filled in later on */ 2100 2101 mmvar->num_axis = 2102 num_axes; 2103 mmvar->num_designs = 2104 ~0U; /* meaningless in this context; each glyph */ 2105 /* may have a different number of designs */ 2106 /* (or tuples, as called by Apple) */ 2107 mmvar->num_namedstyles = 2108 num_instances; 2109 2110 /* alas, no public field in `FT_Var_Axis' for axis flags */ 2111 axis_flags = 2112 (FT_UShort*)( (char*)mmvar + mmvar_size ); 2113 mmvar->axis = 2114 (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size ); 2115 mmvar->namedstyle = 2116 (FT_Var_Named_Style*)( (char*)mmvar->axis + axis_size ); 2117 2118 next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle + 2119 namedstyle_size ); 2120 for ( i = 0; i < num_instances; i++ ) 2121 { 2122 mmvar->namedstyle[i].coords = next_coords; 2123 next_coords += num_axes; 2124 } 2125 2126 next_name = (FT_String*)( (char*)mmvar->namedstyle + 2127 namedstyle_size + next_coords_size ); 2128 for ( i = 0; i < num_axes; i++ ) 2129 { 2130 mmvar->axis[i].name = next_name; 2131 next_name += 5; 2132 } 2133 2134 /* now fill in the data */ 2135 2136 if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) ) 2137 goto Exit; 2138 2139 a = mmvar->axis; 2140 for ( i = 0; i < num_axes; i++ ) 2141 { 2142 GX_FVar_Axis axis_rec; 2143 2144 #ifdef FT_DEBUG_LEVEL_TRACE 2145 int invalid = 0; 2146 #endif 2147 2148 2149 if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) ) 2150 goto Exit; 2151 a->tag = axis_rec.axisTag; 2152 a->minimum = axis_rec.minValue; 2153 a->def = axis_rec.defaultValue; 2154 a->maximum = axis_rec.maxValue; 2155 a->strid = axis_rec.nameID; 2156 2157 a->name[0] = (FT_String)( a->tag >> 24 ); 2158 a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF ); 2159 a->name[2] = (FT_String)( ( a->tag >> 8 ) & 0xFF ); 2160 a->name[3] = (FT_String)( ( a->tag ) & 0xFF ); 2161 a->name[4] = '\0'; 2162 2163 *axis_flags = axis_rec.flags; 2164 2165 if ( a->minimum > a->def || 2166 a->def > a->maximum ) 2167 { 2168 a->minimum = a->def; 2169 a->maximum = a->def; 2170 2171 #ifdef FT_DEBUG_LEVEL_TRACE 2172 invalid = 1; 2173 #endif 2174 } 2175 2176 #ifdef FT_DEBUG_LEVEL_TRACE 2177 if ( i == 0 ) 2178 FT_TRACE5(( " idx tag " 2179 /* " XXX `XXXX'" */ 2180 " minimum default maximum flags\n" )); 2181 /* " XXXX.XXXXX XXXX.XXXXX XXXX.XXXXX 0xXXXX" */ 2182 2183 FT_TRACE5(( " %3d `%s'" 2184 " %10.5f %10.5f %10.5f 0x%04X%s\n", 2185 i, 2186 a->name, 2187 a->minimum / 65536.0, 2188 a->def / 65536.0, 2189 a->maximum / 65536.0, 2190 *axis_flags, 2191 invalid ? " (invalid, disabled)" : "" )); 2192 #endif 2193 2194 a++; 2195 axis_flags++; 2196 } 2197 2198 FT_TRACE5(( "\n" )); 2199 2200 /* named instance coordinates are stored as design coordinates; */ 2201 /* we have to convert them to normalized coordinates also */ 2202 if ( FT_NEW_ARRAY( face->blend->normalized_stylecoords, 2203 num_axes * num_instances ) ) 2204 goto Exit; 2205 2206 if ( fvar_head.instanceCount && !face->blend->avar_loaded ) 2207 { 2208 FT_ULong offset = FT_STREAM_POS(); 2209 2210 2211 ft_var_load_avar( face ); 2212 2213 if ( FT_STREAM_SEEK( offset ) ) 2214 goto Exit; 2215 } 2216 2217 FT_TRACE5(( "%d instance%s\n", 2218 fvar_head.instanceCount, 2219 fvar_head.instanceCount == 1 ? "" : "s" )); 2220 2221 ns = mmvar->namedstyle; 2222 nsc = face->blend->normalized_stylecoords; 2223 for ( i = 0; i < fvar_head.instanceCount; i++, ns++ ) 2224 { 2225 /* PostScript names add 2 bytes to the instance record size */ 2226 if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) + 2227 4L * num_axes ) ) 2228 goto Exit; 2229 2230 ns->strid = FT_GET_USHORT(); 2231 (void) /* flags = */ FT_GET_USHORT(); 2232 2233 c = ns->coords; 2234 for ( j = 0; j < num_axes; j++, c++ ) 2235 *c = FT_GET_LONG(); 2236 2237 /* valid psid values are 6, [256;32767], and 0xFFFF */ 2238 if ( usePsName ) 2239 ns->psid = FT_GET_USHORT(); 2240 else 2241 ns->psid = 0xFFFF; 2242 2243 #ifdef FT_DEBUG_LEVEL_TRACE 2244 { 2245 SFNT_Service sfnt = (SFNT_Service)face->sfnt; 2246 2247 FT_String* strname = NULL; 2248 FT_String* psname = NULL; 2249 2250 FT_ULong pos; 2251 2252 2253 pos = FT_STREAM_POS(); 2254 2255 if ( ns->strid != 0xFFFF ) 2256 { 2257 (void)sfnt->get_name( face, 2258 (FT_UShort)ns->strid, 2259 &strname ); 2260 if ( strname && !ft_strcmp( strname, ".notdef" ) ) 2261 strname = NULL; 2262 } 2263 2264 if ( ns->psid != 0xFFFF ) 2265 { 2266 (void)sfnt->get_name( face, 2267 (FT_UShort)ns->psid, 2268 &psname ); 2269 if ( psname && !ft_strcmp( psname, ".notdef" ) ) 2270 psname = NULL; 2271 } 2272 2273 (void)FT_STREAM_SEEK( pos ); 2274 2275 FT_TRACE5(( " instance %d (%s%s%s, %s%s%s)\n", 2276 i, 2277 strname ? "name: `" : "", 2278 strname ? strname : "unnamed", 2279 strname ? "'" : "", 2280 psname ? "PS name: `" : "", 2281 psname ? psname : "no PS name", 2282 psname ? "'" : "" )); 2283 } 2284 #endif /* FT_DEBUG_LEVEL_TRACE */ 2285 2286 ft_var_to_normalized( face, num_axes, ns->coords, nsc ); 2287 nsc += num_axes; 2288 2289 FT_FRAME_EXIT(); 2290 } 2291 2292 if ( num_instances != fvar_head.instanceCount ) 2293 { 2294 SFNT_Service sfnt = (SFNT_Service)face->sfnt; 2295 2296 FT_Int found, dummy1, dummy2; 2297 FT_UInt strid = ~0U; 2298 2299 2300 /* the default instance is missing in array the */ 2301 /* of named instances; try to synthesize an entry */ 2302 found = sfnt->get_name_id( face, 2303 TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY, 2304 &dummy1, 2305 &dummy2 ); 2306 if ( found ) 2307 strid = TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY; 2308 else 2309 { 2310 found = sfnt->get_name_id( face, 2311 TT_NAME_ID_FONT_SUBFAMILY, 2312 &dummy1, 2313 &dummy2 ); 2314 if ( found ) 2315 strid = TT_NAME_ID_FONT_SUBFAMILY; 2316 } 2317 2318 if ( found ) 2319 { 2320 found = sfnt->get_name_id( face, 2321 TT_NAME_ID_PS_NAME, 2322 &dummy1, 2323 &dummy2 ); 2324 if ( found ) 2325 { 2326 FT_TRACE5(( "TT_Get_MM_Var:" 2327 " Adding default instance to named instances\n" )); 2328 2329 ns = &mmvar->namedstyle[fvar_head.instanceCount]; 2330 2331 ns->strid = strid; 2332 ns->psid = TT_NAME_ID_PS_NAME; 2333 2334 a = mmvar->axis; 2335 c = ns->coords; 2336 for ( j = 0; j < num_axes; j++, a++, c++ ) 2337 *c = a->def; 2338 } 2339 } 2340 } 2341 2342 ft_var_load_mvar( face ); 2343 } 2344 2345 /* fill the output array if requested */ 2346 2347 if ( master ) 2348 { 2349 FT_UInt n; 2350 2351 2352 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) ) 2353 goto Exit; 2354 FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len ); 2355 2356 axis_flags = 2357 (FT_UShort*)( (char*)mmvar + mmvar_size ); 2358 mmvar->axis = 2359 (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size ); 2360 mmvar->namedstyle = 2361 (FT_Var_Named_Style*)( (char*)mmvar->axis+ axis_size ); 2362 2363 next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle + 2364 namedstyle_size ); 2365 for ( n = 0; n < mmvar->num_namedstyles; n++ ) 2366 { 2367 mmvar->namedstyle[n].coords = next_coords; 2368 next_coords += num_axes; 2369 } 2370 2371 a = mmvar->axis; 2372 next_name = (FT_String*)( (char*)mmvar->namedstyle + 2373 namedstyle_size + next_coords_size ); 2374 for ( n = 0; n < num_axes; n++ ) 2375 { 2376 a->name = next_name; 2377 2378 /* standard PostScript names for some standard apple tags */ 2379 if ( a->tag == TTAG_wght ) 2380 a->name = (char*)"Weight"; 2381 else if ( a->tag == TTAG_wdth ) 2382 a->name = (char*)"Width"; 2383 else if ( a->tag == TTAG_opsz ) 2384 a->name = (char*)"OpticalSize"; 2385 else if ( a->tag == TTAG_slnt ) 2386 a->name = (char*)"Slant"; 2387 2388 next_name += 5; 2389 a++; 2390 } 2391 2392 *master = mmvar; 2393 } 2394 2395 Exit: 2396 return error; 2397 } 2398 2399 2400 static FT_Error 2401 tt_set_mm_blend( TT_Face face, 2402 FT_UInt num_coords, 2403 FT_Fixed* coords, 2404 FT_Bool set_design_coords ) 2405 { 2406 FT_Error error = FT_Err_Ok; 2407 GX_Blend blend; 2408 FT_MM_Var* mmvar; 2409 FT_UInt i; 2410 2411 FT_Bool all_design_coords = FALSE; 2412 2413 FT_Memory memory = face->root.memory; 2414 2415 enum 2416 { 2417 mcvt_retain, 2418 mcvt_modify, 2419 mcvt_load 2420 2421 } manageCvt; 2422 2423 2424 face->doblend = FALSE; 2425 2426 if ( !face->blend ) 2427 { 2428 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 2429 goto Exit; 2430 } 2431 2432 blend = face->blend; 2433 mmvar = blend->mmvar; 2434 2435 if ( num_coords > mmvar->num_axis ) 2436 { 2437 FT_TRACE2(( "TT_Set_MM_Blend:" 2438 " only using first %d of %d coordinates\n", 2439 mmvar->num_axis, num_coords )); 2440 num_coords = mmvar->num_axis; 2441 } 2442 2443 FT_TRACE5(( "TT_Set_MM_Blend:\n" 2444 " normalized design coordinates:\n" )); 2445 2446 for ( i = 0; i < num_coords; i++ ) 2447 { 2448 FT_TRACE5(( " %.5f\n", coords[i] / 65536.0 )); 2449 if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L ) 2450 { 2451 FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n" 2452 " is out of range [-1;1]\n", 2453 coords[i] / 65536.0 )); 2454 error = FT_THROW( Invalid_Argument ); 2455 goto Exit; 2456 } 2457 } 2458 2459 FT_TRACE5(( "\n" )); 2460 2461 if ( !face->is_cff2 && !blend->glyphoffsets ) 2462 if ( FT_SET_ERROR( ft_var_load_gvar( face ) ) ) 2463 goto Exit; 2464 2465 if ( !blend->coords ) 2466 { 2467 if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) ) 2468 goto Exit; 2469 2470 /* the first time we have to compute all design coordinates */ 2471 all_design_coords = TRUE; 2472 } 2473 2474 if ( !blend->normalizedcoords ) 2475 { 2476 if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) ) 2477 goto Exit; 2478 2479 manageCvt = mcvt_modify; 2480 2481 /* If we have not set the blend coordinates before this, then the */ 2482 /* cvt table will still be what we read from the `cvt ' table and */ 2483 /* we don't need to reload it. We may need to change it though... */ 2484 } 2485 else 2486 { 2487 FT_Bool have_diff = 0; 2488 FT_UInt j; 2489 FT_Fixed* c; 2490 FT_Fixed* n; 2491 2492 2493 manageCvt = mcvt_retain; 2494 2495 for ( i = 0; i < num_coords; i++ ) 2496 { 2497 if ( blend->normalizedcoords[i] != coords[i] ) 2498 { 2499 manageCvt = mcvt_load; 2500 have_diff = 1; 2501 break; 2502 } 2503 } 2504 2505 if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ) 2506 { 2507 FT_UInt idx = (FT_UInt)face->root.face_index >> 16; 2508 2509 2510 c = blend->normalizedcoords + i; 2511 n = blend->normalized_stylecoords + idx * mmvar->num_axis + i; 2512 for ( j = i; j < mmvar->num_axis; j++, n++, c++ ) 2513 if ( *c != *n ) 2514 have_diff = 1; 2515 } 2516 else 2517 { 2518 c = blend->normalizedcoords + i; 2519 for ( j = i; j < mmvar->num_axis; j++, c++ ) 2520 if ( *c != 0 ) 2521 have_diff = 1; 2522 } 2523 2524 /* return value -1 indicates `no change' */ 2525 if ( !have_diff ) 2526 return -1; 2527 2528 for ( ; i < mmvar->num_axis; i++ ) 2529 { 2530 if ( blend->normalizedcoords[i] != 0 ) 2531 { 2532 manageCvt = mcvt_load; 2533 break; 2534 } 2535 } 2536 2537 /* If we don't change the blend coords then we don't need to do */ 2538 /* anything to the cvt table. It will be correct. Otherwise we */ 2539 /* no longer have the original cvt (it was modified when we set */ 2540 /* the blend last time), so we must reload and then modify it. */ 2541 } 2542 2543 blend->num_axis = mmvar->num_axis; 2544 FT_MEM_COPY( blend->normalizedcoords, 2545 coords, 2546 num_coords * sizeof ( FT_Fixed ) ); 2547 2548 if ( set_design_coords ) 2549 ft_var_to_design( face, 2550 all_design_coords ? blend->num_axis : num_coords, 2551 blend->normalizedcoords, 2552 blend->coords ); 2553 2554 face->doblend = TRUE; 2555 2556 if ( face->cvt ) 2557 { 2558 switch ( manageCvt ) 2559 { 2560 case mcvt_load: 2561 /* The cvt table has been loaded already; every time we change the */ 2562 /* blend we may need to reload and remodify the cvt table. */ 2563 FT_FREE( face->cvt ); 2564 face->cvt = NULL; 2565 2566 error = tt_face_load_cvt( face, face->root.stream ); 2567 break; 2568 2569 case mcvt_modify: 2570 /* The original cvt table is in memory. All we need to do is */ 2571 /* apply the `cvar' table (if any). */ 2572 error = tt_face_vary_cvt( face, face->root.stream ); 2573 break; 2574 2575 case mcvt_retain: 2576 /* The cvt table is correct for this set of coordinates. */ 2577 break; 2578 } 2579 } 2580 2581 /* enforce recomputation of the PostScript name; */ 2582 FT_FREE( face->postscript_name ); 2583 face->postscript_name = NULL; 2584 2585 Exit: 2586 return error; 2587 } 2588 2589 2590 /*************************************************************************/ 2591 /* */ 2592 /* <Function> */ 2593 /* TT_Set_MM_Blend */ 2594 /* */ 2595 /* <Description> */ 2596 /* Set the blend (normalized) coordinates for this instance of the */ 2597 /* font. Check that the `gvar' table is reasonable and does some */ 2598 /* initial preparation. */ 2599 /* */ 2600 /* <InOut> */ 2601 /* face :: The font. */ 2602 /* Initialize the blend structure with `gvar' data. */ 2603 /* */ 2604 /* <Input> */ 2605 /* num_coords :: The number of available coordinates. If it is */ 2606 /* larger than the number of axes, ignore the excess */ 2607 /* values. If it is smaller than the number of axes, */ 2608 /* use the default value (0) for the remaining axes. */ 2609 /* */ 2610 /* coords :: An array of `num_coords', each between [-1,1]. */ 2611 /* */ 2612 /* <Return> */ 2613 /* FreeType error code. 0 means success. */ 2614 /* */ 2615 FT_LOCAL_DEF( FT_Error ) 2616 TT_Set_MM_Blend( TT_Face face, 2617 FT_UInt num_coords, 2618 FT_Fixed* coords ) 2619 { 2620 FT_Error error; 2621 2622 2623 error = tt_set_mm_blend( face, num_coords, coords, 1 ); 2624 if ( error ) 2625 return error; 2626 2627 if ( num_coords ) 2628 face->root.face_flags |= FT_FACE_FLAG_VARIATION; 2629 else 2630 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION; 2631 2632 return FT_Err_Ok; 2633 } 2634 2635 2636 /*************************************************************************/ 2637 /* */ 2638 /* <Function> */ 2639 /* TT_Get_MM_Blend */ 2640 /* */ 2641 /* <Description> */ 2642 /* Get the blend (normalized) coordinates for this instance of the */ 2643 /* font. */ 2644 /* */ 2645 /* <InOut> */ 2646 /* face :: The font. */ 2647 /* Initialize the blend structure with `gvar' data. */ 2648 /* */ 2649 /* <Input> */ 2650 /* num_coords :: The number of available coordinates. If it is */ 2651 /* larger than the number of axes, set the excess */ 2652 /* values to 0. */ 2653 /* */ 2654 /* coords :: An array of `num_coords', each between [-1,1]. */ 2655 /* */ 2656 /* <Return> */ 2657 /* FreeType error code. 0 means success. */ 2658 /* */ 2659 FT_LOCAL_DEF( FT_Error ) 2660 TT_Get_MM_Blend( TT_Face face, 2661 FT_UInt num_coords, 2662 FT_Fixed* coords ) 2663 { 2664 FT_Error error = FT_Err_Ok; 2665 GX_Blend blend; 2666 FT_UInt i, nc; 2667 2668 2669 if ( !face->blend ) 2670 { 2671 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 2672 return error; 2673 } 2674 2675 blend = face->blend; 2676 2677 if ( !blend->coords ) 2678 { 2679 /* select default instance coordinates */ 2680 /* if no instance is selected yet */ 2681 if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) ) 2682 return error; 2683 } 2684 2685 nc = num_coords; 2686 if ( num_coords > blend->num_axis ) 2687 { 2688 FT_TRACE2(( "TT_Get_MM_Blend:" 2689 " only using first %d of %d coordinates\n", 2690 blend->num_axis, num_coords )); 2691 nc = blend->num_axis; 2692 } 2693 2694 if ( face->doblend ) 2695 { 2696 for ( i = 0; i < nc; i++ ) 2697 coords[i] = blend->normalizedcoords[i]; 2698 } 2699 else 2700 { 2701 for ( i = 0; i < nc; i++ ) 2702 coords[i] = 0; 2703 } 2704 2705 for ( ; i < num_coords; i++ ) 2706 coords[i] = 0; 2707 2708 return FT_Err_Ok; 2709 } 2710 2711 2712 /*************************************************************************/ 2713 /* */ 2714 /* <Function> */ 2715 /* TT_Set_Var_Design */ 2716 /* */ 2717 /* <Description> */ 2718 /* Set the coordinates for the instance, measured in the user */ 2719 /* coordinate system. Parse the `avar' table (if present) to convert */ 2720 /* from user to normalized coordinates. */ 2721 /* */ 2722 /* <InOut> */ 2723 /* face :: The font face. */ 2724 /* Initialize the blend struct with `gvar' data. */ 2725 /* */ 2726 /* <Input> */ 2727 /* num_coords :: The number of available coordinates. If it is */ 2728 /* larger than the number of axes, ignore the excess */ 2729 /* values. If it is smaller than the number of axes, */ 2730 /* use the default values for the remaining axes. */ 2731 /* */ 2732 /* coords :: A coordinate array with `num_coords' elements. */ 2733 /* */ 2734 /* <Return> */ 2735 /* FreeType error code. 0 means success. */ 2736 /* */ 2737 FT_LOCAL_DEF( FT_Error ) 2738 TT_Set_Var_Design( TT_Face face, 2739 FT_UInt num_coords, 2740 FT_Fixed* coords ) 2741 { 2742 FT_Error error = FT_Err_Ok; 2743 GX_Blend blend; 2744 FT_MM_Var* mmvar; 2745 FT_UInt i; 2746 FT_Memory memory = face->root.memory; 2747 2748 FT_Fixed* c; 2749 FT_Fixed* n; 2750 FT_Fixed* normalized = NULL; 2751 2752 FT_Bool have_diff = 0; 2753 2754 2755 if ( !face->blend ) 2756 { 2757 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 2758 goto Exit; 2759 } 2760 2761 blend = face->blend; 2762 mmvar = blend->mmvar; 2763 2764 if ( num_coords > mmvar->num_axis ) 2765 { 2766 FT_TRACE2(( "TT_Set_Var_Design:" 2767 " only using first %d of %d coordinates\n", 2768 mmvar->num_axis, num_coords )); 2769 num_coords = mmvar->num_axis; 2770 } 2771 2772 if ( !blend->coords ) 2773 { 2774 if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) ) 2775 goto Exit; 2776 } 2777 2778 c = blend->coords; 2779 n = coords; 2780 for ( i = 0; i < num_coords; i++, n++, c++ ) 2781 { 2782 if ( *c != *n ) 2783 { 2784 *c = *n; 2785 have_diff = 1; 2786 } 2787 } 2788 2789 if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ) 2790 { 2791 FT_UInt instance_index; 2792 FT_Var_Named_Style* named_style; 2793 2794 2795 instance_index = (FT_UInt)face->root.face_index >> 16; 2796 named_style = mmvar->namedstyle + instance_index - 1; 2797 2798 n = named_style->coords + num_coords; 2799 for ( ; i < mmvar->num_axis; i++, n++, c++ ) 2800 { 2801 if ( *c != *n ) 2802 { 2803 *c = *n; 2804 have_diff = 1; 2805 } 2806 } 2807 } 2808 else 2809 { 2810 FT_Var_Axis* a; 2811 2812 2813 a = mmvar->axis + num_coords; 2814 for ( ; i < mmvar->num_axis; i++, a++, c++ ) 2815 { 2816 if ( *c != a->def ) 2817 { 2818 *c = a->def; 2819 have_diff = 1; 2820 } 2821 } 2822 } 2823 2824 /* return value -1 indicates `no change'; */ 2825 /* we can exit early if `normalizedcoords' is already computed */ 2826 if ( blend->normalizedcoords && !have_diff ) 2827 return -1; 2828 2829 if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) ) 2830 goto Exit; 2831 2832 if ( !face->blend->avar_loaded ) 2833 ft_var_load_avar( face ); 2834 2835 FT_TRACE5(( "TT_Set_Var_Design:\n" 2836 " normalized design coordinates:\n" )); 2837 ft_var_to_normalized( face, num_coords, blend->coords, normalized ); 2838 2839 error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 ); 2840 if ( error ) 2841 goto Exit; 2842 2843 if ( num_coords ) 2844 face->root.face_flags |= FT_FACE_FLAG_VARIATION; 2845 else 2846 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION; 2847 2848 Exit: 2849 FT_FREE( normalized ); 2850 return error; 2851 } 2852 2853 2854 /*************************************************************************/ 2855 /* */ 2856 /* <Function> */ 2857 /* TT_Get_Var_Design */ 2858 /* */ 2859 /* <Description> */ 2860 /* Get the design coordinates of the currently selected interpolated */ 2861 /* font. */ 2862 /* */ 2863 /* <Input> */ 2864 /* face :: A handle to the source face. */ 2865 /* */ 2866 /* num_coords :: The number of design coordinates to retrieve. If it */ 2867 /* is larger than the number of axes, set the excess */ 2868 /* values to~0. */ 2869 /* */ 2870 /* <Output> */ 2871 /* coords :: The design coordinates array. */ 2872 /* */ 2873 /* <Return> */ 2874 /* FreeType error code. 0~means success. */ 2875 /* */ 2876 FT_LOCAL_DEF( FT_Error ) 2877 TT_Get_Var_Design( TT_Face face, 2878 FT_UInt num_coords, 2879 FT_Fixed* coords ) 2880 { 2881 FT_Error error = FT_Err_Ok; 2882 GX_Blend blend; 2883 FT_UInt i, nc; 2884 2885 2886 if ( !face->blend ) 2887 { 2888 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 2889 return error; 2890 } 2891 2892 blend = face->blend; 2893 2894 if ( !blend->coords ) 2895 { 2896 /* select default instance coordinates */ 2897 /* if no instance is selected yet */ 2898 if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) ) 2899 return error; 2900 } 2901 2902 nc = num_coords; 2903 if ( num_coords > blend->num_axis ) 2904 { 2905 FT_TRACE2(( "TT_Get_Var_Design:" 2906 " only using first %d of %d coordinates\n", 2907 blend->num_axis, num_coords )); 2908 nc = blend->num_axis; 2909 } 2910 2911 if ( face->doblend ) 2912 { 2913 for ( i = 0; i < nc; i++ ) 2914 coords[i] = blend->coords[i]; 2915 } 2916 else 2917 { 2918 for ( i = 0; i < nc; i++ ) 2919 coords[i] = 0; 2920 } 2921 2922 for ( ; i < num_coords; i++ ) 2923 coords[i] = 0; 2924 2925 return FT_Err_Ok; 2926 } 2927 2928 2929 /*************************************************************************/ 2930 /* */ 2931 /* <Function> */ 2932 /* TT_Set_Named_Instance */ 2933 /* */ 2934 /* <Description> */ 2935 /* Set the given named instance, also resetting any further */ 2936 /* variation. */ 2937 /* */ 2938 /* <Input> */ 2939 /* face :: A handle to the source face. */ 2940 /* */ 2941 /* instance_index :: The instance index, starting with value 1. */ 2942 /* Value 0 indicates to not use an instance. */ 2943 /* */ 2944 /* <Return> */ 2945 /* FreeType error code. 0~means success. */ 2946 /* */ 2947 FT_LOCAL_DEF( FT_Error ) 2948 TT_Set_Named_Instance( TT_Face face, 2949 FT_UInt instance_index ) 2950 { 2951 FT_Error error = FT_ERR( Invalid_Argument ); 2952 GX_Blend blend; 2953 FT_MM_Var* mmvar; 2954 2955 FT_UInt num_instances; 2956 2957 2958 if ( !face->blend ) 2959 { 2960 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 2961 goto Exit; 2962 } 2963 2964 blend = face->blend; 2965 mmvar = blend->mmvar; 2966 2967 num_instances = (FT_UInt)face->root.style_flags >> 16; 2968 2969 /* `instance_index' starts with value 1, thus `>' */ 2970 if ( instance_index > num_instances ) 2971 goto Exit; 2972 2973 if ( instance_index > 0 && mmvar->namedstyle ) 2974 { 2975 FT_Memory memory = face->root.memory; 2976 SFNT_Service sfnt = (SFNT_Service)face->sfnt; 2977 2978 FT_Var_Named_Style* named_style; 2979 FT_String* style_name; 2980 2981 2982 named_style = mmvar->namedstyle + instance_index - 1; 2983 2984 error = sfnt->get_name( face, 2985 (FT_UShort)named_style->strid, 2986 &style_name ); 2987 if ( error ) 2988 goto Exit; 2989 2990 /* set (or replace) style name */ 2991 FT_FREE( face->root.style_name ); 2992 face->root.style_name = style_name; 2993 2994 /* finally, select the named instance */ 2995 error = TT_Set_Var_Design( face, 2996 mmvar->num_axis, 2997 named_style->coords ); 2998 if ( error ) 2999 goto Exit; 3000 } 3001 else 3002 error = TT_Set_Var_Design( face, 0, NULL ); 3003 3004 face->root.face_index = ( instance_index << 16 ) | 3005 ( face->root.face_index & 0xFFFFL ); 3006 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION; 3007 3008 Exit: 3009 return error; 3010 } 3011 3012 3013 /*************************************************************************/ 3014 /*************************************************************************/ 3015 /***** *****/ 3016 /***** GX VAR PARSING ROUTINES *****/ 3017 /***** *****/ 3018 /*************************************************************************/ 3019 /*************************************************************************/ 3020 3021 3022 /*************************************************************************/ 3023 /* */ 3024 /* <Function> */ 3025 /* tt_face_vary_cvt */ 3026 /* */ 3027 /* <Description> */ 3028 /* Modify the loaded cvt table according to the `cvar' table and the */ 3029 /* font's blend. */ 3030 /* */ 3031 /* <InOut> */ 3032 /* face :: A handle to the target face object. */ 3033 /* */ 3034 /* <Input> */ 3035 /* stream :: A handle to the input stream. */ 3036 /* */ 3037 /* <Return> */ 3038 /* FreeType error code. 0 means success. */ 3039 /* */ 3040 /* Most errors are ignored. It is perfectly valid not to have a */ 3041 /* `cvar' table even if there is a `gvar' and `fvar' table. */ 3042 /* */ 3043 FT_LOCAL_DEF( FT_Error ) 3044 tt_face_vary_cvt( TT_Face face, 3045 FT_Stream stream ) 3046 { 3047 FT_Error error; 3048 FT_Memory memory = stream->memory; 3049 FT_ULong table_start; 3050 FT_ULong table_len; 3051 FT_UInt tupleCount; 3052 FT_ULong offsetToData; 3053 FT_ULong here; 3054 FT_UInt i, j; 3055 FT_Fixed* tuple_coords = NULL; 3056 FT_Fixed* im_start_coords = NULL; 3057 FT_Fixed* im_end_coords = NULL; 3058 GX_Blend blend = face->blend; 3059 FT_UInt point_count, spoint_count = 0; 3060 FT_UShort* sharedpoints = NULL; 3061 FT_UShort* localpoints = NULL; 3062 FT_UShort* points; 3063 FT_Short* deltas; 3064 3065 3066 FT_TRACE2(( "CVAR " )); 3067 3068 if ( !blend ) 3069 { 3070 FT_TRACE2(( "\n" 3071 "tt_face_vary_cvt: no blend specified\n" )); 3072 error = FT_Err_Ok; 3073 goto Exit; 3074 } 3075 3076 if ( !face->cvt ) 3077 { 3078 FT_TRACE2(( "\n" 3079 "tt_face_vary_cvt: no `cvt ' table\n" )); 3080 error = FT_Err_Ok; 3081 goto Exit; 3082 } 3083 3084 error = face->goto_table( face, TTAG_cvar, stream, &table_len ); 3085 if ( error ) 3086 { 3087 FT_TRACE2(( "is missing\n" )); 3088 3089 error = FT_Err_Ok; 3090 goto Exit; 3091 } 3092 3093 if ( FT_FRAME_ENTER( table_len ) ) 3094 { 3095 error = FT_Err_Ok; 3096 goto Exit; 3097 } 3098 3099 table_start = FT_Stream_FTell( stream ); 3100 if ( FT_GET_LONG() != 0x00010000L ) 3101 { 3102 FT_TRACE2(( "bad table version\n" )); 3103 3104 error = FT_Err_Ok; 3105 goto FExit; 3106 } 3107 3108 FT_TRACE2(( "loaded\n" )); 3109 3110 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || 3111 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || 3112 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) 3113 goto FExit; 3114 3115 tupleCount = FT_GET_USHORT(); 3116 offsetToData = FT_GET_USHORT(); 3117 3118 /* rough sanity test */ 3119 if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 > 3120 table_len ) 3121 { 3122 FT_TRACE2(( "tt_face_vary_cvt:" 3123 " invalid CVT variation array header\n" )); 3124 3125 error = FT_THROW( Invalid_Table ); 3126 goto FExit; 3127 } 3128 3129 offsetToData += table_start; 3130 3131 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS ) 3132 { 3133 here = FT_Stream_FTell( stream ); 3134 3135 FT_Stream_SeekSet( stream, offsetToData ); 3136 3137 sharedpoints = ft_var_readpackedpoints( stream, 3138 table_len, 3139 &spoint_count ); 3140 offsetToData = FT_Stream_FTell( stream ); 3141 3142 FT_Stream_SeekSet( stream, here ); 3143 } 3144 3145 FT_TRACE5(( "cvar: there %s %d tuple%s:\n", 3146 ( tupleCount & 0xFFF ) == 1 ? "is" : "are", 3147 tupleCount & 0xFFF, 3148 ( tupleCount & 0xFFF ) == 1 ? "" : "s" )); 3149 3150 for ( i = 0; i < ( tupleCount & 0xFFF ); i++ ) 3151 { 3152 FT_UInt tupleDataSize; 3153 FT_UInt tupleIndex; 3154 FT_Fixed apply; 3155 3156 3157 FT_TRACE6(( " tuple %d:\n", i )); 3158 3159 tupleDataSize = FT_GET_USHORT(); 3160 tupleIndex = FT_GET_USHORT(); 3161 3162 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 3163 { 3164 for ( j = 0; j < blend->num_axis; j++ ) 3165 tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */ 3166 /* short frac to fixed */ 3167 } 3168 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount ) 3169 { 3170 FT_TRACE2(( "tt_face_vary_cvt:" 3171 " invalid tuple index\n" )); 3172 3173 error = FT_THROW( Invalid_Table ); 3174 goto Exit; 3175 } 3176 else 3177 FT_MEM_COPY( 3178 tuple_coords, 3179 &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis], 3180 blend->num_axis * sizeof ( FT_Fixed ) ); 3181 3182 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 3183 { 3184 for ( j = 0; j < blend->num_axis; j++ ) 3185 im_start_coords[j] = FT_GET_SHORT() * 4; 3186 for ( j = 0; j < blend->num_axis; j++ ) 3187 im_end_coords[j] = FT_GET_SHORT() * 4; 3188 } 3189 3190 apply = ft_var_apply_tuple( blend, 3191 (FT_UShort)tupleIndex, 3192 tuple_coords, 3193 im_start_coords, 3194 im_end_coords ); 3195 3196 if ( apply == 0 ) /* tuple isn't active for our blend */ 3197 { 3198 offsetToData += tupleDataSize; 3199 continue; 3200 } 3201 3202 here = FT_Stream_FTell( stream ); 3203 3204 FT_Stream_SeekSet( stream, offsetToData ); 3205 3206 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) 3207 { 3208 localpoints = ft_var_readpackedpoints( stream, 3209 table_len, 3210 &point_count ); 3211 points = localpoints; 3212 } 3213 else 3214 { 3215 points = sharedpoints; 3216 point_count = spoint_count; 3217 } 3218 3219 deltas = ft_var_readpackeddeltas( stream, 3220 table_len, 3221 point_count == 0 ? face->cvt_size 3222 : point_count ); 3223 3224 if ( !points || 3225 !deltas || 3226 ( localpoints == ALL_POINTS && point_count != face->cvt_size ) ) 3227 ; /* failure, ignore it */ 3228 3229 else if ( localpoints == ALL_POINTS ) 3230 { 3231 #ifdef FT_DEBUG_LEVEL_TRACE 3232 int count = 0; 3233 #endif 3234 3235 3236 FT_TRACE7(( " CVT deltas:\n" )); 3237 3238 /* this means that there are deltas for every entry in cvt */ 3239 for ( j = 0; j < face->cvt_size; j++ ) 3240 { 3241 FT_Long orig_cvt = face->cvt[j]; 3242 3243 3244 face->cvt[j] = (FT_Short)( orig_cvt + 3245 FT_MulFix( deltas[j], apply ) ); 3246 3247 #ifdef FT_DEBUG_LEVEL_TRACE 3248 if ( orig_cvt != face->cvt[j] ) 3249 { 3250 FT_TRACE7(( " %d: %d -> %d\n", 3251 j, orig_cvt, face->cvt[j] )); 3252 count++; 3253 } 3254 #endif 3255 } 3256 3257 #ifdef FT_DEBUG_LEVEL_TRACE 3258 if ( !count ) 3259 FT_TRACE7(( " none\n" )); 3260 #endif 3261 } 3262 3263 else 3264 { 3265 #ifdef FT_DEBUG_LEVEL_TRACE 3266 int count = 0; 3267 #endif 3268 3269 3270 FT_TRACE7(( " CVT deltas:\n" )); 3271 3272 for ( j = 0; j < point_count; j++ ) 3273 { 3274 int pindex; 3275 FT_Long orig_cvt; 3276 3277 3278 pindex = points[j]; 3279 if ( (FT_ULong)pindex >= face->cvt_size ) 3280 continue; 3281 3282 orig_cvt = face->cvt[pindex]; 3283 face->cvt[pindex] = (FT_Short)( orig_cvt + 3284 FT_MulFix( deltas[j], apply ) ); 3285 3286 #ifdef FT_DEBUG_LEVEL_TRACE 3287 if ( orig_cvt != face->cvt[pindex] ) 3288 { 3289 FT_TRACE7(( " %d: %d -> %d\n", 3290 pindex, orig_cvt, face->cvt[pindex] )); 3291 count++; 3292 } 3293 #endif 3294 } 3295 3296 #ifdef FT_DEBUG_LEVEL_TRACE 3297 if ( !count ) 3298 FT_TRACE7(( " none\n" )); 3299 #endif 3300 } 3301 3302 if ( localpoints != ALL_POINTS ) 3303 FT_FREE( localpoints ); 3304 FT_FREE( deltas ); 3305 3306 offsetToData += tupleDataSize; 3307 3308 FT_Stream_SeekSet( stream, here ); 3309 } 3310 3311 FT_TRACE5(( "\n" )); 3312 3313 FExit: 3314 FT_FRAME_EXIT(); 3315 3316 Exit: 3317 if ( sharedpoints != ALL_POINTS ) 3318 FT_FREE( sharedpoints ); 3319 FT_FREE( tuple_coords ); 3320 FT_FREE( im_start_coords ); 3321 FT_FREE( im_end_coords ); 3322 3323 return error; 3324 } 3325 3326 3327 /* Shift the original coordinates of all points between indices `p1' */ 3328 /* and `p2', using the same difference as given by index `ref'. */ 3329 3330 /* modeled after `af_iup_shift' */ 3331 3332 static void 3333 tt_delta_shift( int p1, 3334 int p2, 3335 int ref, 3336 FT_Vector* in_points, 3337 FT_Vector* out_points ) 3338 { 3339 int p; 3340 FT_Vector delta; 3341 3342 3343 delta.x = out_points[ref].x - in_points[ref].x; 3344 delta.y = out_points[ref].y - in_points[ref].y; 3345 3346 if ( delta.x == 0 && delta.y == 0 ) 3347 return; 3348 3349 for ( p = p1; p < ref; p++ ) 3350 { 3351 out_points[p].x += delta.x; 3352 out_points[p].y += delta.y; 3353 } 3354 3355 for ( p = ref + 1; p <= p2; p++ ) 3356 { 3357 out_points[p].x += delta.x; 3358 out_points[p].y += delta.y; 3359 } 3360 } 3361 3362 3363 /* Interpolate the original coordinates of all points with indices */ 3364 /* between `p1' and `p2', using `ref1' and `ref2' as the reference */ 3365 /* point indices. */ 3366 3367 /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */ 3368 /* `Ins_IUP' */ 3369 3370 static void 3371 tt_delta_interpolate( int p1, 3372 int p2, 3373 int ref1, 3374 int ref2, 3375 FT_Vector* in_points, 3376 FT_Vector* out_points ) 3377 { 3378 int p, i; 3379 3380 FT_Pos out, in1, in2, out1, out2, d1, d2; 3381 3382 3383 if ( p1 > p2 ) 3384 return; 3385 3386 /* handle both horizontal and vertical coordinates */ 3387 for ( i = 0; i <= 1; i++ ) 3388 { 3389 /* shift array pointers so that we can access `foo.y' as `foo.x' */ 3390 in_points = (FT_Vector*)( (FT_Pos*)in_points + i ); 3391 out_points = (FT_Vector*)( (FT_Pos*)out_points + i ); 3392 3393 if ( in_points[ref1].x > in_points[ref2].x ) 3394 { 3395 p = ref1; 3396 ref1 = ref2; 3397 ref2 = p; 3398 } 3399 3400 in1 = in_points[ref1].x; 3401 in2 = in_points[ref2].x; 3402 out1 = out_points[ref1].x; 3403 out2 = out_points[ref2].x; 3404 d1 = out1 - in1; 3405 d2 = out2 - in2; 3406 3407 /* If the reference points have the same coordinate but different */ 3408 /* delta, inferred delta is zero. Otherwise interpolate. */ 3409 if ( in1 != in2 || out1 == out2 ) 3410 { 3411 FT_Fixed scale = in1 != in2 ? FT_DivFix( out2 - out1, in2 - in1 ) 3412 : 0; 3413 3414 3415 for ( p = p1; p <= p2; p++ ) 3416 { 3417 out = in_points[p].x; 3418 3419 if ( out <= in1 ) 3420 out += d1; 3421 else if ( out >= in2 ) 3422 out += d2; 3423 else 3424 out = out1 + FT_MulFix( out - in1, scale ); 3425 3426 out_points[p].x = out; 3427 } 3428 } 3429 } 3430 } 3431 3432 3433 /* Interpolate points without delta values, similar to */ 3434 /* the `IUP' hinting instruction. */ 3435 3436 /* modeled after `Ins_IUP */ 3437 3438 static void 3439 tt_interpolate_deltas( FT_Outline* outline, 3440 FT_Vector* out_points, 3441 FT_Vector* in_points, 3442 FT_Bool* has_delta ) 3443 { 3444 FT_Int first_point; 3445 FT_Int end_point; 3446 3447 FT_Int first_delta; 3448 FT_Int cur_delta; 3449 3450 FT_Int point; 3451 FT_Short contour; 3452 3453 3454 /* ignore empty outlines */ 3455 if ( !outline->n_contours ) 3456 return; 3457 3458 contour = 0; 3459 point = 0; 3460 3461 do 3462 { 3463 end_point = outline->contours[contour]; 3464 first_point = point; 3465 3466 /* search first point that has a delta */ 3467 while ( point <= end_point && !has_delta[point] ) 3468 point++; 3469 3470 if ( point <= end_point ) 3471 { 3472 first_delta = point; 3473 cur_delta = point; 3474 3475 point++; 3476 3477 while ( point <= end_point ) 3478 { 3479 /* search next point that has a delta */ 3480 /* and interpolate intermediate points */ 3481 if ( has_delta[point] ) 3482 { 3483 tt_delta_interpolate( cur_delta + 1, 3484 point - 1, 3485 cur_delta, 3486 point, 3487 in_points, 3488 out_points ); 3489 cur_delta = point; 3490 } 3491 3492 point++; 3493 } 3494 3495 /* shift contour if we only have a single delta */ 3496 if ( cur_delta == first_delta ) 3497 tt_delta_shift( first_point, 3498 end_point, 3499 cur_delta, 3500 in_points, 3501 out_points ); 3502 else 3503 { 3504 /* otherwise handle remaining points */ 3505 /* at the end and beginning of the contour */ 3506 tt_delta_interpolate( cur_delta + 1, 3507 end_point, 3508 cur_delta, 3509 first_delta, 3510 in_points, 3511 out_points ); 3512 3513 if ( first_delta > 0 ) 3514 tt_delta_interpolate( first_point, 3515 first_delta - 1, 3516 cur_delta, 3517 first_delta, 3518 in_points, 3519 out_points ); 3520 } 3521 } 3522 contour++; 3523 3524 } while ( contour < outline->n_contours ); 3525 } 3526 3527 3528 /*************************************************************************/ 3529 /* */ 3530 /* <Function> */ 3531 /* TT_Vary_Apply_Glyph_Deltas */ 3532 /* */ 3533 /* <Description> */ 3534 /* Apply the appropriate deltas to the current glyph. */ 3535 /* */ 3536 /* <Input> */ 3537 /* face :: A handle to the target face object. */ 3538 /* */ 3539 /* glyph_index :: The index of the glyph being modified. */ 3540 /* */ 3541 /* n_points :: The number of the points in the glyph, including */ 3542 /* phantom points. */ 3543 /* */ 3544 /* <InOut> */ 3545 /* outline :: The outline to change. */ 3546 /* */ 3547 /* <Return> */ 3548 /* FreeType error code. 0 means success. */ 3549 /* */ 3550 FT_LOCAL_DEF( FT_Error ) 3551 TT_Vary_Apply_Glyph_Deltas( TT_Face face, 3552 FT_UInt glyph_index, 3553 FT_Outline* outline, 3554 FT_UInt n_points ) 3555 { 3556 FT_Stream stream = face->root.stream; 3557 FT_Memory memory = stream->memory; 3558 GX_Blend blend = face->blend; 3559 3560 FT_Vector* points_org = NULL; 3561 FT_Vector* points_out = NULL; 3562 FT_Bool* has_delta = NULL; 3563 3564 FT_Error error; 3565 FT_ULong glyph_start; 3566 FT_UInt tupleCount; 3567 FT_ULong offsetToData; 3568 FT_ULong here; 3569 FT_UInt i, j; 3570 FT_Fixed* tuple_coords = NULL; 3571 FT_Fixed* im_start_coords = NULL; 3572 FT_Fixed* im_end_coords = NULL; 3573 FT_UInt point_count, spoint_count = 0; 3574 FT_UShort* sharedpoints = NULL; 3575 FT_UShort* localpoints = NULL; 3576 FT_UShort* points; 3577 FT_Short *deltas_x, *deltas_y; 3578 3579 3580 if ( !face->doblend || !blend ) 3581 return FT_THROW( Invalid_Argument ); 3582 3583 if ( glyph_index >= blend->gv_glyphcnt || 3584 blend->glyphoffsets[glyph_index] == 3585 blend->glyphoffsets[glyph_index + 1] ) 3586 { 3587 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 3588 " no variation data for this glyph\n" )); 3589 return FT_Err_Ok; 3590 } 3591 3592 if ( FT_NEW_ARRAY( points_org, n_points ) || 3593 FT_NEW_ARRAY( points_out, n_points ) || 3594 FT_NEW_ARRAY( has_delta, n_points ) ) 3595 goto Fail1; 3596 3597 if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) || 3598 FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] - 3599 blend->glyphoffsets[glyph_index] ) ) 3600 goto Fail1; 3601 3602 glyph_start = FT_Stream_FTell( stream ); 3603 3604 /* each set of glyph variation data is formatted similarly to `cvar' */ 3605 3606 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || 3607 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || 3608 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) 3609 goto Fail2; 3610 3611 tupleCount = FT_GET_USHORT(); 3612 offsetToData = FT_GET_USHORT(); 3613 3614 /* rough sanity test */ 3615 if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 > 3616 blend->gvar_size ) 3617 { 3618 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 3619 " invalid glyph variation array header\n" )); 3620 3621 error = FT_THROW( Invalid_Table ); 3622 goto Fail2; 3623 } 3624 3625 offsetToData += glyph_start; 3626 3627 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS ) 3628 { 3629 here = FT_Stream_FTell( stream ); 3630 3631 FT_Stream_SeekSet( stream, offsetToData ); 3632 3633 sharedpoints = ft_var_readpackedpoints( stream, 3634 blend->gvar_size, 3635 &spoint_count ); 3636 offsetToData = FT_Stream_FTell( stream ); 3637 3638 FT_Stream_SeekSet( stream, here ); 3639 } 3640 3641 FT_TRACE5(( "gvar: there %s %d tuple%s:\n", 3642 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are", 3643 tupleCount & GX_TC_TUPLE_COUNT_MASK, 3644 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" )); 3645 3646 for ( j = 0; j < n_points; j++ ) 3647 points_org[j] = outline->points[j]; 3648 3649 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ ) 3650 { 3651 FT_UInt tupleDataSize; 3652 FT_UInt tupleIndex; 3653 FT_Fixed apply; 3654 3655 3656 FT_TRACE6(( " tuple %d:\n", i )); 3657 3658 tupleDataSize = FT_GET_USHORT(); 3659 tupleIndex = FT_GET_USHORT(); 3660 3661 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 3662 { 3663 for ( j = 0; j < blend->num_axis; j++ ) 3664 tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */ 3665 /* short frac to fixed */ 3666 } 3667 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount ) 3668 { 3669 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 3670 " invalid tuple index\n" )); 3671 3672 error = FT_THROW( Invalid_Table ); 3673 goto Fail2; 3674 } 3675 else 3676 FT_MEM_COPY( 3677 tuple_coords, 3678 &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis], 3679 blend->num_axis * sizeof ( FT_Fixed ) ); 3680 3681 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 3682 { 3683 for ( j = 0; j < blend->num_axis; j++ ) 3684 im_start_coords[j] = FT_GET_SHORT() * 4; 3685 for ( j = 0; j < blend->num_axis; j++ ) 3686 im_end_coords[j] = FT_GET_SHORT() * 4; 3687 } 3688 3689 apply = ft_var_apply_tuple( blend, 3690 (FT_UShort)tupleIndex, 3691 tuple_coords, 3692 im_start_coords, 3693 im_end_coords ); 3694 3695 if ( apply == 0 ) /* tuple isn't active for our blend */ 3696 { 3697 offsetToData += tupleDataSize; 3698 continue; 3699 } 3700 3701 here = FT_Stream_FTell( stream ); 3702 3703 FT_Stream_SeekSet( stream, offsetToData ); 3704 3705 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) 3706 { 3707 localpoints = ft_var_readpackedpoints( stream, 3708 blend->gvar_size, 3709 &point_count ); 3710 points = localpoints; 3711 } 3712 else 3713 { 3714 points = sharedpoints; 3715 point_count = spoint_count; 3716 } 3717 3718 deltas_x = ft_var_readpackeddeltas( stream, 3719 blend->gvar_size, 3720 point_count == 0 ? n_points 3721 : point_count ); 3722 deltas_y = ft_var_readpackeddeltas( stream, 3723 blend->gvar_size, 3724 point_count == 0 ? n_points 3725 : point_count ); 3726 3727 if ( !points || !deltas_y || !deltas_x ) 3728 ; /* failure, ignore it */ 3729 3730 else if ( points == ALL_POINTS ) 3731 { 3732 #ifdef FT_DEBUG_LEVEL_TRACE 3733 int count = 0; 3734 #endif 3735 3736 3737 FT_TRACE7(( " point deltas:\n" )); 3738 3739 /* this means that there are deltas for every point in the glyph */ 3740 for ( j = 0; j < n_points; j++ ) 3741 { 3742 FT_Pos delta_x = FT_MulFix( deltas_x[j], apply ); 3743 FT_Pos delta_y = FT_MulFix( deltas_y[j], apply ); 3744 3745 3746 if ( j < n_points - 4 ) 3747 { 3748 outline->points[j].x += delta_x; 3749 outline->points[j].y += delta_y; 3750 } 3751 else 3752 { 3753 /* To avoid double adjustment of advance width or height, */ 3754 /* adjust phantom points only if there is no HVAR or VVAR */ 3755 /* support, respectively. */ 3756 if ( j == ( n_points - 4 ) && 3757 !( face->variation_support & 3758 TT_FACE_FLAG_VAR_LSB ) ) 3759 outline->points[j].x += delta_x; 3760 3761 else if ( j == ( n_points - 3 ) && 3762 !( face->variation_support & 3763 TT_FACE_FLAG_VAR_HADVANCE ) ) 3764 outline->points[j].x += delta_x; 3765 3766 else if ( j == ( n_points - 2 ) && 3767 !( face->variation_support & 3768 TT_FACE_FLAG_VAR_TSB ) ) 3769 outline->points[j].y += delta_y; 3770 3771 else if ( j == ( n_points - 1 ) && 3772 !( face->variation_support & 3773 TT_FACE_FLAG_VAR_VADVANCE ) ) 3774 outline->points[j].y += delta_y; 3775 } 3776 3777 #ifdef FT_DEBUG_LEVEL_TRACE 3778 if ( delta_x || delta_y ) 3779 { 3780 FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n", 3781 j, 3782 outline->points[j].x - delta_x, 3783 outline->points[j].y - delta_y, 3784 outline->points[j].x, 3785 outline->points[j].y )); 3786 count++; 3787 } 3788 #endif 3789 } 3790 3791 #ifdef FT_DEBUG_LEVEL_TRACE 3792 if ( !count ) 3793 FT_TRACE7(( " none\n" )); 3794 #endif 3795 } 3796 3797 else 3798 { 3799 #ifdef FT_DEBUG_LEVEL_TRACE 3800 int count = 0; 3801 #endif 3802 3803 3804 /* we have to interpolate the missing deltas similar to the */ 3805 /* IUP bytecode instruction */ 3806 for ( j = 0; j < n_points; j++ ) 3807 { 3808 has_delta[j] = FALSE; 3809 points_out[j] = points_org[j]; 3810 } 3811 3812 for ( j = 0; j < point_count; j++ ) 3813 { 3814 FT_UShort idx = points[j]; 3815 3816 3817 if ( idx >= n_points ) 3818 continue; 3819 3820 has_delta[idx] = TRUE; 3821 3822 points_out[idx].x += FT_MulFix( deltas_x[j], apply ); 3823 points_out[idx].y += FT_MulFix( deltas_y[j], apply ); 3824 } 3825 3826 /* no need to handle phantom points here, */ 3827 /* since solitary points can't be interpolated */ 3828 tt_interpolate_deltas( outline, 3829 points_out, 3830 points_org, 3831 has_delta ); 3832 3833 FT_TRACE7(( " point deltas:\n" )); 3834 3835 for ( j = 0; j < n_points; j++ ) 3836 { 3837 FT_Pos delta_x = points_out[j].x - points_org[j].x; 3838 FT_Pos delta_y = points_out[j].y - points_org[j].y; 3839 3840 3841 if ( j < n_points - 4 ) 3842 { 3843 outline->points[j].x += delta_x; 3844 outline->points[j].y += delta_y; 3845 } 3846 else 3847 { 3848 /* To avoid double adjustment of advance width or height, */ 3849 /* adjust phantom points only if there is no HVAR or VVAR */ 3850 /* support, respectively. */ 3851 if ( j == ( n_points - 4 ) && 3852 !( face->variation_support & 3853 TT_FACE_FLAG_VAR_LSB ) ) 3854 outline->points[j].x += delta_x; 3855 3856 else if ( j == ( n_points - 3 ) && 3857 !( face->variation_support & 3858 TT_FACE_FLAG_VAR_HADVANCE ) ) 3859 outline->points[j].x += delta_x; 3860 3861 else if ( j == ( n_points - 2 ) && 3862 !( face->variation_support & 3863 TT_FACE_FLAG_VAR_TSB ) ) 3864 outline->points[j].y += delta_y; 3865 3866 else if ( j == ( n_points - 1 ) && 3867 !( face->variation_support & 3868 TT_FACE_FLAG_VAR_VADVANCE ) ) 3869 outline->points[j].y += delta_y; 3870 } 3871 3872 #ifdef FT_DEBUG_LEVEL_TRACE 3873 if ( delta_x || delta_y ) 3874 { 3875 FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n", 3876 j, 3877 outline->points[j].x - delta_x, 3878 outline->points[j].y - delta_y, 3879 outline->points[j].x, 3880 outline->points[j].y )); 3881 count++; 3882 } 3883 #endif 3884 } 3885 3886 #ifdef FT_DEBUG_LEVEL_TRACE 3887 if ( !count ) 3888 FT_TRACE7(( " none\n" )); 3889 #endif 3890 } 3891 3892 if ( localpoints != ALL_POINTS ) 3893 FT_FREE( localpoints ); 3894 FT_FREE( deltas_x ); 3895 FT_FREE( deltas_y ); 3896 3897 offsetToData += tupleDataSize; 3898 3899 FT_Stream_SeekSet( stream, here ); 3900 } 3901 3902 FT_TRACE5(( "\n" )); 3903 3904 Fail2: 3905 if ( sharedpoints != ALL_POINTS ) 3906 FT_FREE( sharedpoints ); 3907 FT_FREE( tuple_coords ); 3908 FT_FREE( im_start_coords ); 3909 FT_FREE( im_end_coords ); 3910 3911 FT_FRAME_EXIT(); 3912 3913 Fail1: 3914 FT_FREE( points_org ); 3915 FT_FREE( points_out ); 3916 FT_FREE( has_delta ); 3917 3918 return error; 3919 } 3920 3921 3922 /*************************************************************************/ 3923 /* */ 3924 /* <Function> */ 3925 /* tt_get_var_blend */ 3926 /* */ 3927 /* <Description> */ 3928 /* An extended internal version of `TT_Get_MM_Blend' that returns */ 3929 /* pointers instead of copying data, without any initialization of */ 3930 /* the MM machinery in case it isn't loaded yet. */ 3931 /* */ 3932 FT_LOCAL_DEF( FT_Error ) 3933 tt_get_var_blend( TT_Face face, 3934 FT_UInt *num_coords, 3935 FT_Fixed* *coords, 3936 FT_Fixed* *normalizedcoords, 3937 FT_MM_Var* *mm_var ) 3938 { 3939 if ( face->blend ) 3940 { 3941 if ( num_coords ) 3942 *num_coords = face->blend->num_axis; 3943 if ( coords ) 3944 *coords = face->blend->coords; 3945 if ( normalizedcoords ) 3946 *normalizedcoords = face->blend->normalizedcoords; 3947 if ( mm_var ) 3948 *mm_var = face->blend->mmvar; 3949 } 3950 else 3951 { 3952 if ( num_coords ) 3953 *num_coords = 0; 3954 if ( coords ) 3955 *coords = NULL; 3956 if ( mm_var ) 3957 *mm_var = NULL; 3958 } 3959 3960 return FT_Err_Ok; 3961 } 3962 3963 3964 static void 3965 ft_var_done_item_variation_store( TT_Face face, 3966 GX_ItemVarStore itemStore ) 3967 { 3968 FT_Memory memory = FT_FACE_MEMORY( face ); 3969 FT_UInt i; 3970 3971 3972 if ( itemStore->varData ) 3973 { 3974 for ( i = 0; i < itemStore->dataCount; i++ ) 3975 { 3976 FT_FREE( itemStore->varData[i].regionIndices ); 3977 FT_FREE( itemStore->varData[i].deltaSet ); 3978 } 3979 3980 FT_FREE( itemStore->varData ); 3981 } 3982 3983 if ( itemStore->varRegionList ) 3984 { 3985 for ( i = 0; i < itemStore->regionCount; i++ ) 3986 FT_FREE( itemStore->varRegionList[i].axisList ); 3987 3988 FT_FREE( itemStore->varRegionList ); 3989 } 3990 } 3991 3992 3993 /*************************************************************************/ 3994 /* */ 3995 /* <Function> */ 3996 /* tt_done_blend */ 3997 /* */ 3998 /* <Description> */ 3999 /* Free the blend internal data structure. */ 4000 /* */ 4001 FT_LOCAL_DEF( void ) 4002 tt_done_blend( TT_Face face ) 4003 { 4004 FT_Memory memory = FT_FACE_MEMORY( face ); 4005 GX_Blend blend = face->blend; 4006 4007 4008 if ( blend ) 4009 { 4010 FT_UInt i, num_axes; 4011 4012 4013 /* blend->num_axis might not be set up yet */ 4014 num_axes = blend->mmvar->num_axis; 4015 4016 FT_FREE( blend->coords ); 4017 FT_FREE( blend->normalizedcoords ); 4018 FT_FREE( blend->normalized_stylecoords ); 4019 FT_FREE( blend->mmvar ); 4020 4021 if ( blend->avar_segment ) 4022 { 4023 for ( i = 0; i < num_axes; i++ ) 4024 FT_FREE( blend->avar_segment[i].correspondence ); 4025 FT_FREE( blend->avar_segment ); 4026 } 4027 4028 if ( blend->hvar_table ) 4029 { 4030 ft_var_done_item_variation_store( face, 4031 &blend->hvar_table->itemStore ); 4032 4033 FT_FREE( blend->hvar_table->widthMap.innerIndex ); 4034 FT_FREE( blend->hvar_table->widthMap.outerIndex ); 4035 FT_FREE( blend->hvar_table ); 4036 } 4037 4038 if ( blend->vvar_table ) 4039 { 4040 ft_var_done_item_variation_store( face, 4041 &blend->vvar_table->itemStore ); 4042 4043 FT_FREE( blend->vvar_table->widthMap.innerIndex ); 4044 FT_FREE( blend->vvar_table->widthMap.outerIndex ); 4045 FT_FREE( blend->vvar_table ); 4046 } 4047 4048 if ( blend->mvar_table ) 4049 { 4050 ft_var_done_item_variation_store( face, 4051 &blend->mvar_table->itemStore ); 4052 4053 FT_FREE( blend->mvar_table->values ); 4054 FT_FREE( blend->mvar_table ); 4055 } 4056 4057 FT_FREE( blend->tuplecoords ); 4058 FT_FREE( blend->glyphoffsets ); 4059 FT_FREE( blend ); 4060 } 4061 } 4062 4063 #else /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */ 4064 4065 /* ANSI C doesn't like empty source files */ 4066 typedef int _tt_gxvar_dummy; 4067 4068 #endif /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */ 4069 4070 4071 /* END */ 4072