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