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 FT_FREE( strname ); 2285 FT_FREE( psname ); 2286 } 2287 #endif /* FT_DEBUG_LEVEL_TRACE */ 2288 2289 ft_var_to_normalized( face, num_axes, ns->coords, nsc ); 2290 nsc += num_axes; 2291 2292 FT_FRAME_EXIT(); 2293 } 2294 2295 if ( num_instances != fvar_head.instanceCount ) 2296 { 2297 SFNT_Service sfnt = (SFNT_Service)face->sfnt; 2298 2299 FT_Int found, dummy1, dummy2; 2300 FT_UInt strid = ~0U; 2301 2302 2303 /* the default instance is missing in array the */ 2304 /* of named instances; try to synthesize an entry */ 2305 found = sfnt->get_name_id( face, 2306 TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY, 2307 &dummy1, 2308 &dummy2 ); 2309 if ( found ) 2310 strid = TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY; 2311 else 2312 { 2313 found = sfnt->get_name_id( face, 2314 TT_NAME_ID_FONT_SUBFAMILY, 2315 &dummy1, 2316 &dummy2 ); 2317 if ( found ) 2318 strid = TT_NAME_ID_FONT_SUBFAMILY; 2319 } 2320 2321 if ( found ) 2322 { 2323 found = sfnt->get_name_id( face, 2324 TT_NAME_ID_PS_NAME, 2325 &dummy1, 2326 &dummy2 ); 2327 if ( found ) 2328 { 2329 FT_TRACE5(( "TT_Get_MM_Var:" 2330 " Adding default instance to named instances\n" )); 2331 2332 ns = &mmvar->namedstyle[fvar_head.instanceCount]; 2333 2334 ns->strid = strid; 2335 ns->psid = TT_NAME_ID_PS_NAME; 2336 2337 a = mmvar->axis; 2338 c = ns->coords; 2339 for ( j = 0; j < num_axes; j++, a++, c++ ) 2340 *c = a->def; 2341 } 2342 } 2343 } 2344 2345 ft_var_load_mvar( face ); 2346 } 2347 2348 /* fill the output array if requested */ 2349 2350 if ( master ) 2351 { 2352 FT_UInt n; 2353 2354 2355 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) ) 2356 goto Exit; 2357 FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len ); 2358 2359 axis_flags = 2360 (FT_UShort*)( (char*)mmvar + mmvar_size ); 2361 mmvar->axis = 2362 (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size ); 2363 mmvar->namedstyle = 2364 (FT_Var_Named_Style*)( (char*)mmvar->axis+ axis_size ); 2365 2366 next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle + 2367 namedstyle_size ); 2368 for ( n = 0; n < mmvar->num_namedstyles; n++ ) 2369 { 2370 mmvar->namedstyle[n].coords = next_coords; 2371 next_coords += num_axes; 2372 } 2373 2374 a = mmvar->axis; 2375 next_name = (FT_String*)( (char*)mmvar->namedstyle + 2376 namedstyle_size + next_coords_size ); 2377 for ( n = 0; n < num_axes; n++ ) 2378 { 2379 a->name = next_name; 2380 2381 /* standard PostScript names for some standard apple tags */ 2382 if ( a->tag == TTAG_wght ) 2383 a->name = (char*)"Weight"; 2384 else if ( a->tag == TTAG_wdth ) 2385 a->name = (char*)"Width"; 2386 else if ( a->tag == TTAG_opsz ) 2387 a->name = (char*)"OpticalSize"; 2388 else if ( a->tag == TTAG_slnt ) 2389 a->name = (char*)"Slant"; 2390 2391 next_name += 5; 2392 a++; 2393 } 2394 2395 *master = mmvar; 2396 } 2397 2398 Exit: 2399 return error; 2400 } 2401 2402 2403 static FT_Error 2404 tt_set_mm_blend( TT_Face face, 2405 FT_UInt num_coords, 2406 FT_Fixed* coords, 2407 FT_Bool set_design_coords ) 2408 { 2409 FT_Error error = FT_Err_Ok; 2410 GX_Blend blend; 2411 FT_MM_Var* mmvar; 2412 FT_UInt i; 2413 2414 FT_Bool all_design_coords = FALSE; 2415 2416 FT_Memory memory = face->root.memory; 2417 2418 enum 2419 { 2420 mcvt_retain, 2421 mcvt_modify, 2422 mcvt_load 2423 2424 } manageCvt; 2425 2426 2427 face->doblend = FALSE; 2428 2429 if ( !face->blend ) 2430 { 2431 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 2432 goto Exit; 2433 } 2434 2435 blend = face->blend; 2436 mmvar = blend->mmvar; 2437 2438 if ( num_coords > mmvar->num_axis ) 2439 { 2440 FT_TRACE2(( "TT_Set_MM_Blend:" 2441 " only using first %d of %d coordinates\n", 2442 mmvar->num_axis, num_coords )); 2443 num_coords = mmvar->num_axis; 2444 } 2445 2446 FT_TRACE5(( "TT_Set_MM_Blend:\n" 2447 " normalized design coordinates:\n" )); 2448 2449 for ( i = 0; i < num_coords; i++ ) 2450 { 2451 FT_TRACE5(( " %.5f\n", coords[i] / 65536.0 )); 2452 if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L ) 2453 { 2454 FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n" 2455 " is out of range [-1;1]\n", 2456 coords[i] / 65536.0 )); 2457 error = FT_THROW( Invalid_Argument ); 2458 goto Exit; 2459 } 2460 } 2461 2462 FT_TRACE5(( "\n" )); 2463 2464 if ( !face->is_cff2 && !blend->glyphoffsets ) 2465 if ( FT_SET_ERROR( ft_var_load_gvar( face ) ) ) 2466 goto Exit; 2467 2468 if ( !blend->coords ) 2469 { 2470 if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) ) 2471 goto Exit; 2472 2473 /* the first time we have to compute all design coordinates */ 2474 all_design_coords = TRUE; 2475 } 2476 2477 if ( !blend->normalizedcoords ) 2478 { 2479 if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) ) 2480 goto Exit; 2481 2482 manageCvt = mcvt_modify; 2483 2484 /* If we have not set the blend coordinates before this, then the */ 2485 /* cvt table will still be what we read from the `cvt ' table and */ 2486 /* we don't need to reload it. We may need to change it though... */ 2487 } 2488 else 2489 { 2490 FT_Bool have_diff = 0; 2491 FT_UInt j; 2492 FT_Fixed* c; 2493 FT_Fixed* n; 2494 2495 2496 manageCvt = mcvt_retain; 2497 2498 for ( i = 0; i < num_coords; i++ ) 2499 { 2500 if ( blend->normalizedcoords[i] != coords[i] ) 2501 { 2502 manageCvt = mcvt_load; 2503 have_diff = 1; 2504 break; 2505 } 2506 } 2507 2508 if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ) 2509 { 2510 FT_UInt idx = (FT_UInt)face->root.face_index >> 16; 2511 2512 2513 c = blend->normalizedcoords + i; 2514 n = blend->normalized_stylecoords + idx * mmvar->num_axis + i; 2515 for ( j = i; j < mmvar->num_axis; j++, n++, c++ ) 2516 if ( *c != *n ) 2517 have_diff = 1; 2518 } 2519 else 2520 { 2521 c = blend->normalizedcoords + i; 2522 for ( j = i; j < mmvar->num_axis; j++, c++ ) 2523 if ( *c != 0 ) 2524 have_diff = 1; 2525 } 2526 2527 /* return value -1 indicates `no change' */ 2528 if ( !have_diff ) 2529 return -1; 2530 2531 for ( ; i < mmvar->num_axis; i++ ) 2532 { 2533 if ( blend->normalizedcoords[i] != 0 ) 2534 { 2535 manageCvt = mcvt_load; 2536 break; 2537 } 2538 } 2539 2540 /* If we don't change the blend coords then we don't need to do */ 2541 /* anything to the cvt table. It will be correct. Otherwise we */ 2542 /* no longer have the original cvt (it was modified when we set */ 2543 /* the blend last time), so we must reload and then modify it. */ 2544 } 2545 2546 blend->num_axis = mmvar->num_axis; 2547 FT_MEM_COPY( blend->normalizedcoords, 2548 coords, 2549 num_coords * sizeof ( FT_Fixed ) ); 2550 2551 if ( set_design_coords ) 2552 ft_var_to_design( face, 2553 all_design_coords ? blend->num_axis : num_coords, 2554 blend->normalizedcoords, 2555 blend->coords ); 2556 2557 face->doblend = TRUE; 2558 2559 if ( face->cvt ) 2560 { 2561 switch ( manageCvt ) 2562 { 2563 case mcvt_load: 2564 /* The cvt table has been loaded already; every time we change the */ 2565 /* blend we may need to reload and remodify the cvt table. */ 2566 FT_FREE( face->cvt ); 2567 face->cvt = NULL; 2568 2569 error = tt_face_load_cvt( face, face->root.stream ); 2570 break; 2571 2572 case mcvt_modify: 2573 /* The original cvt table is in memory. All we need to do is */ 2574 /* apply the `cvar' table (if any). */ 2575 error = tt_face_vary_cvt( face, face->root.stream ); 2576 break; 2577 2578 case mcvt_retain: 2579 /* The cvt table is correct for this set of coordinates. */ 2580 break; 2581 } 2582 } 2583 2584 /* enforce recomputation of the PostScript name; */ 2585 FT_FREE( face->postscript_name ); 2586 face->postscript_name = NULL; 2587 2588 Exit: 2589 return error; 2590 } 2591 2592 2593 /*************************************************************************/ 2594 /* */ 2595 /* <Function> */ 2596 /* TT_Set_MM_Blend */ 2597 /* */ 2598 /* <Description> */ 2599 /* Set the blend (normalized) coordinates for this instance of the */ 2600 /* font. Check that the `gvar' table is reasonable and does some */ 2601 /* initial preparation. */ 2602 /* */ 2603 /* <InOut> */ 2604 /* face :: The font. */ 2605 /* Initialize the blend structure with `gvar' data. */ 2606 /* */ 2607 /* <Input> */ 2608 /* num_coords :: The number of available coordinates. If it is */ 2609 /* larger than the number of axes, ignore the excess */ 2610 /* values. If it is smaller than the number of axes, */ 2611 /* use the default value (0) for the remaining axes. */ 2612 /* */ 2613 /* coords :: An array of `num_coords', each between [-1,1]. */ 2614 /* */ 2615 /* <Return> */ 2616 /* FreeType error code. 0 means success. */ 2617 /* */ 2618 FT_LOCAL_DEF( FT_Error ) 2619 TT_Set_MM_Blend( TT_Face face, 2620 FT_UInt num_coords, 2621 FT_Fixed* coords ) 2622 { 2623 FT_Error error; 2624 2625 2626 error = tt_set_mm_blend( face, num_coords, coords, 1 ); 2627 if ( error ) 2628 return error; 2629 2630 if ( num_coords ) 2631 face->root.face_flags |= FT_FACE_FLAG_VARIATION; 2632 else 2633 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION; 2634 2635 return FT_Err_Ok; 2636 } 2637 2638 2639 /*************************************************************************/ 2640 /* */ 2641 /* <Function> */ 2642 /* TT_Get_MM_Blend */ 2643 /* */ 2644 /* <Description> */ 2645 /* Get the blend (normalized) coordinates for this instance of the */ 2646 /* font. */ 2647 /* */ 2648 /* <InOut> */ 2649 /* face :: The font. */ 2650 /* Initialize the blend structure with `gvar' data. */ 2651 /* */ 2652 /* <Input> */ 2653 /* num_coords :: The number of available coordinates. If it is */ 2654 /* larger than the number of axes, set the excess */ 2655 /* values to 0. */ 2656 /* */ 2657 /* coords :: An array of `num_coords', each between [-1,1]. */ 2658 /* */ 2659 /* <Return> */ 2660 /* FreeType error code. 0 means success. */ 2661 /* */ 2662 FT_LOCAL_DEF( FT_Error ) 2663 TT_Get_MM_Blend( TT_Face face, 2664 FT_UInt num_coords, 2665 FT_Fixed* coords ) 2666 { 2667 FT_Error error = FT_Err_Ok; 2668 GX_Blend blend; 2669 FT_UInt i, nc; 2670 2671 2672 if ( !face->blend ) 2673 { 2674 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 2675 return error; 2676 } 2677 2678 blend = face->blend; 2679 2680 if ( !blend->coords ) 2681 { 2682 /* select default instance coordinates */ 2683 /* if no instance is selected yet */ 2684 if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) ) 2685 return error; 2686 } 2687 2688 nc = num_coords; 2689 if ( num_coords > blend->num_axis ) 2690 { 2691 FT_TRACE2(( "TT_Get_MM_Blend:" 2692 " only using first %d of %d coordinates\n", 2693 blend->num_axis, num_coords )); 2694 nc = blend->num_axis; 2695 } 2696 2697 if ( face->doblend ) 2698 { 2699 for ( i = 0; i < nc; i++ ) 2700 coords[i] = blend->normalizedcoords[i]; 2701 } 2702 else 2703 { 2704 for ( i = 0; i < nc; i++ ) 2705 coords[i] = 0; 2706 } 2707 2708 for ( ; i < num_coords; i++ ) 2709 coords[i] = 0; 2710 2711 return FT_Err_Ok; 2712 } 2713 2714 2715 /*************************************************************************/ 2716 /* */ 2717 /* <Function> */ 2718 /* TT_Set_Var_Design */ 2719 /* */ 2720 /* <Description> */ 2721 /* Set the coordinates for the instance, measured in the user */ 2722 /* coordinate system. Parse the `avar' table (if present) to convert */ 2723 /* from user to normalized coordinates. */ 2724 /* */ 2725 /* <InOut> */ 2726 /* face :: The font face. */ 2727 /* Initialize the blend struct with `gvar' data. */ 2728 /* */ 2729 /* <Input> */ 2730 /* num_coords :: The number of available coordinates. If it is */ 2731 /* larger than the number of axes, ignore the excess */ 2732 /* values. If it is smaller than the number of axes, */ 2733 /* use the default values for the remaining axes. */ 2734 /* */ 2735 /* coords :: A coordinate array with `num_coords' elements. */ 2736 /* */ 2737 /* <Return> */ 2738 /* FreeType error code. 0 means success. */ 2739 /* */ 2740 FT_LOCAL_DEF( FT_Error ) 2741 TT_Set_Var_Design( TT_Face face, 2742 FT_UInt num_coords, 2743 FT_Fixed* coords ) 2744 { 2745 FT_Error error = FT_Err_Ok; 2746 GX_Blend blend; 2747 FT_MM_Var* mmvar; 2748 FT_UInt i; 2749 FT_Memory memory = face->root.memory; 2750 2751 FT_Fixed* c; 2752 FT_Fixed* n; 2753 FT_Fixed* normalized = NULL; 2754 2755 FT_Bool have_diff = 0; 2756 2757 2758 if ( !face->blend ) 2759 { 2760 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 2761 goto Exit; 2762 } 2763 2764 blend = face->blend; 2765 mmvar = blend->mmvar; 2766 2767 if ( num_coords > mmvar->num_axis ) 2768 { 2769 FT_TRACE2(( "TT_Set_Var_Design:" 2770 " only using first %d of %d coordinates\n", 2771 mmvar->num_axis, num_coords )); 2772 num_coords = mmvar->num_axis; 2773 } 2774 2775 if ( !blend->coords ) 2776 { 2777 if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) ) 2778 goto Exit; 2779 } 2780 2781 c = blend->coords; 2782 n = coords; 2783 for ( i = 0; i < num_coords; i++, n++, c++ ) 2784 { 2785 if ( *c != *n ) 2786 { 2787 *c = *n; 2788 have_diff = 1; 2789 } 2790 } 2791 2792 if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ) 2793 { 2794 FT_UInt instance_index; 2795 FT_Var_Named_Style* named_style; 2796 2797 2798 instance_index = (FT_UInt)face->root.face_index >> 16; 2799 named_style = mmvar->namedstyle + instance_index - 1; 2800 2801 n = named_style->coords + num_coords; 2802 for ( ; i < mmvar->num_axis; i++, n++, c++ ) 2803 { 2804 if ( *c != *n ) 2805 { 2806 *c = *n; 2807 have_diff = 1; 2808 } 2809 } 2810 } 2811 else 2812 { 2813 FT_Var_Axis* a; 2814 2815 2816 a = mmvar->axis + num_coords; 2817 for ( ; i < mmvar->num_axis; i++, a++, c++ ) 2818 { 2819 if ( *c != a->def ) 2820 { 2821 *c = a->def; 2822 have_diff = 1; 2823 } 2824 } 2825 } 2826 2827 /* return value -1 indicates `no change'; */ 2828 /* we can exit early if `normalizedcoords' is already computed */ 2829 if ( blend->normalizedcoords && !have_diff ) 2830 return -1; 2831 2832 if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) ) 2833 goto Exit; 2834 2835 if ( !face->blend->avar_loaded ) 2836 ft_var_load_avar( face ); 2837 2838 FT_TRACE5(( "TT_Set_Var_Design:\n" 2839 " normalized design coordinates:\n" )); 2840 ft_var_to_normalized( face, num_coords, blend->coords, normalized ); 2841 2842 error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 ); 2843 if ( error ) 2844 goto Exit; 2845 2846 if ( num_coords ) 2847 face->root.face_flags |= FT_FACE_FLAG_VARIATION; 2848 else 2849 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION; 2850 2851 Exit: 2852 FT_FREE( normalized ); 2853 return error; 2854 } 2855 2856 2857 /*************************************************************************/ 2858 /* */ 2859 /* <Function> */ 2860 /* TT_Get_Var_Design */ 2861 /* */ 2862 /* <Description> */ 2863 /* Get the design coordinates of the currently selected interpolated */ 2864 /* font. */ 2865 /* */ 2866 /* <Input> */ 2867 /* face :: A handle to the source face. */ 2868 /* */ 2869 /* num_coords :: The number of design coordinates to retrieve. If it */ 2870 /* is larger than the number of axes, set the excess */ 2871 /* values to~0. */ 2872 /* */ 2873 /* <Output> */ 2874 /* coords :: The design coordinates array. */ 2875 /* */ 2876 /* <Return> */ 2877 /* FreeType error code. 0~means success. */ 2878 /* */ 2879 FT_LOCAL_DEF( FT_Error ) 2880 TT_Get_Var_Design( TT_Face face, 2881 FT_UInt num_coords, 2882 FT_Fixed* coords ) 2883 { 2884 FT_Error error = FT_Err_Ok; 2885 GX_Blend blend; 2886 FT_UInt i, nc; 2887 2888 2889 if ( !face->blend ) 2890 { 2891 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 2892 return error; 2893 } 2894 2895 blend = face->blend; 2896 2897 if ( !blend->coords ) 2898 { 2899 /* select default instance coordinates */ 2900 /* if no instance is selected yet */ 2901 if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) ) 2902 return error; 2903 } 2904 2905 nc = num_coords; 2906 if ( num_coords > blend->num_axis ) 2907 { 2908 FT_TRACE2(( "TT_Get_Var_Design:" 2909 " only using first %d of %d coordinates\n", 2910 blend->num_axis, num_coords )); 2911 nc = blend->num_axis; 2912 } 2913 2914 if ( face->doblend ) 2915 { 2916 for ( i = 0; i < nc; i++ ) 2917 coords[i] = blend->coords[i]; 2918 } 2919 else 2920 { 2921 for ( i = 0; i < nc; i++ ) 2922 coords[i] = 0; 2923 } 2924 2925 for ( ; i < num_coords; i++ ) 2926 coords[i] = 0; 2927 2928 return FT_Err_Ok; 2929 } 2930 2931 2932 /*************************************************************************/ 2933 /* */ 2934 /* <Function> */ 2935 /* TT_Set_Named_Instance */ 2936 /* */ 2937 /* <Description> */ 2938 /* Set the given named instance, also resetting any further */ 2939 /* variation. */ 2940 /* */ 2941 /* <Input> */ 2942 /* face :: A handle to the source face. */ 2943 /* */ 2944 /* instance_index :: The instance index, starting with value 1. */ 2945 /* Value 0 indicates to not use an instance. */ 2946 /* */ 2947 /* <Return> */ 2948 /* FreeType error code. 0~means success. */ 2949 /* */ 2950 FT_LOCAL_DEF( FT_Error ) 2951 TT_Set_Named_Instance( TT_Face face, 2952 FT_UInt instance_index ) 2953 { 2954 FT_Error error = FT_ERR( Invalid_Argument ); 2955 GX_Blend blend; 2956 FT_MM_Var* mmvar; 2957 2958 FT_UInt num_instances; 2959 2960 2961 if ( !face->blend ) 2962 { 2963 if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) ) 2964 goto Exit; 2965 } 2966 2967 blend = face->blend; 2968 mmvar = blend->mmvar; 2969 2970 num_instances = (FT_UInt)face->root.style_flags >> 16; 2971 2972 /* `instance_index' starts with value 1, thus `>' */ 2973 if ( instance_index > num_instances ) 2974 goto Exit; 2975 2976 if ( instance_index > 0 && mmvar->namedstyle ) 2977 { 2978 FT_Memory memory = face->root.memory; 2979 SFNT_Service sfnt = (SFNT_Service)face->sfnt; 2980 2981 FT_Var_Named_Style* named_style; 2982 FT_String* style_name; 2983 2984 2985 named_style = mmvar->namedstyle + instance_index - 1; 2986 2987 error = sfnt->get_name( face, 2988 (FT_UShort)named_style->strid, 2989 &style_name ); 2990 if ( error ) 2991 goto Exit; 2992 2993 /* set (or replace) style name */ 2994 FT_FREE( face->root.style_name ); 2995 face->root.style_name = style_name; 2996 2997 /* finally, select the named instance */ 2998 error = TT_Set_Var_Design( face, 2999 mmvar->num_axis, 3000 named_style->coords ); 3001 if ( error ) 3002 goto Exit; 3003 } 3004 else 3005 error = TT_Set_Var_Design( face, 0, NULL ); 3006 3007 face->root.face_index = ( instance_index << 16 ) | 3008 ( face->root.face_index & 0xFFFFL ); 3009 face->root.face_flags &= ~FT_FACE_FLAG_VARIATION; 3010 3011 Exit: 3012 return error; 3013 } 3014 3015 3016 /*************************************************************************/ 3017 /*************************************************************************/ 3018 /***** *****/ 3019 /***** GX VAR PARSING ROUTINES *****/ 3020 /***** *****/ 3021 /*************************************************************************/ 3022 /*************************************************************************/ 3023 3024 3025 /*************************************************************************/ 3026 /* */ 3027 /* <Function> */ 3028 /* tt_face_vary_cvt */ 3029 /* */ 3030 /* <Description> */ 3031 /* Modify the loaded cvt table according to the `cvar' table and the */ 3032 /* font's blend. */ 3033 /* */ 3034 /* <InOut> */ 3035 /* face :: A handle to the target face object. */ 3036 /* */ 3037 /* <Input> */ 3038 /* stream :: A handle to the input stream. */ 3039 /* */ 3040 /* <Return> */ 3041 /* FreeType error code. 0 means success. */ 3042 /* */ 3043 /* Most errors are ignored. It is perfectly valid not to have a */ 3044 /* `cvar' table even if there is a `gvar' and `fvar' table. */ 3045 /* */ 3046 FT_LOCAL_DEF( FT_Error ) 3047 tt_face_vary_cvt( TT_Face face, 3048 FT_Stream stream ) 3049 { 3050 FT_Error error; 3051 FT_Memory memory = stream->memory; 3052 FT_ULong table_start; 3053 FT_ULong table_len; 3054 FT_UInt tupleCount; 3055 FT_ULong offsetToData; 3056 FT_ULong here; 3057 FT_UInt i, j; 3058 FT_Fixed* tuple_coords = NULL; 3059 FT_Fixed* im_start_coords = NULL; 3060 FT_Fixed* im_end_coords = NULL; 3061 GX_Blend blend = face->blend; 3062 FT_UInt point_count, spoint_count = 0; 3063 FT_UShort* sharedpoints = NULL; 3064 FT_UShort* localpoints = NULL; 3065 FT_UShort* points; 3066 FT_Short* deltas; 3067 3068 3069 FT_TRACE2(( "CVAR " )); 3070 3071 if ( !blend ) 3072 { 3073 FT_TRACE2(( "\n" 3074 "tt_face_vary_cvt: no blend specified\n" )); 3075 error = FT_Err_Ok; 3076 goto Exit; 3077 } 3078 3079 if ( !face->cvt ) 3080 { 3081 FT_TRACE2(( "\n" 3082 "tt_face_vary_cvt: no `cvt ' table\n" )); 3083 error = FT_Err_Ok; 3084 goto Exit; 3085 } 3086 3087 error = face->goto_table( face, TTAG_cvar, stream, &table_len ); 3088 if ( error ) 3089 { 3090 FT_TRACE2(( "is missing\n" )); 3091 3092 error = FT_Err_Ok; 3093 goto Exit; 3094 } 3095 3096 if ( FT_FRAME_ENTER( table_len ) ) 3097 { 3098 error = FT_Err_Ok; 3099 goto Exit; 3100 } 3101 3102 table_start = FT_Stream_FTell( stream ); 3103 if ( FT_GET_LONG() != 0x00010000L ) 3104 { 3105 FT_TRACE2(( "bad table version\n" )); 3106 3107 error = FT_Err_Ok; 3108 goto FExit; 3109 } 3110 3111 FT_TRACE2(( "loaded\n" )); 3112 3113 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || 3114 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || 3115 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) 3116 goto FExit; 3117 3118 tupleCount = FT_GET_USHORT(); 3119 offsetToData = FT_GET_USHORT(); 3120 3121 /* rough sanity test */ 3122 if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 > 3123 table_len ) 3124 { 3125 FT_TRACE2(( "tt_face_vary_cvt:" 3126 " invalid CVT variation array header\n" )); 3127 3128 error = FT_THROW( Invalid_Table ); 3129 goto FExit; 3130 } 3131 3132 offsetToData += table_start; 3133 3134 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS ) 3135 { 3136 here = FT_Stream_FTell( stream ); 3137 3138 FT_Stream_SeekSet( stream, offsetToData ); 3139 3140 sharedpoints = ft_var_readpackedpoints( stream, 3141 table_len, 3142 &spoint_count ); 3143 offsetToData = FT_Stream_FTell( stream ); 3144 3145 FT_Stream_SeekSet( stream, here ); 3146 } 3147 3148 FT_TRACE5(( "cvar: there %s %d tuple%s:\n", 3149 ( tupleCount & 0xFFF ) == 1 ? "is" : "are", 3150 tupleCount & 0xFFF, 3151 ( tupleCount & 0xFFF ) == 1 ? "" : "s" )); 3152 3153 for ( i = 0; i < ( tupleCount & 0xFFF ); i++ ) 3154 { 3155 FT_UInt tupleDataSize; 3156 FT_UInt tupleIndex; 3157 FT_Fixed apply; 3158 3159 3160 FT_TRACE6(( " tuple %d:\n", i )); 3161 3162 tupleDataSize = FT_GET_USHORT(); 3163 tupleIndex = FT_GET_USHORT(); 3164 3165 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 3166 { 3167 for ( j = 0; j < blend->num_axis; j++ ) 3168 tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */ 3169 /* short frac to fixed */ 3170 } 3171 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount ) 3172 { 3173 FT_TRACE2(( "tt_face_vary_cvt:" 3174 " invalid tuple index\n" )); 3175 3176 error = FT_THROW( Invalid_Table ); 3177 goto Exit; 3178 } 3179 else 3180 FT_MEM_COPY( 3181 tuple_coords, 3182 &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis], 3183 blend->num_axis * sizeof ( FT_Fixed ) ); 3184 3185 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 3186 { 3187 for ( j = 0; j < blend->num_axis; j++ ) 3188 im_start_coords[j] = FT_GET_SHORT() * 4; 3189 for ( j = 0; j < blend->num_axis; j++ ) 3190 im_end_coords[j] = FT_GET_SHORT() * 4; 3191 } 3192 3193 apply = ft_var_apply_tuple( blend, 3194 (FT_UShort)tupleIndex, 3195 tuple_coords, 3196 im_start_coords, 3197 im_end_coords ); 3198 3199 if ( apply == 0 ) /* tuple isn't active for our blend */ 3200 { 3201 offsetToData += tupleDataSize; 3202 continue; 3203 } 3204 3205 here = FT_Stream_FTell( stream ); 3206 3207 FT_Stream_SeekSet( stream, offsetToData ); 3208 3209 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) 3210 { 3211 localpoints = ft_var_readpackedpoints( stream, 3212 table_len, 3213 &point_count ); 3214 points = localpoints; 3215 } 3216 else 3217 { 3218 points = sharedpoints; 3219 point_count = spoint_count; 3220 } 3221 3222 deltas = ft_var_readpackeddeltas( stream, 3223 table_len, 3224 point_count == 0 ? face->cvt_size 3225 : point_count ); 3226 3227 if ( !points || 3228 !deltas || 3229 ( localpoints == ALL_POINTS && point_count != face->cvt_size ) ) 3230 ; /* failure, ignore it */ 3231 3232 else if ( localpoints == ALL_POINTS ) 3233 { 3234 #ifdef FT_DEBUG_LEVEL_TRACE 3235 int count = 0; 3236 #endif 3237 3238 3239 FT_TRACE7(( " CVT deltas:\n" )); 3240 3241 /* this means that there are deltas for every entry in cvt */ 3242 for ( j = 0; j < face->cvt_size; j++ ) 3243 { 3244 FT_Long orig_cvt = face->cvt[j]; 3245 3246 3247 face->cvt[j] = (FT_Short)( orig_cvt + 3248 FT_MulFix( deltas[j], apply ) ); 3249 3250 #ifdef FT_DEBUG_LEVEL_TRACE 3251 if ( orig_cvt != face->cvt[j] ) 3252 { 3253 FT_TRACE7(( " %d: %d -> %d\n", 3254 j, orig_cvt, face->cvt[j] )); 3255 count++; 3256 } 3257 #endif 3258 } 3259 3260 #ifdef FT_DEBUG_LEVEL_TRACE 3261 if ( !count ) 3262 FT_TRACE7(( " none\n" )); 3263 #endif 3264 } 3265 3266 else 3267 { 3268 #ifdef FT_DEBUG_LEVEL_TRACE 3269 int count = 0; 3270 #endif 3271 3272 3273 FT_TRACE7(( " CVT deltas:\n" )); 3274 3275 for ( j = 0; j < point_count; j++ ) 3276 { 3277 int pindex; 3278 FT_Long orig_cvt; 3279 3280 3281 pindex = points[j]; 3282 if ( (FT_ULong)pindex >= face->cvt_size ) 3283 continue; 3284 3285 orig_cvt = face->cvt[pindex]; 3286 face->cvt[pindex] = (FT_Short)( orig_cvt + 3287 FT_MulFix( deltas[j], apply ) ); 3288 3289 #ifdef FT_DEBUG_LEVEL_TRACE 3290 if ( orig_cvt != face->cvt[pindex] ) 3291 { 3292 FT_TRACE7(( " %d: %d -> %d\n", 3293 pindex, orig_cvt, face->cvt[pindex] )); 3294 count++; 3295 } 3296 #endif 3297 } 3298 3299 #ifdef FT_DEBUG_LEVEL_TRACE 3300 if ( !count ) 3301 FT_TRACE7(( " none\n" )); 3302 #endif 3303 } 3304 3305 if ( localpoints != ALL_POINTS ) 3306 FT_FREE( localpoints ); 3307 FT_FREE( deltas ); 3308 3309 offsetToData += tupleDataSize; 3310 3311 FT_Stream_SeekSet( stream, here ); 3312 } 3313 3314 FT_TRACE5(( "\n" )); 3315 3316 FExit: 3317 FT_FRAME_EXIT(); 3318 3319 Exit: 3320 if ( sharedpoints != ALL_POINTS ) 3321 FT_FREE( sharedpoints ); 3322 FT_FREE( tuple_coords ); 3323 FT_FREE( im_start_coords ); 3324 FT_FREE( im_end_coords ); 3325 3326 return error; 3327 } 3328 3329 3330 /* Shift the original coordinates of all points between indices `p1' */ 3331 /* and `p2', using the same difference as given by index `ref'. */ 3332 3333 /* modeled after `af_iup_shift' */ 3334 3335 static void 3336 tt_delta_shift( int p1, 3337 int p2, 3338 int ref, 3339 FT_Vector* in_points, 3340 FT_Vector* out_points ) 3341 { 3342 int p; 3343 FT_Vector delta; 3344 3345 3346 delta.x = out_points[ref].x - in_points[ref].x; 3347 delta.y = out_points[ref].y - in_points[ref].y; 3348 3349 if ( delta.x == 0 && delta.y == 0 ) 3350 return; 3351 3352 for ( p = p1; p < ref; p++ ) 3353 { 3354 out_points[p].x += delta.x; 3355 out_points[p].y += delta.y; 3356 } 3357 3358 for ( p = ref + 1; p <= p2; p++ ) 3359 { 3360 out_points[p].x += delta.x; 3361 out_points[p].y += delta.y; 3362 } 3363 } 3364 3365 3366 /* Interpolate the original coordinates of all points with indices */ 3367 /* between `p1' and `p2', using `ref1' and `ref2' as the reference */ 3368 /* point indices. */ 3369 3370 /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */ 3371 /* `Ins_IUP' */ 3372 3373 static void 3374 tt_delta_interpolate( int p1, 3375 int p2, 3376 int ref1, 3377 int ref2, 3378 FT_Vector* in_points, 3379 FT_Vector* out_points ) 3380 { 3381 int p, i; 3382 3383 FT_Pos out, in1, in2, out1, out2, d1, d2; 3384 3385 3386 if ( p1 > p2 ) 3387 return; 3388 3389 /* handle both horizontal and vertical coordinates */ 3390 for ( i = 0; i <= 1; i++ ) 3391 { 3392 /* shift array pointers so that we can access `foo.y' as `foo.x' */ 3393 in_points = (FT_Vector*)( (FT_Pos*)in_points + i ); 3394 out_points = (FT_Vector*)( (FT_Pos*)out_points + i ); 3395 3396 if ( in_points[ref1].x > in_points[ref2].x ) 3397 { 3398 p = ref1; 3399 ref1 = ref2; 3400 ref2 = p; 3401 } 3402 3403 in1 = in_points[ref1].x; 3404 in2 = in_points[ref2].x; 3405 out1 = out_points[ref1].x; 3406 out2 = out_points[ref2].x; 3407 d1 = out1 - in1; 3408 d2 = out2 - in2; 3409 3410 /* If the reference points have the same coordinate but different */ 3411 /* delta, inferred delta is zero. Otherwise interpolate. */ 3412 if ( in1 != in2 || out1 == out2 ) 3413 { 3414 FT_Fixed scale = in1 != in2 ? FT_DivFix( out2 - out1, in2 - in1 ) 3415 : 0; 3416 3417 3418 for ( p = p1; p <= p2; p++ ) 3419 { 3420 out = in_points[p].x; 3421 3422 if ( out <= in1 ) 3423 out += d1; 3424 else if ( out >= in2 ) 3425 out += d2; 3426 else 3427 out = out1 + FT_MulFix( out - in1, scale ); 3428 3429 out_points[p].x = out; 3430 } 3431 } 3432 } 3433 } 3434 3435 3436 /* Interpolate points without delta values, similar to */ 3437 /* the `IUP' hinting instruction. */ 3438 3439 /* modeled after `Ins_IUP */ 3440 3441 static void 3442 tt_interpolate_deltas( FT_Outline* outline, 3443 FT_Vector* out_points, 3444 FT_Vector* in_points, 3445 FT_Bool* has_delta ) 3446 { 3447 FT_Int first_point; 3448 FT_Int end_point; 3449 3450 FT_Int first_delta; 3451 FT_Int cur_delta; 3452 3453 FT_Int point; 3454 FT_Short contour; 3455 3456 3457 /* ignore empty outlines */ 3458 if ( !outline->n_contours ) 3459 return; 3460 3461 contour = 0; 3462 point = 0; 3463 3464 do 3465 { 3466 end_point = outline->contours[contour]; 3467 first_point = point; 3468 3469 /* search first point that has a delta */ 3470 while ( point <= end_point && !has_delta[point] ) 3471 point++; 3472 3473 if ( point <= end_point ) 3474 { 3475 first_delta = point; 3476 cur_delta = point; 3477 3478 point++; 3479 3480 while ( point <= end_point ) 3481 { 3482 /* search next point that has a delta */ 3483 /* and interpolate intermediate points */ 3484 if ( has_delta[point] ) 3485 { 3486 tt_delta_interpolate( cur_delta + 1, 3487 point - 1, 3488 cur_delta, 3489 point, 3490 in_points, 3491 out_points ); 3492 cur_delta = point; 3493 } 3494 3495 point++; 3496 } 3497 3498 /* shift contour if we only have a single delta */ 3499 if ( cur_delta == first_delta ) 3500 tt_delta_shift( first_point, 3501 end_point, 3502 cur_delta, 3503 in_points, 3504 out_points ); 3505 else 3506 { 3507 /* otherwise handle remaining points */ 3508 /* at the end and beginning of the contour */ 3509 tt_delta_interpolate( cur_delta + 1, 3510 end_point, 3511 cur_delta, 3512 first_delta, 3513 in_points, 3514 out_points ); 3515 3516 if ( first_delta > 0 ) 3517 tt_delta_interpolate( first_point, 3518 first_delta - 1, 3519 cur_delta, 3520 first_delta, 3521 in_points, 3522 out_points ); 3523 } 3524 } 3525 contour++; 3526 3527 } while ( contour < outline->n_contours ); 3528 } 3529 3530 3531 /*************************************************************************/ 3532 /* */ 3533 /* <Function> */ 3534 /* TT_Vary_Apply_Glyph_Deltas */ 3535 /* */ 3536 /* <Description> */ 3537 /* Apply the appropriate deltas to the current glyph. */ 3538 /* */ 3539 /* <Input> */ 3540 /* face :: A handle to the target face object. */ 3541 /* */ 3542 /* glyph_index :: The index of the glyph being modified. */ 3543 /* */ 3544 /* n_points :: The number of the points in the glyph, including */ 3545 /* phantom points. */ 3546 /* */ 3547 /* <InOut> */ 3548 /* outline :: The outline to change. */ 3549 /* */ 3550 /* <Return> */ 3551 /* FreeType error code. 0 means success. */ 3552 /* */ 3553 FT_LOCAL_DEF( FT_Error ) 3554 TT_Vary_Apply_Glyph_Deltas( TT_Face face, 3555 FT_UInt glyph_index, 3556 FT_Outline* outline, 3557 FT_UInt n_points ) 3558 { 3559 FT_Stream stream = face->root.stream; 3560 FT_Memory memory = stream->memory; 3561 GX_Blend blend = face->blend; 3562 3563 FT_Vector* points_org = NULL; 3564 FT_Vector* points_out = NULL; 3565 FT_Bool* has_delta = NULL; 3566 3567 FT_Error error; 3568 FT_ULong glyph_start; 3569 FT_UInt tupleCount; 3570 FT_ULong offsetToData; 3571 FT_ULong here; 3572 FT_UInt i, j; 3573 FT_Fixed* tuple_coords = NULL; 3574 FT_Fixed* im_start_coords = NULL; 3575 FT_Fixed* im_end_coords = NULL; 3576 FT_UInt point_count, spoint_count = 0; 3577 FT_UShort* sharedpoints = NULL; 3578 FT_UShort* localpoints = NULL; 3579 FT_UShort* points; 3580 FT_Short *deltas_x, *deltas_y; 3581 3582 3583 if ( !face->doblend || !blend ) 3584 return FT_THROW( Invalid_Argument ); 3585 3586 if ( glyph_index >= blend->gv_glyphcnt || 3587 blend->glyphoffsets[glyph_index] == 3588 blend->glyphoffsets[glyph_index + 1] ) 3589 { 3590 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 3591 " no variation data for this glyph\n" )); 3592 return FT_Err_Ok; 3593 } 3594 3595 if ( FT_NEW_ARRAY( points_org, n_points ) || 3596 FT_NEW_ARRAY( points_out, n_points ) || 3597 FT_NEW_ARRAY( has_delta, n_points ) ) 3598 goto Fail1; 3599 3600 if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) || 3601 FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] - 3602 blend->glyphoffsets[glyph_index] ) ) 3603 goto Fail1; 3604 3605 glyph_start = FT_Stream_FTell( stream ); 3606 3607 /* each set of glyph variation data is formatted similarly to `cvar' */ 3608 3609 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || 3610 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || 3611 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) 3612 goto Fail2; 3613 3614 tupleCount = FT_GET_USHORT(); 3615 offsetToData = FT_GET_USHORT(); 3616 3617 /* rough sanity test */ 3618 if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 > 3619 blend->gvar_size ) 3620 { 3621 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 3622 " invalid glyph variation array header\n" )); 3623 3624 error = FT_THROW( Invalid_Table ); 3625 goto Fail2; 3626 } 3627 3628 offsetToData += glyph_start; 3629 3630 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS ) 3631 { 3632 here = FT_Stream_FTell( stream ); 3633 3634 FT_Stream_SeekSet( stream, offsetToData ); 3635 3636 sharedpoints = ft_var_readpackedpoints( stream, 3637 blend->gvar_size, 3638 &spoint_count ); 3639 offsetToData = FT_Stream_FTell( stream ); 3640 3641 FT_Stream_SeekSet( stream, here ); 3642 } 3643 3644 FT_TRACE5(( "gvar: there %s %d tuple%s:\n", 3645 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are", 3646 tupleCount & GX_TC_TUPLE_COUNT_MASK, 3647 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" )); 3648 3649 for ( j = 0; j < n_points; j++ ) 3650 points_org[j] = outline->points[j]; 3651 3652 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ ) 3653 { 3654 FT_UInt tupleDataSize; 3655 FT_UInt tupleIndex; 3656 FT_Fixed apply; 3657 3658 3659 FT_TRACE6(( " tuple %d:\n", i )); 3660 3661 tupleDataSize = FT_GET_USHORT(); 3662 tupleIndex = FT_GET_USHORT(); 3663 3664 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 3665 { 3666 for ( j = 0; j < blend->num_axis; j++ ) 3667 tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */ 3668 /* short frac to fixed */ 3669 } 3670 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount ) 3671 { 3672 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 3673 " invalid tuple index\n" )); 3674 3675 error = FT_THROW( Invalid_Table ); 3676 goto Fail2; 3677 } 3678 else 3679 FT_MEM_COPY( 3680 tuple_coords, 3681 &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis], 3682 blend->num_axis * sizeof ( FT_Fixed ) ); 3683 3684 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 3685 { 3686 for ( j = 0; j < blend->num_axis; j++ ) 3687 im_start_coords[j] = FT_GET_SHORT() * 4; 3688 for ( j = 0; j < blend->num_axis; j++ ) 3689 im_end_coords[j] = FT_GET_SHORT() * 4; 3690 } 3691 3692 apply = ft_var_apply_tuple( blend, 3693 (FT_UShort)tupleIndex, 3694 tuple_coords, 3695 im_start_coords, 3696 im_end_coords ); 3697 3698 if ( apply == 0 ) /* tuple isn't active for our blend */ 3699 { 3700 offsetToData += tupleDataSize; 3701 continue; 3702 } 3703 3704 here = FT_Stream_FTell( stream ); 3705 3706 FT_Stream_SeekSet( stream, offsetToData ); 3707 3708 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) 3709 { 3710 localpoints = ft_var_readpackedpoints( stream, 3711 blend->gvar_size, 3712 &point_count ); 3713 points = localpoints; 3714 } 3715 else 3716 { 3717 points = sharedpoints; 3718 point_count = spoint_count; 3719 } 3720 3721 deltas_x = ft_var_readpackeddeltas( stream, 3722 blend->gvar_size, 3723 point_count == 0 ? n_points 3724 : point_count ); 3725 deltas_y = ft_var_readpackeddeltas( stream, 3726 blend->gvar_size, 3727 point_count == 0 ? n_points 3728 : point_count ); 3729 3730 if ( !points || !deltas_y || !deltas_x ) 3731 ; /* failure, ignore it */ 3732 3733 else if ( points == ALL_POINTS ) 3734 { 3735 #ifdef FT_DEBUG_LEVEL_TRACE 3736 int count = 0; 3737 #endif 3738 3739 3740 FT_TRACE7(( " point deltas:\n" )); 3741 3742 /* this means that there are deltas for every point in the glyph */ 3743 for ( j = 0; j < n_points; j++ ) 3744 { 3745 FT_Pos delta_x = FT_MulFix( deltas_x[j], apply ); 3746 FT_Pos delta_y = FT_MulFix( deltas_y[j], apply ); 3747 3748 3749 if ( j < n_points - 4 ) 3750 { 3751 outline->points[j].x += delta_x; 3752 outline->points[j].y += delta_y; 3753 } 3754 else 3755 { 3756 /* To avoid double adjustment of advance width or height, */ 3757 /* adjust phantom points only if there is no HVAR or VVAR */ 3758 /* support, respectively. */ 3759 if ( j == ( n_points - 4 ) && 3760 !( face->variation_support & 3761 TT_FACE_FLAG_VAR_LSB ) ) 3762 outline->points[j].x += delta_x; 3763 3764 else if ( j == ( n_points - 3 ) && 3765 !( face->variation_support & 3766 TT_FACE_FLAG_VAR_HADVANCE ) ) 3767 outline->points[j].x += delta_x; 3768 3769 else if ( j == ( n_points - 2 ) && 3770 !( face->variation_support & 3771 TT_FACE_FLAG_VAR_TSB ) ) 3772 outline->points[j].y += delta_y; 3773 3774 else if ( j == ( n_points - 1 ) && 3775 !( face->variation_support & 3776 TT_FACE_FLAG_VAR_VADVANCE ) ) 3777 outline->points[j].y += delta_y; 3778 } 3779 3780 #ifdef FT_DEBUG_LEVEL_TRACE 3781 if ( delta_x || delta_y ) 3782 { 3783 FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n", 3784 j, 3785 outline->points[j].x - delta_x, 3786 outline->points[j].y - delta_y, 3787 outline->points[j].x, 3788 outline->points[j].y )); 3789 count++; 3790 } 3791 #endif 3792 } 3793 3794 #ifdef FT_DEBUG_LEVEL_TRACE 3795 if ( !count ) 3796 FT_TRACE7(( " none\n" )); 3797 #endif 3798 } 3799 3800 else 3801 { 3802 #ifdef FT_DEBUG_LEVEL_TRACE 3803 int count = 0; 3804 #endif 3805 3806 3807 /* we have to interpolate the missing deltas similar to the */ 3808 /* IUP bytecode instruction */ 3809 for ( j = 0; j < n_points; j++ ) 3810 { 3811 has_delta[j] = FALSE; 3812 points_out[j] = points_org[j]; 3813 } 3814 3815 for ( j = 0; j < point_count; j++ ) 3816 { 3817 FT_UShort idx = points[j]; 3818 3819 3820 if ( idx >= n_points ) 3821 continue; 3822 3823 has_delta[idx] = TRUE; 3824 3825 points_out[idx].x += FT_MulFix( deltas_x[j], apply ); 3826 points_out[idx].y += FT_MulFix( deltas_y[j], apply ); 3827 } 3828 3829 /* no need to handle phantom points here, */ 3830 /* since solitary points can't be interpolated */ 3831 tt_interpolate_deltas( outline, 3832 points_out, 3833 points_org, 3834 has_delta ); 3835 3836 FT_TRACE7(( " point deltas:\n" )); 3837 3838 for ( j = 0; j < n_points; j++ ) 3839 { 3840 FT_Pos delta_x = points_out[j].x - points_org[j].x; 3841 FT_Pos delta_y = points_out[j].y - points_org[j].y; 3842 3843 3844 if ( j < n_points - 4 ) 3845 { 3846 outline->points[j].x += delta_x; 3847 outline->points[j].y += delta_y; 3848 } 3849 else 3850 { 3851 /* To avoid double adjustment of advance width or height, */ 3852 /* adjust phantom points only if there is no HVAR or VVAR */ 3853 /* support, respectively. */ 3854 if ( j == ( n_points - 4 ) && 3855 !( face->variation_support & 3856 TT_FACE_FLAG_VAR_LSB ) ) 3857 outline->points[j].x += delta_x; 3858 3859 else if ( j == ( n_points - 3 ) && 3860 !( face->variation_support & 3861 TT_FACE_FLAG_VAR_HADVANCE ) ) 3862 outline->points[j].x += delta_x; 3863 3864 else if ( j == ( n_points - 2 ) && 3865 !( face->variation_support & 3866 TT_FACE_FLAG_VAR_TSB ) ) 3867 outline->points[j].y += delta_y; 3868 3869 else if ( j == ( n_points - 1 ) && 3870 !( face->variation_support & 3871 TT_FACE_FLAG_VAR_VADVANCE ) ) 3872 outline->points[j].y += delta_y; 3873 } 3874 3875 #ifdef FT_DEBUG_LEVEL_TRACE 3876 if ( delta_x || delta_y ) 3877 { 3878 FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n", 3879 j, 3880 outline->points[j].x - delta_x, 3881 outline->points[j].y - delta_y, 3882 outline->points[j].x, 3883 outline->points[j].y )); 3884 count++; 3885 } 3886 #endif 3887 } 3888 3889 #ifdef FT_DEBUG_LEVEL_TRACE 3890 if ( !count ) 3891 FT_TRACE7(( " none\n" )); 3892 #endif 3893 } 3894 3895 if ( localpoints != ALL_POINTS ) 3896 FT_FREE( localpoints ); 3897 FT_FREE( deltas_x ); 3898 FT_FREE( deltas_y ); 3899 3900 offsetToData += tupleDataSize; 3901 3902 FT_Stream_SeekSet( stream, here ); 3903 } 3904 3905 FT_TRACE5(( "\n" )); 3906 3907 Fail2: 3908 if ( sharedpoints != ALL_POINTS ) 3909 FT_FREE( sharedpoints ); 3910 FT_FREE( tuple_coords ); 3911 FT_FREE( im_start_coords ); 3912 FT_FREE( im_end_coords ); 3913 3914 FT_FRAME_EXIT(); 3915 3916 Fail1: 3917 FT_FREE( points_org ); 3918 FT_FREE( points_out ); 3919 FT_FREE( has_delta ); 3920 3921 return error; 3922 } 3923 3924 3925 /*************************************************************************/ 3926 /* */ 3927 /* <Function> */ 3928 /* tt_get_var_blend */ 3929 /* */ 3930 /* <Description> */ 3931 /* An extended internal version of `TT_Get_MM_Blend' that returns */ 3932 /* pointers instead of copying data, without any initialization of */ 3933 /* the MM machinery in case it isn't loaded yet. */ 3934 /* */ 3935 FT_LOCAL_DEF( FT_Error ) 3936 tt_get_var_blend( TT_Face face, 3937 FT_UInt *num_coords, 3938 FT_Fixed* *coords, 3939 FT_Fixed* *normalizedcoords, 3940 FT_MM_Var* *mm_var ) 3941 { 3942 if ( face->blend ) 3943 { 3944 if ( num_coords ) 3945 *num_coords = face->blend->num_axis; 3946 if ( coords ) 3947 *coords = face->blend->coords; 3948 if ( normalizedcoords ) 3949 *normalizedcoords = face->blend->normalizedcoords; 3950 if ( mm_var ) 3951 *mm_var = face->blend->mmvar; 3952 } 3953 else 3954 { 3955 if ( num_coords ) 3956 *num_coords = 0; 3957 if ( coords ) 3958 *coords = NULL; 3959 if ( mm_var ) 3960 *mm_var = NULL; 3961 } 3962 3963 return FT_Err_Ok; 3964 } 3965 3966 3967 static void 3968 ft_var_done_item_variation_store( TT_Face face, 3969 GX_ItemVarStore itemStore ) 3970 { 3971 FT_Memory memory = FT_FACE_MEMORY( face ); 3972 FT_UInt i; 3973 3974 3975 if ( itemStore->varData ) 3976 { 3977 for ( i = 0; i < itemStore->dataCount; i++ ) 3978 { 3979 FT_FREE( itemStore->varData[i].regionIndices ); 3980 FT_FREE( itemStore->varData[i].deltaSet ); 3981 } 3982 3983 FT_FREE( itemStore->varData ); 3984 } 3985 3986 if ( itemStore->varRegionList ) 3987 { 3988 for ( i = 0; i < itemStore->regionCount; i++ ) 3989 FT_FREE( itemStore->varRegionList[i].axisList ); 3990 3991 FT_FREE( itemStore->varRegionList ); 3992 } 3993 } 3994 3995 3996 /*************************************************************************/ 3997 /* */ 3998 /* <Function> */ 3999 /* tt_done_blend */ 4000 /* */ 4001 /* <Description> */ 4002 /* Free the blend internal data structure. */ 4003 /* */ 4004 FT_LOCAL_DEF( void ) 4005 tt_done_blend( TT_Face face ) 4006 { 4007 FT_Memory memory = FT_FACE_MEMORY( face ); 4008 GX_Blend blend = face->blend; 4009 4010 4011 if ( blend ) 4012 { 4013 FT_UInt i, num_axes; 4014 4015 4016 /* blend->num_axis might not be set up yet */ 4017 num_axes = blend->mmvar->num_axis; 4018 4019 FT_FREE( blend->coords ); 4020 FT_FREE( blend->normalizedcoords ); 4021 FT_FREE( blend->normalized_stylecoords ); 4022 FT_FREE( blend->mmvar ); 4023 4024 if ( blend->avar_segment ) 4025 { 4026 for ( i = 0; i < num_axes; i++ ) 4027 FT_FREE( blend->avar_segment[i].correspondence ); 4028 FT_FREE( blend->avar_segment ); 4029 } 4030 4031 if ( blend->hvar_table ) 4032 { 4033 ft_var_done_item_variation_store( face, 4034 &blend->hvar_table->itemStore ); 4035 4036 FT_FREE( blend->hvar_table->widthMap.innerIndex ); 4037 FT_FREE( blend->hvar_table->widthMap.outerIndex ); 4038 FT_FREE( blend->hvar_table ); 4039 } 4040 4041 if ( blend->vvar_table ) 4042 { 4043 ft_var_done_item_variation_store( face, 4044 &blend->vvar_table->itemStore ); 4045 4046 FT_FREE( blend->vvar_table->widthMap.innerIndex ); 4047 FT_FREE( blend->vvar_table->widthMap.outerIndex ); 4048 FT_FREE( blend->vvar_table ); 4049 } 4050 4051 if ( blend->mvar_table ) 4052 { 4053 ft_var_done_item_variation_store( face, 4054 &blend->mvar_table->itemStore ); 4055 4056 FT_FREE( blend->mvar_table->values ); 4057 FT_FREE( blend->mvar_table ); 4058 } 4059 4060 FT_FREE( blend->tuplecoords ); 4061 FT_FREE( blend->glyphoffsets ); 4062 FT_FREE( blend ); 4063 } 4064 } 4065 4066 #else /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */ 4067 4068 /* ANSI C doesn't like empty source files */ 4069 typedef int _tt_gxvar_dummy; 4070 4071 #endif /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */ 4072 4073 4074 /* END */