1 /***************************************************************************/ 2 /* */ 3 /* ttgxvar.c */ 4 /* */ 5 /* TrueType GX Font Variation loader */ 6 /* */ 7 /* Copyright 2004-2016 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 `fvar' is inconsistent. At one point it says */ 26 /* that `countSizePairs' should be 3, at another point 2. It should */ 27 /* be 2. */ 28 /* */ 29 /* The documentation for `gvar' is not intelligible; `cvar' refers you */ 30 /* to `gvar' and is thus also incomprehensible. */ 31 /* */ 32 /* The documentation for `avar' appears correct, but Apple has no fonts */ 33 /* with an `avar' table, so it is hard to test. */ 34 /* */ 35 /* Many thanks to John Jenkins (at Apple) in figuring this out. */ 36 /* */ 37 /* */ 38 /* Apple's `kern' table has some references to tuple indices, but as */ 39 /* there is no indication where these indices are defined, nor how to */ 40 /* interpolate the kerning values (different tuples have different */ 41 /* classes) this issue is ignored. */ 42 /* */ 43 /*************************************************************************/ 44 45 46 #include <ft2build.h> 47 #include FT_INTERNAL_DEBUG_H 48 #include FT_CONFIG_CONFIG_H 49 #include FT_INTERNAL_STREAM_H 50 #include FT_INTERNAL_SFNT_H 51 #include FT_TRUETYPE_TAGS_H 52 #include FT_MULTIPLE_MASTERS_H 53 54 #include "ttpload.h" 55 #include "ttgxvar.h" 56 57 #include "tterrors.h" 58 59 60 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT 61 62 63 #define FT_Stream_FTell( stream ) \ 64 (FT_ULong)( (stream)->cursor - (stream)->base ) 65 #define FT_Stream_SeekSet( stream, off ) \ 66 ( (stream)->cursor = (stream)->base + (off) ) 67 68 69 /*************************************************************************/ 70 /* */ 71 /* The macro FT_COMPONENT is used in trace mode. It is an implicit */ 72 /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log */ 73 /* messages during execution. */ 74 /* */ 75 #undef FT_COMPONENT 76 #define FT_COMPONENT trace_ttgxvar 77 78 79 /*************************************************************************/ 80 /*************************************************************************/ 81 /***** *****/ 82 /***** Internal Routines *****/ 83 /***** *****/ 84 /*************************************************************************/ 85 /*************************************************************************/ 86 87 88 /*************************************************************************/ 89 /* */ 90 /* The macro ALL_POINTS is used in `ft_var_readpackedpoints'. It */ 91 /* indicates that there is a delta for every point without needing to */ 92 /* enumerate all of them. */ 93 /* */ 94 95 /* ensure that value `0' has the same width as a pointer */ 96 #define ALL_POINTS (FT_UShort*)~(FT_PtrDist)0 97 98 99 #define GX_PT_POINTS_ARE_WORDS 0x80U 100 #define GX_PT_POINT_RUN_COUNT_MASK 0x7FU 101 102 103 /*************************************************************************/ 104 /* */ 105 /* <Function> */ 106 /* ft_var_readpackedpoints */ 107 /* */ 108 /* <Description> */ 109 /* Read a set of points to which the following deltas will apply. */ 110 /* Points are packed with a run length encoding. */ 111 /* */ 112 /* <Input> */ 113 /* stream :: The data stream. */ 114 /* */ 115 /* size :: The size of the table holding the data. */ 116 /* */ 117 /* <Output> */ 118 /* point_cnt :: The number of points read. A zero value means that */ 119 /* all points in the glyph will be affected, without */ 120 /* enumerating them individually. */ 121 /* */ 122 /* <Return> */ 123 /* An array of FT_UShort containing the affected points or the */ 124 /* special value ALL_POINTS. */ 125 /* */ 126 static FT_UShort* ft_var_readpackedpoints(FT_Stream stream,FT_ULong size,FT_UInt * point_cnt)127 ft_var_readpackedpoints( FT_Stream stream, 128 FT_ULong size, 129 FT_UInt *point_cnt ) 130 { 131 FT_UShort *points = NULL; 132 FT_UInt n; 133 FT_UInt runcnt; 134 FT_UInt i, j; 135 FT_UShort first; 136 FT_Memory memory = stream->memory; 137 FT_Error error = FT_Err_Ok; 138 139 FT_UNUSED( error ); 140 141 142 *point_cnt = 0; 143 144 n = FT_GET_BYTE(); 145 if ( n == 0 ) 146 return ALL_POINTS; 147 148 if ( n & GX_PT_POINTS_ARE_WORDS ) 149 { 150 n &= GX_PT_POINT_RUN_COUNT_MASK; 151 n <<= 8; 152 n |= FT_GET_BYTE(); 153 } 154 155 if ( n > size ) 156 { 157 FT_TRACE1(( "ft_var_readpackedpoints: number of points too large\n" )); 158 return NULL; 159 } 160 161 if ( FT_NEW_ARRAY( points, n ) ) 162 return NULL; 163 164 *point_cnt = n; 165 166 i = 0; 167 while ( i < n ) 168 { 169 runcnt = FT_GET_BYTE(); 170 if ( runcnt & GX_PT_POINTS_ARE_WORDS ) 171 { 172 runcnt &= GX_PT_POINT_RUN_COUNT_MASK; 173 first = FT_GET_USHORT(); 174 points[i++] = first; 175 176 if ( runcnt < 1 || i + runcnt > n ) 177 goto Exit; 178 179 /* first point not included in run count */ 180 for ( j = 0; j < runcnt; j++ ) 181 { 182 first += FT_GET_USHORT(); 183 points[i++] = first; 184 } 185 } 186 else 187 { 188 first = FT_GET_BYTE(); 189 points[i++] = first; 190 191 if ( runcnt < 1 || i + runcnt > n ) 192 goto Exit; 193 194 for ( j = 0; j < runcnt; j++ ) 195 { 196 first += FT_GET_BYTE(); 197 points[i++] = first; 198 } 199 } 200 } 201 202 Exit: 203 return points; 204 } 205 206 207 #define GX_DT_DELTAS_ARE_ZERO 0x80U 208 #define GX_DT_DELTAS_ARE_WORDS 0x40U 209 #define GX_DT_DELTA_RUN_COUNT_MASK 0x3FU 210 211 212 /*************************************************************************/ 213 /* */ 214 /* <Function> */ 215 /* ft_var_readpackeddeltas */ 216 /* */ 217 /* <Description> */ 218 /* Read a set of deltas. These are packed slightly differently than */ 219 /* points. In particular there is no overall count. */ 220 /* */ 221 /* <Input> */ 222 /* stream :: The data stream. */ 223 /* */ 224 /* size :: The size of the table holding the data. */ 225 /* */ 226 /* delta_cnt :: The number of deltas to be read. */ 227 /* */ 228 /* <Return> */ 229 /* An array of FT_Short containing the deltas for the affected */ 230 /* points. (This only gets the deltas for one dimension. It will */ 231 /* generally be called twice, once for x, once for y. When used in */ 232 /* cvt table, it will only be called once.) */ 233 /* */ 234 static FT_Short* ft_var_readpackeddeltas(FT_Stream stream,FT_ULong size,FT_UInt delta_cnt)235 ft_var_readpackeddeltas( FT_Stream stream, 236 FT_ULong size, 237 FT_UInt delta_cnt ) 238 { 239 FT_Short *deltas = NULL; 240 FT_UInt runcnt, cnt; 241 FT_UInt i, j; 242 FT_Memory memory = stream->memory; 243 FT_Error error = FT_Err_Ok; 244 245 FT_UNUSED( error ); 246 247 248 if ( delta_cnt > size ) 249 { 250 FT_TRACE1(( "ft_var_readpackeddeltas: number of points too large\n" )); 251 return NULL; 252 } 253 254 if ( FT_NEW_ARRAY( deltas, delta_cnt ) ) 255 return NULL; 256 257 i = 0; 258 while ( i < delta_cnt ) 259 { 260 runcnt = FT_GET_BYTE(); 261 cnt = runcnt & GX_DT_DELTA_RUN_COUNT_MASK; 262 263 if ( runcnt & GX_DT_DELTAS_ARE_ZERO ) 264 { 265 /* `runcnt' zeroes get added */ 266 for ( j = 0; j <= cnt && i < delta_cnt; j++ ) 267 deltas[i++] = 0; 268 } 269 else if ( runcnt & GX_DT_DELTAS_ARE_WORDS ) 270 { 271 /* `runcnt' shorts from the stack */ 272 for ( j = 0; j <= cnt && i < delta_cnt; j++ ) 273 deltas[i++] = FT_GET_SHORT(); 274 } 275 else 276 { 277 /* `runcnt' signed bytes from the stack */ 278 for ( j = 0; j <= cnt && i < delta_cnt; j++ ) 279 deltas[i++] = FT_GET_CHAR(); 280 } 281 282 if ( j <= cnt ) 283 { 284 /* bad format */ 285 FT_FREE( deltas ); 286 return NULL; 287 } 288 } 289 290 return deltas; 291 } 292 293 294 /*************************************************************************/ 295 /* */ 296 /* <Function> */ 297 /* ft_var_load_avar */ 298 /* */ 299 /* <Description> */ 300 /* Parse the `avar' table if present. It need not be, so we return */ 301 /* nothing. */ 302 /* */ 303 /* <InOut> */ 304 /* face :: The font face. */ 305 /* */ 306 static void ft_var_load_avar(TT_Face face)307 ft_var_load_avar( TT_Face face ) 308 { 309 FT_Stream stream = FT_FACE_STREAM( face ); 310 FT_Memory memory = stream->memory; 311 GX_Blend blend = face->blend; 312 GX_AVarSegment segment; 313 FT_Error error = FT_Err_Ok; 314 FT_Long version; 315 FT_Long axisCount; 316 FT_Int i, j; 317 FT_ULong table_len; 318 319 FT_UNUSED( error ); 320 321 322 FT_TRACE2(( "AVAR " )); 323 324 blend->avar_checked = TRUE; 325 error = face->goto_table( face, TTAG_avar, stream, &table_len ); 326 if ( error ) 327 { 328 FT_TRACE2(( "is missing\n" )); 329 return; 330 } 331 332 if ( FT_FRAME_ENTER( table_len ) ) 333 return; 334 335 version = FT_GET_LONG(); 336 axisCount = FT_GET_LONG(); 337 338 if ( version != 0x00010000L ) 339 { 340 FT_TRACE2(( "bad table version\n" )); 341 goto Exit; 342 } 343 344 FT_TRACE2(( "loaded\n" )); 345 346 if ( axisCount != (FT_Long)blend->mmvar->num_axis ) 347 { 348 FT_TRACE2(( "ft_var_load_avar: number of axes in `avar' and `cvar'\n" 349 " table are different\n" )); 350 goto Exit; 351 } 352 353 if ( FT_NEW_ARRAY( blend->avar_segment, axisCount ) ) 354 goto Exit; 355 356 segment = &blend->avar_segment[0]; 357 for ( i = 0; i < axisCount; i++, segment++ ) 358 { 359 FT_TRACE5(( " axis %d:\n", i )); 360 361 segment->pairCount = FT_GET_USHORT(); 362 if ( (FT_ULong)segment->pairCount * 4 > table_len || 363 FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) ) 364 { 365 /* Failure. Free everything we have done so far. We must do */ 366 /* it right now since loading the `avar' table is optional. */ 367 368 for ( j = i - 1; j >= 0; j-- ) 369 FT_FREE( blend->avar_segment[j].correspondence ); 370 371 FT_FREE( blend->avar_segment ); 372 blend->avar_segment = NULL; 373 goto Exit; 374 } 375 376 for ( j = 0; j < segment->pairCount; j++ ) 377 { 378 /* convert to Fixed */ 379 segment->correspondence[j].fromCoord = FT_GET_SHORT() * 4; 380 segment->correspondence[j].toCoord = FT_GET_SHORT() * 4; 381 382 FT_TRACE5(( " mapping %.4f to %.4f\n", 383 segment->correspondence[j].fromCoord / 65536.0, 384 segment->correspondence[j].toCoord / 65536.0 )); 385 } 386 387 FT_TRACE5(( "\n" )); 388 } 389 390 Exit: 391 FT_FRAME_EXIT(); 392 } 393 394 395 typedef struct GX_GVar_Head_ 396 { 397 FT_Long version; 398 FT_UShort axisCount; 399 FT_UShort globalCoordCount; 400 FT_ULong offsetToCoord; 401 FT_UShort glyphCount; 402 FT_UShort flags; 403 FT_ULong offsetToData; 404 405 } GX_GVar_Head; 406 407 408 /*************************************************************************/ 409 /* */ 410 /* <Function> */ 411 /* ft_var_load_gvar */ 412 /* */ 413 /* <Description> */ 414 /* Parse the `gvar' table if present. If `fvar' is there, `gvar' had */ 415 /* better be there too. */ 416 /* */ 417 /* <InOut> */ 418 /* face :: The font face. */ 419 /* */ 420 /* <Return> */ 421 /* FreeType error code. 0 means success. */ 422 /* */ 423 static FT_Error ft_var_load_gvar(TT_Face face)424 ft_var_load_gvar( TT_Face face ) 425 { 426 FT_Stream stream = FT_FACE_STREAM( face ); 427 FT_Memory memory = stream->memory; 428 GX_Blend blend = face->blend; 429 FT_Error error; 430 FT_UInt i, j; 431 FT_ULong table_len; 432 FT_ULong gvar_start; 433 FT_ULong offsetToData; 434 GX_GVar_Head gvar_head; 435 436 static const FT_Frame_Field gvar_fields[] = 437 { 438 439 #undef FT_STRUCTURE 440 #define FT_STRUCTURE GX_GVar_Head 441 442 FT_FRAME_START( 20 ), 443 FT_FRAME_LONG ( version ), 444 FT_FRAME_USHORT( axisCount ), 445 FT_FRAME_USHORT( globalCoordCount ), 446 FT_FRAME_ULONG ( offsetToCoord ), 447 FT_FRAME_USHORT( glyphCount ), 448 FT_FRAME_USHORT( flags ), 449 FT_FRAME_ULONG ( offsetToData ), 450 FT_FRAME_END 451 }; 452 453 454 FT_TRACE2(( "GVAR " )); 455 456 if ( ( error = face->goto_table( face, 457 TTAG_gvar, 458 stream, 459 &table_len ) ) != 0 ) 460 { 461 FT_TRACE2(( "is missing\n" )); 462 goto Exit; 463 } 464 465 gvar_start = FT_STREAM_POS( ); 466 if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) ) 467 goto Exit; 468 469 if ( gvar_head.version != 0x00010000L ) 470 { 471 FT_TRACE1(( "bad table version\n" )); 472 error = FT_THROW( Invalid_Table ); 473 goto Exit; 474 } 475 476 if ( gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis ) 477 { 478 FT_TRACE1(( "ft_var_load_gvar: number of axes in `gvar' and `cvar'\n" 479 " table are different\n" )); 480 error = FT_THROW( Invalid_Table ); 481 goto Exit; 482 } 483 484 /* rough sanity check, ignoring offsets */ 485 if ( (FT_ULong)gvar_head.globalCoordCount * gvar_head.axisCount > 486 table_len / 2 ) 487 { 488 FT_TRACE1(( "ft_var_load_gvar:" 489 " invalid number of global coordinates\n" )); 490 error = FT_THROW( Invalid_Table ); 491 goto Exit; 492 } 493 494 /* rough sanity check: offsets can be either 2 or 4 bytes, */ 495 /* and a single variation needs at least 4 bytes per glyph */ 496 if ( (FT_ULong)gvar_head.glyphCount * 497 ( ( gvar_head.flags & 1 ) ? 8 : 6 ) > table_len ) 498 { 499 FT_TRACE1(( "ft_var_load_gvar: invalid number of glyphs\n" )); 500 error = FT_THROW( Invalid_Table ); 501 goto Exit; 502 } 503 504 FT_TRACE2(( "loaded\n" )); 505 506 blend->gvar_size = table_len; 507 blend->tuplecount = gvar_head.globalCoordCount; 508 blend->gv_glyphcnt = gvar_head.glyphCount; 509 offsetToData = gvar_start + gvar_head.offsetToData; 510 511 FT_TRACE5(( "gvar: there are %d shared coordinates:\n", 512 blend->tuplecount )); 513 514 if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) ) 515 goto Exit; 516 517 if ( gvar_head.flags & 1 ) 518 { 519 /* long offsets (one more offset than glyphs, to mark size of last) */ 520 if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) ) 521 goto Exit; 522 523 for ( i = 0; i <= blend->gv_glyphcnt; i++ ) 524 blend->glyphoffsets[i] = offsetToData + FT_GET_ULONG(); 525 526 FT_FRAME_EXIT(); 527 } 528 else 529 { 530 /* short offsets (one more offset than glyphs, to mark size of last) */ 531 if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) ) 532 goto Exit; 533 534 for ( i = 0; i <= blend->gv_glyphcnt; i++ ) 535 blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2; 536 /* XXX: Undocumented: `*2'! */ 537 538 FT_FRAME_EXIT(); 539 } 540 541 if ( blend->tuplecount != 0 ) 542 { 543 if ( FT_NEW_ARRAY( blend->tuplecoords, 544 gvar_head.axisCount * blend->tuplecount ) ) 545 goto Exit; 546 547 if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord ) || 548 FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L ) ) 549 goto Exit; 550 551 for ( i = 0; i < blend->tuplecount; i++ ) 552 { 553 FT_TRACE5(( " [ " )); 554 for ( j = 0; j < (FT_UInt)gvar_head.axisCount; j++ ) 555 { 556 blend->tuplecoords[i * gvar_head.axisCount + j] = 557 FT_GET_SHORT() * 4; /* convert to FT_Fixed */ 558 FT_TRACE5(( "%.4f ", 559 blend->tuplecoords[i * gvar_head.axisCount + j] / 65536.0 )); 560 } 561 FT_TRACE5(( "]\n" )); 562 } 563 564 FT_TRACE5(( "\n" )); 565 566 FT_FRAME_EXIT(); 567 } 568 569 Exit: 570 return error; 571 } 572 573 574 /*************************************************************************/ 575 /* */ 576 /* <Function> */ 577 /* ft_var_apply_tuple */ 578 /* */ 579 /* <Description> */ 580 /* Figure out whether a given tuple (design) applies to the current */ 581 /* blend, and if so, what is the scaling factor. */ 582 /* */ 583 /* <Input> */ 584 /* blend :: The current blend of the font. */ 585 /* */ 586 /* tupleIndex :: A flag saying whether this is an intermediate */ 587 /* tuple or not. */ 588 /* */ 589 /* tuple_coords :: The coordinates of the tuple in normalized axis */ 590 /* units. */ 591 /* */ 592 /* im_start_coords :: The initial coordinates where this tuple starts */ 593 /* to apply (for intermediate coordinates). */ 594 /* */ 595 /* im_end_coords :: The final coordinates after which this tuple no */ 596 /* longer applies (for intermediate coordinates). */ 597 /* */ 598 /* <Return> */ 599 /* An FT_Fixed value containing the scaling factor. */ 600 /* */ 601 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)602 ft_var_apply_tuple( GX_Blend blend, 603 FT_UShort tupleIndex, 604 FT_Fixed* tuple_coords, 605 FT_Fixed* im_start_coords, 606 FT_Fixed* im_end_coords ) 607 { 608 FT_UInt i; 609 FT_Fixed apply = 0x10000L; 610 611 612 for ( i = 0; i < blend->num_axis; i++ ) 613 { 614 FT_TRACE6(( " axis coordinate %d (%.4f):\n", 615 i, blend->normalizedcoords[i] / 65536.0 )); 616 if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) ) 617 FT_TRACE6(( " intermediate coordinates %d (%.4f, %.4f):\n", 618 i, 619 im_start_coords[i] / 65536.0, 620 im_end_coords[i] / 65536.0 )); 621 622 /* It's not clear why (for intermediate tuples) we don't need */ 623 /* to check against start/end -- the documentation says we don't. */ 624 /* Similarly, it's unclear why we don't need to scale along the */ 625 /* axis. */ 626 627 if ( tuple_coords[i] == 0 ) 628 { 629 FT_TRACE6(( " tuple coordinate is zero, ignored\n", i )); 630 continue; 631 } 632 633 if ( blend->normalizedcoords[i] == 0 ) 634 { 635 FT_TRACE6(( " axis coordinate is zero, stop\n" )); 636 apply = 0; 637 break; 638 } 639 640 if ( blend->normalizedcoords[i] == tuple_coords[i] ) 641 { 642 FT_TRACE6(( " tuple coordinate value %.4f fits perfectly\n", 643 tuple_coords[i] / 65536.0 )); 644 /* `apply' does not change */ 645 continue; 646 } 647 648 if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) ) 649 { 650 /* not an intermediate tuple */ 651 652 if ( blend->normalizedcoords[i] < FT_MIN( 0, tuple_coords[i] ) || 653 blend->normalizedcoords[i] > FT_MAX( 0, tuple_coords[i] ) ) 654 { 655 FT_TRACE6(( " tuple coordinate value %.4f is exceeded, stop\n", 656 tuple_coords[i] / 65536.0 )); 657 apply = 0; 658 break; 659 } 660 661 FT_TRACE6(( " tuple coordinate value %.4f fits\n", 662 tuple_coords[i] / 65536.0 )); 663 apply = FT_MulDiv( apply, 664 blend->normalizedcoords[i], 665 tuple_coords[i] ); 666 } 667 else 668 { 669 /* intermediate tuple */ 670 671 if ( blend->normalizedcoords[i] < im_start_coords[i] || 672 blend->normalizedcoords[i] > im_end_coords[i] ) 673 { 674 FT_TRACE6(( " intermediate tuple range [%.4f;%.4f] is exceeded," 675 " stop\n", 676 im_start_coords[i] / 65536.0, 677 im_end_coords[i] / 65536.0 )); 678 apply = 0; 679 break; 680 } 681 682 else if ( blend->normalizedcoords[i] < tuple_coords[i] ) 683 { 684 FT_TRACE6(( " intermediate tuple range [%.4f;%.4f] fits\n", 685 im_start_coords[i] / 65536.0, 686 im_end_coords[i] / 65536.0 )); 687 apply = FT_MulDiv( apply, 688 blend->normalizedcoords[i] - im_start_coords[i], 689 tuple_coords[i] - im_start_coords[i] ); 690 } 691 692 else 693 { 694 FT_TRACE6(( " intermediate tuple range [%.4f;%.4f] fits\n", 695 im_start_coords[i] / 65536.0, 696 im_end_coords[i] / 65536.0 )); 697 apply = FT_MulDiv( apply, 698 im_end_coords[i] - blend->normalizedcoords[i], 699 im_end_coords[i] - tuple_coords[i] ); 700 } 701 } 702 } 703 704 FT_TRACE6(( " apply factor is %.4f\n", apply / 65536.0 )); 705 706 return apply; 707 } 708 709 710 /*************************************************************************/ 711 /*************************************************************************/ 712 /***** *****/ 713 /***** MULTIPLE MASTERS SERVICE FUNCTIONS *****/ 714 /***** *****/ 715 /*************************************************************************/ 716 /*************************************************************************/ 717 718 719 typedef struct GX_FVar_Head_ 720 { 721 FT_Long version; 722 FT_UShort offsetToData; 723 FT_UShort countSizePairs; 724 FT_UShort axisCount; 725 FT_UShort axisSize; 726 FT_UShort instanceCount; 727 FT_UShort instanceSize; 728 729 } GX_FVar_Head; 730 731 732 typedef struct fvar_axis_ 733 { 734 FT_ULong axisTag; 735 FT_Fixed minValue; 736 FT_Fixed defaultValue; 737 FT_Fixed maxValue; 738 FT_UShort flags; 739 FT_UShort nameID; 740 741 } GX_FVar_Axis; 742 743 744 /*************************************************************************/ 745 /* */ 746 /* <Function> */ 747 /* TT_Get_MM_Var */ 748 /* */ 749 /* <Description> */ 750 /* Check that the font's `fvar' table is valid, parse it, and return */ 751 /* those data. */ 752 /* */ 753 /* <InOut> */ 754 /* face :: The font face. */ 755 /* TT_Get_MM_Var initializes the blend structure. */ 756 /* */ 757 /* <Output> */ 758 /* master :: The `fvar' data (must be freed by caller). Can be NULL, */ 759 /* which makes this function simply load MM support. */ 760 /* */ 761 /* <Return> */ 762 /* FreeType error code. 0 means success. */ 763 /* */ 764 FT_LOCAL_DEF( FT_Error ) TT_Get_MM_Var(TT_Face face,FT_MM_Var ** master)765 TT_Get_MM_Var( TT_Face face, 766 FT_MM_Var* *master ) 767 { 768 FT_Stream stream = face->root.stream; 769 FT_Memory memory = face->root.memory; 770 FT_ULong table_len; 771 FT_Error error = FT_Err_Ok; 772 FT_ULong fvar_start; 773 FT_Int i, j; 774 FT_MM_Var* mmvar = NULL; 775 FT_Fixed* next_coords; 776 FT_String* next_name; 777 FT_Var_Axis* a; 778 FT_Var_Named_Style* ns; 779 GX_FVar_Head fvar_head; 780 781 static const FT_Frame_Field fvar_fields[] = 782 { 783 784 #undef FT_STRUCTURE 785 #define FT_STRUCTURE GX_FVar_Head 786 787 FT_FRAME_START( 16 ), 788 FT_FRAME_LONG ( version ), 789 FT_FRAME_USHORT( offsetToData ), 790 FT_FRAME_USHORT( countSizePairs ), 791 FT_FRAME_USHORT( axisCount ), 792 FT_FRAME_USHORT( axisSize ), 793 FT_FRAME_USHORT( instanceCount ), 794 FT_FRAME_USHORT( instanceSize ), 795 FT_FRAME_END 796 }; 797 798 static const FT_Frame_Field fvaraxis_fields[] = 799 { 800 801 #undef FT_STRUCTURE 802 #define FT_STRUCTURE GX_FVar_Axis 803 804 FT_FRAME_START( 20 ), 805 FT_FRAME_ULONG ( axisTag ), 806 FT_FRAME_LONG ( minValue ), 807 FT_FRAME_LONG ( defaultValue ), 808 FT_FRAME_LONG ( maxValue ), 809 FT_FRAME_USHORT( flags ), 810 FT_FRAME_USHORT( nameID ), 811 FT_FRAME_END 812 }; 813 814 815 /* read the font data and set up the internal representation */ 816 /* if not already done */ 817 818 if ( face->blend == NULL ) 819 { 820 FT_TRACE2(( "FVAR " )); 821 822 /* both `fvar' and `gvar' must be present */ 823 if ( ( error = face->goto_table( face, TTAG_gvar, 824 stream, &table_len ) ) != 0 ) 825 { 826 FT_TRACE1(( "\n" 827 "TT_Get_MM_Var: `gvar' table is missing\n" )); 828 goto Exit; 829 } 830 831 if ( ( error = face->goto_table( face, TTAG_fvar, 832 stream, &table_len ) ) != 0 ) 833 { 834 FT_TRACE1(( "is missing\n" )); 835 goto Exit; 836 } 837 838 fvar_start = FT_STREAM_POS( ); 839 840 if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) ) 841 goto Exit; 842 843 if ( fvar_head.version != (FT_Long)0x00010000L || 844 #if 0 845 /* fonts like `JamRegular.ttf' have an incorrect value for */ 846 /* `countSizePairs'; since value 2 is hard-coded in `fvar' */ 847 /* version 1.0, we simply ignore it */ 848 fvar_head.countSizePairs != 2 || 849 #endif 850 fvar_head.axisSize != 20 || 851 /* axisCount limit implied by 16-bit instanceSize */ 852 fvar_head.axisCount > 0x3FFE || 853 fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount || 854 /* instanceCount limit implied by limited range of name IDs */ 855 fvar_head.instanceCount > 0x7EFF || 856 fvar_head.offsetToData + fvar_head.axisCount * 20U + 857 fvar_head.instanceCount * fvar_head.instanceSize > table_len ) 858 { 859 FT_TRACE1(( "\n" 860 "TT_Get_MM_Var: invalid `fvar' header\n" )); 861 error = FT_THROW( Invalid_Table ); 862 goto Exit; 863 } 864 865 FT_TRACE2(( "loaded\n" )); 866 867 FT_TRACE5(( "number of GX style axes: %d\n", fvar_head.axisCount )); 868 869 if ( FT_NEW( face->blend ) ) 870 goto Exit; 871 872 /* cannot overflow 32-bit arithmetic because of limits above */ 873 face->blend->mmvar_len = 874 sizeof ( FT_MM_Var ) + 875 fvar_head.axisCount * sizeof ( FT_Var_Axis ) + 876 fvar_head.instanceCount * sizeof ( FT_Var_Named_Style ) + 877 fvar_head.instanceCount * fvar_head.axisCount * sizeof ( FT_Fixed ) + 878 5 * fvar_head.axisCount; 879 880 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) ) 881 goto Exit; 882 face->blend->mmvar = mmvar; 883 884 /* set up pointers and offsets into the `mmvar' array; */ 885 /* the data gets filled in later on */ 886 887 mmvar->num_axis = 888 fvar_head.axisCount; 889 mmvar->num_designs = 890 ~0U; /* meaningless in this context; each glyph */ 891 /* may have a different number of designs */ 892 /* (or tuples, as called by Apple) */ 893 mmvar->num_namedstyles = 894 fvar_head.instanceCount; 895 mmvar->axis = 896 (FT_Var_Axis*)&( mmvar[1] ); 897 mmvar->namedstyle = 898 (FT_Var_Named_Style*)&( mmvar->axis[fvar_head.axisCount] ); 899 900 next_coords = 901 (FT_Fixed*)&( mmvar->namedstyle[fvar_head.instanceCount] ); 902 for ( i = 0; i < fvar_head.instanceCount; i++ ) 903 { 904 mmvar->namedstyle[i].coords = next_coords; 905 next_coords += fvar_head.axisCount; 906 } 907 908 next_name = (FT_String*)next_coords; 909 for ( i = 0; i < fvar_head.axisCount; i++ ) 910 { 911 mmvar->axis[i].name = next_name; 912 next_name += 5; 913 } 914 915 /* now fill in the data */ 916 917 if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) ) 918 goto Exit; 919 920 a = mmvar->axis; 921 for ( i = 0; i < fvar_head.axisCount; i++ ) 922 { 923 GX_FVar_Axis axis_rec; 924 925 926 if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) ) 927 goto Exit; 928 a->tag = axis_rec.axisTag; 929 a->minimum = axis_rec.minValue; 930 a->def = axis_rec.defaultValue; 931 a->maximum = axis_rec.maxValue; 932 a->strid = axis_rec.nameID; 933 934 a->name[0] = (FT_String)( a->tag >> 24 ); 935 a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF ); 936 a->name[2] = (FT_String)( ( a->tag >> 8 ) & 0xFF ); 937 a->name[3] = (FT_String)( ( a->tag ) & 0xFF ); 938 a->name[4] = '\0'; 939 940 FT_TRACE5(( " \"%s\": minimum=%.4f, default=%.4f, maximum=%.4f\n", 941 a->name, 942 a->minimum / 65536.0, 943 a->def / 65536.0, 944 a->maximum / 65536.0 )); 945 946 a++; 947 } 948 949 FT_TRACE5(( "\n" )); 950 951 ns = mmvar->namedstyle; 952 for ( i = 0; i < fvar_head.instanceCount; i++, ns++ ) 953 { 954 if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) ) 955 goto Exit; 956 957 ns->strid = FT_GET_USHORT(); 958 (void) /* flags = */ FT_GET_USHORT(); 959 960 for ( j = 0; j < fvar_head.axisCount; j++ ) 961 ns->coords[j] = FT_GET_LONG(); 962 963 FT_FRAME_EXIT(); 964 } 965 } 966 967 /* fill the output array if requested */ 968 969 if ( master != NULL ) 970 { 971 FT_UInt n; 972 973 974 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) ) 975 goto Exit; 976 FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len ); 977 978 mmvar->axis = 979 (FT_Var_Axis*)&( mmvar[1] ); 980 mmvar->namedstyle = 981 (FT_Var_Named_Style*)&( mmvar->axis[mmvar->num_axis] ); 982 next_coords = 983 (FT_Fixed*)&( mmvar->namedstyle[mmvar->num_namedstyles] ); 984 985 for ( n = 0; n < mmvar->num_namedstyles; n++ ) 986 { 987 mmvar->namedstyle[n].coords = next_coords; 988 next_coords += mmvar->num_axis; 989 } 990 991 a = mmvar->axis; 992 next_name = (FT_String*)next_coords; 993 for ( n = 0; n < mmvar->num_axis; n++ ) 994 { 995 a->name = next_name; 996 997 /* standard PostScript names for some standard apple tags */ 998 if ( a->tag == TTAG_wght ) 999 a->name = (char*)"Weight"; 1000 else if ( a->tag == TTAG_wdth ) 1001 a->name = (char*)"Width"; 1002 else if ( a->tag == TTAG_opsz ) 1003 a->name = (char*)"OpticalSize"; 1004 else if ( a->tag == TTAG_slnt ) 1005 a->name = (char*)"Slant"; 1006 1007 next_name += 5; 1008 a++; 1009 } 1010 1011 *master = mmvar; 1012 } 1013 1014 Exit: 1015 return error; 1016 } 1017 1018 1019 /*************************************************************************/ 1020 /* */ 1021 /* <Function> */ 1022 /* TT_Set_MM_Blend */ 1023 /* */ 1024 /* <Description> */ 1025 /* Set the blend (normalized) coordinates for this instance of the */ 1026 /* font. Check that the `gvar' table is reasonable and does some */ 1027 /* initial preparation. */ 1028 /* */ 1029 /* <InOut> */ 1030 /* face :: The font. */ 1031 /* Initialize the blend structure with `gvar' data. */ 1032 /* */ 1033 /* <Input> */ 1034 /* num_coords :: The number of available coordinates. If it is */ 1035 /* larger than the number of axes, ignore the excess */ 1036 /* values. If it is smaller than the number of axes, */ 1037 /* use the default value (0) for the remaining axes. */ 1038 /* */ 1039 /* coords :: An array of `num_coords', each between [-1,1]. */ 1040 /* */ 1041 /* <Return> */ 1042 /* FreeType error code. 0 means success. */ 1043 /* */ 1044 FT_LOCAL_DEF( FT_Error ) TT_Set_MM_Blend(TT_Face face,FT_UInt num_coords,FT_Fixed * coords)1045 TT_Set_MM_Blend( TT_Face face, 1046 FT_UInt num_coords, 1047 FT_Fixed* coords ) 1048 { 1049 FT_Error error = FT_Err_Ok; 1050 GX_Blend blend; 1051 FT_MM_Var* mmvar; 1052 FT_UInt i; 1053 FT_Memory memory = face->root.memory; 1054 1055 enum 1056 { 1057 mcvt_retain, 1058 mcvt_modify, 1059 mcvt_load 1060 1061 } manageCvt; 1062 1063 1064 face->doblend = FALSE; 1065 1066 if ( face->blend == NULL ) 1067 { 1068 if ( ( error = TT_Get_MM_Var( face, NULL ) ) != 0 ) 1069 goto Exit; 1070 } 1071 1072 blend = face->blend; 1073 mmvar = blend->mmvar; 1074 1075 if ( num_coords > mmvar->num_axis ) 1076 { 1077 FT_TRACE2(( "TT_Set_MM_Blend: only using first %d of %d coordinates\n", 1078 mmvar->num_axis, num_coords )); 1079 num_coords = mmvar->num_axis; 1080 } 1081 1082 FT_TRACE5(( "normalized design coordinates:\n" )); 1083 1084 for ( i = 0; i < num_coords; i++ ) 1085 { 1086 FT_TRACE5(( " %.4f\n", coords[i] / 65536.0 )); 1087 if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L ) 1088 { 1089 FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.4f\n" 1090 " is out of range [-1;1]\n", 1091 coords[i] / 65536.0 )); 1092 error = FT_THROW( Invalid_Argument ); 1093 goto Exit; 1094 } 1095 } 1096 1097 FT_TRACE5(( "\n" )); 1098 1099 if ( blend->glyphoffsets == NULL ) 1100 if ( ( error = ft_var_load_gvar( face ) ) != 0 ) 1101 goto Exit; 1102 1103 if ( blend->normalizedcoords == NULL ) 1104 { 1105 if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) ) 1106 goto Exit; 1107 1108 manageCvt = mcvt_modify; 1109 1110 /* If we have not set the blend coordinates before this, then the */ 1111 /* cvt table will still be what we read from the `cvt ' table and */ 1112 /* we don't need to reload it. We may need to change it though... */ 1113 } 1114 else 1115 { 1116 manageCvt = mcvt_retain; 1117 1118 for ( i = 0; i < num_coords; i++ ) 1119 { 1120 if ( blend->normalizedcoords[i] != coords[i] ) 1121 { 1122 manageCvt = mcvt_load; 1123 break; 1124 } 1125 } 1126 1127 for ( ; i < mmvar->num_axis; i++ ) 1128 { 1129 if ( blend->normalizedcoords[i] != 0 ) 1130 { 1131 manageCvt = mcvt_load; 1132 break; 1133 } 1134 } 1135 1136 /* If we don't change the blend coords then we don't need to do */ 1137 /* anything to the cvt table. It will be correct. Otherwise we */ 1138 /* no longer have the original cvt (it was modified when we set */ 1139 /* the blend last time), so we must reload and then modify it. */ 1140 } 1141 1142 blend->num_axis = mmvar->num_axis; 1143 FT_MEM_COPY( blend->normalizedcoords, 1144 coords, 1145 num_coords * sizeof ( FT_Fixed ) ); 1146 1147 face->doblend = TRUE; 1148 1149 if ( face->cvt != NULL ) 1150 { 1151 switch ( manageCvt ) 1152 { 1153 case mcvt_load: 1154 /* The cvt table has been loaded already; every time we change the */ 1155 /* blend we may need to reload and remodify the cvt table. */ 1156 FT_FREE( face->cvt ); 1157 face->cvt = NULL; 1158 1159 error = tt_face_load_cvt( face, face->root.stream ); 1160 break; 1161 1162 case mcvt_modify: 1163 /* The original cvt table is in memory. All we need to do is */ 1164 /* apply the `cvar' table (if any). */ 1165 error = tt_face_vary_cvt( face, face->root.stream ); 1166 break; 1167 1168 case mcvt_retain: 1169 /* The cvt table is correct for this set of coordinates. */ 1170 break; 1171 } 1172 } 1173 1174 Exit: 1175 return error; 1176 } 1177 1178 1179 /*************************************************************************/ 1180 /* */ 1181 /* <Function> */ 1182 /* TT_Set_Var_Design */ 1183 /* */ 1184 /* <Description> */ 1185 /* Set the coordinates for the instance, measured in the user */ 1186 /* coordinate system. Parse the `avar' table (if present) to convert */ 1187 /* from user to normalized coordinates. */ 1188 /* */ 1189 /* <InOut> */ 1190 /* face :: The font face. */ 1191 /* Initialize the blend struct with `gvar' data. */ 1192 /* */ 1193 /* <Input> */ 1194 /* num_coords :: The number of available coordinates. If it is */ 1195 /* larger than the number of axes, ignore the excess */ 1196 /* values. If it is smaller than the number of axes, */ 1197 /* use the default values for the remaining axes. */ 1198 /* */ 1199 /* coords :: A coordinate array with `num_coords' elements. */ 1200 /* */ 1201 /* <Return> */ 1202 /* FreeType error code. 0 means success. */ 1203 /* */ 1204 FT_LOCAL_DEF( FT_Error ) TT_Set_Var_Design(TT_Face face,FT_UInt num_coords,FT_Fixed * coords)1205 TT_Set_Var_Design( TT_Face face, 1206 FT_UInt num_coords, 1207 FT_Fixed* coords ) 1208 { 1209 FT_Error error = FT_Err_Ok; 1210 FT_Fixed* normalized = NULL; 1211 GX_Blend blend; 1212 FT_MM_Var* mmvar; 1213 FT_UInt i, j; 1214 FT_Var_Axis* a; 1215 GX_AVarSegment av; 1216 FT_Memory memory = face->root.memory; 1217 1218 1219 if ( face->blend == NULL ) 1220 { 1221 if ( ( error = TT_Get_MM_Var( face, NULL ) ) != 0 ) 1222 goto Exit; 1223 } 1224 1225 blend = face->blend; 1226 mmvar = blend->mmvar; 1227 1228 if ( num_coords > mmvar->num_axis ) 1229 { 1230 FT_TRACE2(( "TT_Set_Var_Design:" 1231 " only using first %d of %d coordinates\n", 1232 mmvar->num_axis, num_coords )); 1233 num_coords = mmvar->num_axis; 1234 } 1235 1236 /* Axis normalization is a two stage process. First we normalize */ 1237 /* based on the [min,def,max] values for the axis to be [-1,0,1]. */ 1238 /* Then, if there's an `avar' table, we renormalize this range. */ 1239 1240 if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) ) 1241 goto Exit; 1242 1243 FT_TRACE5(( "design coordinates:\n" )); 1244 1245 a = mmvar->axis; 1246 for ( i = 0; i < num_coords; i++, a++ ) 1247 { 1248 FT_TRACE5(( " %.4f\n", coords[i] / 65536.0 )); 1249 if ( coords[i] > a->maximum || coords[i] < a->minimum ) 1250 { 1251 FT_TRACE1(( "TT_Set_Var_Design: normalized design coordinate %.4f\n" 1252 " is out of range [%.4f;%.4f]\n", 1253 coords[i] / 65536.0, 1254 a->minimum / 65536.0, 1255 a->maximum / 65536.0 )); 1256 error = FT_THROW( Invalid_Argument ); 1257 goto Exit; 1258 } 1259 1260 if ( coords[i] < a->def ) 1261 normalized[i] = -FT_DivFix( coords[i] - a->def, 1262 a->minimum - a->def ); 1263 else if ( a->maximum == a->def ) 1264 normalized[i] = 0; 1265 else 1266 normalized[i] = FT_DivFix( coords[i] - a->def, 1267 a->maximum - a->def ); 1268 } 1269 1270 FT_TRACE5(( "\n" )); 1271 1272 for ( ; i < mmvar->num_axis; i++ ) 1273 normalized[i] = 0; 1274 1275 if ( !blend->avar_checked ) 1276 ft_var_load_avar( face ); 1277 1278 if ( blend->avar_segment != NULL ) 1279 { 1280 FT_TRACE5(( "normalized design coordinates" 1281 " before applying `avar' data:\n" )); 1282 1283 av = blend->avar_segment; 1284 for ( i = 0; i < mmvar->num_axis; i++, av++ ) 1285 { 1286 for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) 1287 { 1288 FT_TRACE5(( " %.4f\n", normalized[i] / 65536.0 )); 1289 if ( normalized[i] < av->correspondence[j].fromCoord ) 1290 { 1291 normalized[i] = 1292 FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord, 1293 av->correspondence[j].toCoord - 1294 av->correspondence[j - 1].toCoord, 1295 av->correspondence[j].fromCoord - 1296 av->correspondence[j - 1].fromCoord ) + 1297 av->correspondence[j - 1].toCoord; 1298 break; 1299 } 1300 } 1301 } 1302 } 1303 1304 error = TT_Set_MM_Blend( face, mmvar->num_axis, normalized ); 1305 1306 Exit: 1307 FT_FREE( normalized ); 1308 return error; 1309 } 1310 1311 1312 /*************************************************************************/ 1313 /*************************************************************************/ 1314 /***** *****/ 1315 /***** GX VAR PARSING ROUTINES *****/ 1316 /***** *****/ 1317 /*************************************************************************/ 1318 /*************************************************************************/ 1319 1320 1321 /*************************************************************************/ 1322 /* */ 1323 /* <Function> */ 1324 /* tt_face_vary_cvt */ 1325 /* */ 1326 /* <Description> */ 1327 /* Modify the loaded cvt table according to the `cvar' table and the */ 1328 /* font's blend. */ 1329 /* */ 1330 /* <InOut> */ 1331 /* face :: A handle to the target face object. */ 1332 /* */ 1333 /* <Input> */ 1334 /* stream :: A handle to the input stream. */ 1335 /* */ 1336 /* <Return> */ 1337 /* FreeType error code. 0 means success. */ 1338 /* */ 1339 /* Most errors are ignored. It is perfectly valid not to have a */ 1340 /* `cvar' table even if there is a `gvar' and `fvar' table. */ 1341 /* */ 1342 FT_LOCAL_DEF( FT_Error ) tt_face_vary_cvt(TT_Face face,FT_Stream stream)1343 tt_face_vary_cvt( TT_Face face, 1344 FT_Stream stream ) 1345 { 1346 FT_Error error; 1347 FT_Memory memory = stream->memory; 1348 FT_ULong table_start; 1349 FT_ULong table_len; 1350 FT_UInt tupleCount; 1351 FT_ULong offsetToData; 1352 FT_ULong here; 1353 FT_UInt i, j; 1354 FT_Fixed* tuple_coords = NULL; 1355 FT_Fixed* im_start_coords = NULL; 1356 FT_Fixed* im_end_coords = NULL; 1357 GX_Blend blend = face->blend; 1358 FT_UInt point_count; 1359 FT_UShort* localpoints; 1360 FT_Short* deltas; 1361 1362 1363 FT_TRACE2(( "CVAR " )); 1364 1365 if ( blend == NULL ) 1366 { 1367 FT_TRACE2(( "\n" 1368 "tt_face_vary_cvt: no blend specified\n" )); 1369 error = FT_Err_Ok; 1370 goto Exit; 1371 } 1372 1373 if ( face->cvt == NULL ) 1374 { 1375 FT_TRACE2(( "\n" 1376 "tt_face_vary_cvt: no `cvt ' table\n" )); 1377 error = FT_Err_Ok; 1378 goto Exit; 1379 } 1380 1381 error = face->goto_table( face, TTAG_cvar, stream, &table_len ); 1382 if ( error ) 1383 { 1384 FT_TRACE2(( "is missing\n" )); 1385 1386 error = FT_Err_Ok; 1387 goto Exit; 1388 } 1389 1390 if ( FT_FRAME_ENTER( table_len ) ) 1391 { 1392 error = FT_Err_Ok; 1393 goto Exit; 1394 } 1395 1396 table_start = FT_Stream_FTell( stream ); 1397 if ( FT_GET_LONG() != 0x00010000L ) 1398 { 1399 FT_TRACE2(( "bad table version\n" )); 1400 1401 error = FT_Err_Ok; 1402 goto FExit; 1403 } 1404 1405 FT_TRACE2(( "loaded\n" )); 1406 1407 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || 1408 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || 1409 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) 1410 goto FExit; 1411 1412 tupleCount = FT_GET_USHORT(); 1413 offsetToData = FT_GET_USHORT(); 1414 1415 /* rough sanity test */ 1416 if ( offsetToData + tupleCount * 4 > table_len ) 1417 { 1418 FT_TRACE2(( "tt_face_vary_cvt:" 1419 " invalid CVT variation array header\n" )); 1420 1421 error = FT_THROW( Invalid_Table ); 1422 goto FExit; 1423 } 1424 1425 offsetToData += table_start; 1426 1427 /* The documentation implies there are flags packed into */ 1428 /* `tupleCount', but John Jenkins says that shared points don't apply */ 1429 /* to `cvar', and no other flags are defined. */ 1430 1431 FT_TRACE5(( "cvar: there are %d tuples:\n", tupleCount & 0xFFF )); 1432 1433 for ( i = 0; i < ( tupleCount & 0xFFF ); i++ ) 1434 { 1435 FT_UInt tupleDataSize; 1436 FT_UInt tupleIndex; 1437 FT_Fixed apply; 1438 1439 1440 FT_TRACE6(( " tuple %d:\n", i )); 1441 1442 tupleDataSize = FT_GET_USHORT(); 1443 tupleIndex = FT_GET_USHORT(); 1444 1445 /* There is no provision here for a global tuple coordinate section, */ 1446 /* so John says. There are no tuple indices, just embedded tuples. */ 1447 1448 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 1449 { 1450 for ( j = 0; j < blend->num_axis; j++ ) 1451 tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */ 1452 /* short frac to fixed */ 1453 } 1454 else 1455 { 1456 /* skip this tuple; it makes no sense */ 1457 1458 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 1459 for ( j = 0; j < 2 * blend->num_axis; j++ ) 1460 (void)FT_GET_SHORT(); 1461 1462 offsetToData += tupleDataSize; 1463 continue; 1464 } 1465 1466 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 1467 { 1468 for ( j = 0; j < blend->num_axis; j++ ) 1469 im_start_coords[j] = FT_GET_SHORT() * 4; 1470 for ( j = 0; j < blend->num_axis; j++ ) 1471 im_end_coords[j] = FT_GET_SHORT() * 4; 1472 } 1473 1474 apply = ft_var_apply_tuple( blend, 1475 (FT_UShort)tupleIndex, 1476 tuple_coords, 1477 im_start_coords, 1478 im_end_coords ); 1479 if ( /* tuple isn't active for our blend */ 1480 apply == 0 || 1481 /* global points not allowed, */ 1482 /* if they aren't local, makes no sense */ 1483 !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) ) 1484 { 1485 offsetToData += tupleDataSize; 1486 continue; 1487 } 1488 1489 here = FT_Stream_FTell( stream ); 1490 1491 FT_Stream_SeekSet( stream, offsetToData ); 1492 1493 localpoints = ft_var_readpackedpoints( stream, 1494 table_len, 1495 &point_count ); 1496 deltas = ft_var_readpackeddeltas( stream, 1497 table_len, 1498 point_count == 0 ? face->cvt_size 1499 : point_count ); 1500 if ( localpoints == NULL || deltas == NULL ) 1501 ; /* failure, ignore it */ 1502 1503 else if ( localpoints == ALL_POINTS ) 1504 { 1505 #ifdef FT_DEBUG_LEVEL_TRACE 1506 int count = 0; 1507 #endif 1508 1509 1510 FT_TRACE7(( " CVT deltas:\n" )); 1511 1512 /* this means that there are deltas for every entry in cvt */ 1513 for ( j = 0; j < face->cvt_size; j++ ) 1514 { 1515 FT_Long orig_cvt = face->cvt[j]; 1516 1517 1518 face->cvt[j] = (FT_Short)( orig_cvt + 1519 FT_MulFix( deltas[j], apply ) ); 1520 1521 #ifdef FT_DEBUG_LEVEL_TRACE 1522 if ( orig_cvt != face->cvt[j] ) 1523 { 1524 FT_TRACE7(( " %d: %d -> %d\n", 1525 j, orig_cvt, face->cvt[j] )); 1526 count++; 1527 } 1528 #endif 1529 } 1530 1531 #ifdef FT_DEBUG_LEVEL_TRACE 1532 if ( !count ) 1533 FT_TRACE7(( " none\n" )); 1534 #endif 1535 } 1536 1537 else 1538 { 1539 #ifdef FT_DEBUG_LEVEL_TRACE 1540 int count = 0; 1541 #endif 1542 1543 1544 FT_TRACE7(( " CVT deltas:\n" )); 1545 1546 for ( j = 0; j < point_count; j++ ) 1547 { 1548 int pindex = localpoints[j]; 1549 FT_Long orig_cvt = face->cvt[pindex]; 1550 1551 1552 face->cvt[pindex] = (FT_Short)( orig_cvt + 1553 FT_MulFix( deltas[j], apply ) ); 1554 1555 #ifdef FT_DEBUG_LEVEL_TRACE 1556 if ( orig_cvt != face->cvt[pindex] ) 1557 { 1558 FT_TRACE7(( " %d: %d -> %d\n", 1559 pindex, orig_cvt, face->cvt[pindex] )); 1560 count++; 1561 } 1562 #endif 1563 } 1564 1565 #ifdef FT_DEBUG_LEVEL_TRACE 1566 if ( !count ) 1567 FT_TRACE7(( " none\n" )); 1568 #endif 1569 } 1570 1571 if ( localpoints != ALL_POINTS ) 1572 FT_FREE( localpoints ); 1573 FT_FREE( deltas ); 1574 1575 offsetToData += tupleDataSize; 1576 1577 FT_Stream_SeekSet( stream, here ); 1578 } 1579 1580 FT_TRACE5(( "\n" )); 1581 1582 FExit: 1583 FT_FRAME_EXIT(); 1584 1585 Exit: 1586 FT_FREE( tuple_coords ); 1587 FT_FREE( im_start_coords ); 1588 FT_FREE( im_end_coords ); 1589 1590 return error; 1591 } 1592 1593 1594 /* Shift the original coordinates of all points between indices `p1' */ 1595 /* and `p2', using the same difference as given by index `ref'. */ 1596 1597 /* modeled after `af_iup_shift' */ 1598 1599 static void tt_delta_shift(int p1,int p2,int ref,FT_Vector * in_points,FT_Vector * out_points)1600 tt_delta_shift( int p1, 1601 int p2, 1602 int ref, 1603 FT_Vector* in_points, 1604 FT_Vector* out_points ) 1605 { 1606 int p; 1607 FT_Vector delta; 1608 1609 1610 delta.x = out_points[ref].x - in_points[ref].x; 1611 delta.y = out_points[ref].y - in_points[ref].y; 1612 1613 if ( delta.x == 0 && delta.y == 0 ) 1614 return; 1615 1616 for ( p = p1; p < ref; p++ ) 1617 { 1618 out_points[p].x += delta.x; 1619 out_points[p].y += delta.y; 1620 } 1621 1622 for ( p = ref + 1; p <= p2; p++ ) 1623 { 1624 out_points[p].x += delta.x; 1625 out_points[p].y += delta.y; 1626 } 1627 } 1628 1629 1630 /* Interpolate the original coordinates of all points with indices */ 1631 /* between `p1' and `p2', using `ref1' and `ref2' as the reference */ 1632 /* point indices. */ 1633 1634 /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */ 1635 /* `Ins_IUP' */ 1636 1637 static void tt_delta_interpolate(int p1,int p2,int ref1,int ref2,FT_Vector * in_points,FT_Vector * out_points)1638 tt_delta_interpolate( int p1, 1639 int p2, 1640 int ref1, 1641 int ref2, 1642 FT_Vector* in_points, 1643 FT_Vector* out_points ) 1644 { 1645 int p, i; 1646 1647 FT_Pos out, in1, in2, out1, out2, d1, d2; 1648 1649 1650 if ( p1 > p2 ) 1651 return; 1652 1653 /* handle both horizontal and vertical coordinates */ 1654 for ( i = 0; i <= 1; i++ ) 1655 { 1656 /* shift array pointers so that we can access `foo.y' as `foo.x' */ 1657 in_points = (FT_Vector*)( (FT_Pos*)in_points + i ); 1658 out_points = (FT_Vector*)( (FT_Pos*)out_points + i ); 1659 1660 if ( in_points[ref1].x > in_points[ref2].x ) 1661 { 1662 p = ref1; 1663 ref1 = ref2; 1664 ref2 = p; 1665 } 1666 1667 in1 = in_points[ref1].x; 1668 in2 = in_points[ref2].x; 1669 out1 = out_points[ref1].x; 1670 out2 = out_points[ref2].x; 1671 d1 = out1 - in1; 1672 d2 = out2 - in2; 1673 1674 if ( out1 == out2 || in1 == in2 ) 1675 { 1676 for ( p = p1; p <= p2; p++ ) 1677 { 1678 out = in_points[p].x; 1679 1680 if ( out <= in1 ) 1681 out += d1; 1682 else if ( out >= in2 ) 1683 out += d2; 1684 else 1685 out = out1; 1686 1687 out_points[p].x = out; 1688 } 1689 } 1690 else 1691 { 1692 FT_Fixed scale = FT_DivFix( out2 - out1, in2 - in1 ); 1693 1694 1695 for ( p = p1; p <= p2; p++ ) 1696 { 1697 out = in_points[p].x; 1698 1699 if ( out <= in1 ) 1700 out += d1; 1701 else if ( out >= in2 ) 1702 out += d2; 1703 else 1704 out = out1 + FT_MulFix( out - in1, scale ); 1705 1706 out_points[p].x = out; 1707 } 1708 } 1709 } 1710 } 1711 1712 1713 /* Interpolate points without delta values, similar to */ 1714 /* the `IUP' hinting instruction. */ 1715 1716 /* modeled after `Ins_IUP */ 1717 1718 static void tt_handle_deltas(FT_Outline * outline,FT_Vector * in_points,FT_Bool * has_delta)1719 tt_handle_deltas( FT_Outline* outline, 1720 FT_Vector* in_points, 1721 FT_Bool* has_delta ) 1722 { 1723 FT_Vector* out_points; 1724 1725 FT_Int first_point; 1726 FT_Int end_point; 1727 1728 FT_Int first_delta; 1729 FT_Int cur_delta; 1730 1731 FT_Int point; 1732 FT_Short contour; 1733 1734 1735 /* ignore empty outlines */ 1736 if ( !outline->n_contours ) 1737 return; 1738 1739 out_points = outline->points; 1740 1741 contour = 0; 1742 point = 0; 1743 1744 do 1745 { 1746 end_point = outline->contours[contour]; 1747 first_point = point; 1748 1749 /* search first point that has a delta */ 1750 while ( point <= end_point && !has_delta[point] ) 1751 point++; 1752 1753 if ( point <= end_point ) 1754 { 1755 first_delta = point; 1756 cur_delta = point; 1757 1758 point++; 1759 1760 while ( point <= end_point ) 1761 { 1762 /* search next point that has a delta */ 1763 /* and interpolate intermediate points */ 1764 if ( has_delta[point] ) 1765 { 1766 tt_delta_interpolate( cur_delta + 1, 1767 point - 1, 1768 cur_delta, 1769 point, 1770 in_points, 1771 out_points ); 1772 cur_delta = point; 1773 } 1774 1775 point++; 1776 } 1777 1778 /* shift contour if we only have a single delta */ 1779 if ( cur_delta == first_delta ) 1780 tt_delta_shift( first_point, 1781 end_point, 1782 cur_delta, 1783 in_points, 1784 out_points ); 1785 else 1786 { 1787 /* otherwise handle remaining points */ 1788 /* at the end and beginning of the contour */ 1789 tt_delta_interpolate( cur_delta + 1, 1790 end_point, 1791 cur_delta, 1792 first_delta, 1793 in_points, 1794 out_points ); 1795 1796 if ( first_delta > 0 ) 1797 tt_delta_interpolate( first_point, 1798 first_delta - 1, 1799 cur_delta, 1800 first_delta, 1801 in_points, 1802 out_points ); 1803 } 1804 } 1805 contour++; 1806 1807 } while ( contour < outline->n_contours ); 1808 } 1809 1810 1811 /*************************************************************************/ 1812 /* */ 1813 /* <Function> */ 1814 /* TT_Vary_Apply_Glyph_Deltas */ 1815 /* */ 1816 /* <Description> */ 1817 /* Apply the appropriate deltas to the current glyph. */ 1818 /* */ 1819 /* <Input> */ 1820 /* face :: A handle to the target face object. */ 1821 /* */ 1822 /* glyph_index :: The index of the glyph being modified. */ 1823 /* */ 1824 /* n_points :: The number of the points in the glyph, including */ 1825 /* phantom points. */ 1826 /* */ 1827 /* <InOut> */ 1828 /* outline :: The outline to change. */ 1829 /* */ 1830 /* <Return> */ 1831 /* FreeType error code. 0 means success. */ 1832 /* */ 1833 FT_LOCAL_DEF( FT_Error ) TT_Vary_Apply_Glyph_Deltas(TT_Face face,FT_UInt glyph_index,FT_Outline * outline,FT_UInt n_points)1834 TT_Vary_Apply_Glyph_Deltas( TT_Face face, 1835 FT_UInt glyph_index, 1836 FT_Outline* outline, 1837 FT_UInt n_points ) 1838 { 1839 FT_Stream stream = face->root.stream; 1840 FT_Memory memory = stream->memory; 1841 GX_Blend blend = face->blend; 1842 1843 FT_Vector* points_org = NULL; 1844 FT_Bool* has_delta = NULL; 1845 1846 FT_Error error; 1847 FT_ULong glyph_start; 1848 FT_UInt tupleCount; 1849 FT_ULong offsetToData; 1850 FT_ULong here; 1851 FT_UInt i, j; 1852 FT_Fixed* tuple_coords = NULL; 1853 FT_Fixed* im_start_coords = NULL; 1854 FT_Fixed* im_end_coords = NULL; 1855 FT_UInt point_count, spoint_count = 0; 1856 FT_UShort* sharedpoints = NULL; 1857 FT_UShort* localpoints = NULL; 1858 FT_UShort* points; 1859 FT_Short *deltas_x, *deltas_y; 1860 1861 1862 if ( !face->doblend || blend == NULL ) 1863 return FT_THROW( Invalid_Argument ); 1864 1865 if ( glyph_index >= blend->gv_glyphcnt || 1866 blend->glyphoffsets[glyph_index] == 1867 blend->glyphoffsets[glyph_index + 1] ) 1868 { 1869 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 1870 " no variation data for this glyph\n" )); 1871 return FT_Err_Ok; 1872 } 1873 1874 if ( FT_NEW_ARRAY( points_org, n_points ) || 1875 FT_NEW_ARRAY( has_delta, n_points ) ) 1876 goto Fail1; 1877 1878 if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) || 1879 FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] - 1880 blend->glyphoffsets[glyph_index] ) ) 1881 goto Fail1; 1882 1883 glyph_start = FT_Stream_FTell( stream ); 1884 1885 /* each set of glyph variation data is formatted similarly to `cvar' */ 1886 /* (except we get shared points and global tuples) */ 1887 1888 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || 1889 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || 1890 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) 1891 goto Fail2; 1892 1893 tupleCount = FT_GET_USHORT(); 1894 offsetToData = FT_GET_USHORT(); 1895 1896 /* rough sanity test */ 1897 if ( offsetToData + tupleCount * 4 > blend->gvar_size ) 1898 { 1899 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 1900 " invalid glyph variation array header\n" )); 1901 1902 error = FT_THROW( Invalid_Table ); 1903 goto Fail2; 1904 } 1905 1906 offsetToData += glyph_start; 1907 1908 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS ) 1909 { 1910 here = FT_Stream_FTell( stream ); 1911 1912 FT_Stream_SeekSet( stream, offsetToData ); 1913 1914 sharedpoints = ft_var_readpackedpoints( stream, 1915 blend->gvar_size, 1916 &spoint_count ); 1917 offsetToData = FT_Stream_FTell( stream ); 1918 1919 FT_Stream_SeekSet( stream, here ); 1920 } 1921 1922 FT_TRACE5(( "gvar: there are %d tuples:\n", 1923 tupleCount & GX_TC_TUPLE_COUNT_MASK )); 1924 1925 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ ) 1926 { 1927 FT_UInt tupleDataSize; 1928 FT_UInt tupleIndex; 1929 FT_Fixed apply; 1930 1931 1932 FT_TRACE6(( " tuple %d:\n", i )); 1933 1934 tupleDataSize = FT_GET_USHORT(); 1935 tupleIndex = FT_GET_USHORT(); 1936 1937 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 1938 { 1939 for ( j = 0; j < blend->num_axis; j++ ) 1940 tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */ 1941 /* short frac to fixed */ 1942 } 1943 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount ) 1944 { 1945 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 1946 " invalid tuple index\n" )); 1947 1948 error = FT_THROW( Invalid_Table ); 1949 goto Fail2; 1950 } 1951 else 1952 FT_MEM_COPY( 1953 tuple_coords, 1954 &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis], 1955 blend->num_axis * sizeof ( FT_Fixed ) ); 1956 1957 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 1958 { 1959 for ( j = 0; j < blend->num_axis; j++ ) 1960 im_start_coords[j] = FT_GET_SHORT() * 4; 1961 for ( j = 0; j < blend->num_axis; j++ ) 1962 im_end_coords[j] = FT_GET_SHORT() * 4; 1963 } 1964 1965 apply = ft_var_apply_tuple( blend, 1966 (FT_UShort)tupleIndex, 1967 tuple_coords, 1968 im_start_coords, 1969 im_end_coords ); 1970 1971 if ( apply == 0 ) /* tuple isn't active for our blend */ 1972 { 1973 offsetToData += tupleDataSize; 1974 continue; 1975 } 1976 1977 here = FT_Stream_FTell( stream ); 1978 1979 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) 1980 { 1981 FT_Stream_SeekSet( stream, offsetToData ); 1982 1983 localpoints = ft_var_readpackedpoints( stream, 1984 blend->gvar_size, 1985 &point_count ); 1986 points = localpoints; 1987 } 1988 else 1989 { 1990 points = sharedpoints; 1991 point_count = spoint_count; 1992 } 1993 1994 deltas_x = ft_var_readpackeddeltas( stream, 1995 blend->gvar_size, 1996 point_count == 0 ? n_points 1997 : point_count ); 1998 deltas_y = ft_var_readpackeddeltas( stream, 1999 blend->gvar_size, 2000 point_count == 0 ? n_points 2001 : point_count ); 2002 2003 if ( points == NULL || deltas_y == NULL || deltas_x == NULL ) 2004 ; /* failure, ignore it */ 2005 2006 else if ( points == ALL_POINTS ) 2007 { 2008 #ifdef FT_DEBUG_LEVEL_TRACE 2009 int count = 0; 2010 #endif 2011 2012 2013 FT_TRACE7(( " point deltas:\n" )); 2014 2015 /* this means that there are deltas for every point in the glyph */ 2016 for ( j = 0; j < n_points; j++ ) 2017 { 2018 #ifdef FT_DEBUG_LEVEL_TRACE 2019 FT_Vector point_org = outline->points[j]; 2020 #endif 2021 2022 2023 outline->points[j].x += FT_MulFix( deltas_x[j], apply ); 2024 outline->points[j].y += FT_MulFix( deltas_y[j], apply ); 2025 2026 #ifdef FT_DEBUG_LEVEL_TRACE 2027 if ( ( point_org.x != outline->points[j].x ) || 2028 ( point_org.y != outline->points[j].y ) ) 2029 { 2030 FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n", 2031 j, 2032 point_org.x, 2033 point_org.y, 2034 outline->points[j].x, 2035 outline->points[j].y )); 2036 count++; 2037 } 2038 #endif 2039 } 2040 2041 #ifdef FT_DEBUG_LEVEL_TRACE 2042 if ( !count ) 2043 FT_TRACE7(( " none\n" )); 2044 #endif 2045 } 2046 2047 else if ( localpoints == NULL ) 2048 ; /* failure, ignore it */ 2049 2050 else 2051 { 2052 #ifdef FT_DEBUG_LEVEL_TRACE 2053 int count = 0; 2054 #endif 2055 2056 2057 /* we have to interpolate the missing deltas similar to the */ 2058 /* IUP bytecode instruction */ 2059 for ( j = 0; j < n_points; j++ ) 2060 { 2061 points_org[j] = outline->points[j]; 2062 has_delta[j] = FALSE; 2063 } 2064 2065 for ( j = 0; j < point_count; j++ ) 2066 { 2067 FT_UShort idx = localpoints[j]; 2068 2069 2070 if ( idx >= n_points ) 2071 continue; 2072 2073 has_delta[idx] = TRUE; 2074 2075 outline->points[idx].x += FT_MulFix( deltas_x[j], apply ); 2076 outline->points[idx].y += FT_MulFix( deltas_y[j], apply ); 2077 } 2078 2079 /* no need to handle phantom points here, */ 2080 /* since solitary points can't be interpolated */ 2081 tt_handle_deltas( outline, 2082 points_org, 2083 has_delta ); 2084 2085 #ifdef FT_DEBUG_LEVEL_TRACE 2086 FT_TRACE7(( " point deltas:\n" )); 2087 2088 for ( j = 0; j < n_points; j++) 2089 { 2090 if ( ( points_org[j].x != outline->points[j].x ) || 2091 ( points_org[j].y != outline->points[j].y ) ) 2092 { 2093 FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n", 2094 j, 2095 points_org[j].x, 2096 points_org[j].y, 2097 outline->points[j].x, 2098 outline->points[j].y )); 2099 count++; 2100 } 2101 } 2102 2103 if ( !count ) 2104 FT_TRACE7(( " none\n" )); 2105 #endif 2106 } 2107 2108 if ( localpoints != ALL_POINTS ) 2109 FT_FREE( localpoints ); 2110 FT_FREE( deltas_x ); 2111 FT_FREE( deltas_y ); 2112 2113 offsetToData += tupleDataSize; 2114 2115 FT_Stream_SeekSet( stream, here ); 2116 } 2117 2118 FT_TRACE5(( "\n" )); 2119 2120 Fail2: 2121 if ( sharedpoints != ALL_POINTS ) 2122 FT_FREE( sharedpoints ); 2123 FT_FREE( tuple_coords ); 2124 FT_FREE( im_start_coords ); 2125 FT_FREE( im_end_coords ); 2126 2127 FT_FRAME_EXIT(); 2128 2129 Fail1: 2130 FT_FREE( points_org ); 2131 FT_FREE( has_delta ); 2132 2133 return error; 2134 } 2135 2136 2137 /*************************************************************************/ 2138 /* */ 2139 /* <Function> */ 2140 /* tt_done_blend */ 2141 /* */ 2142 /* <Description> */ 2143 /* Free the blend internal data structure. */ 2144 /* */ 2145 FT_LOCAL_DEF( void ) tt_done_blend(FT_Memory memory,GX_Blend blend)2146 tt_done_blend( FT_Memory memory, 2147 GX_Blend blend ) 2148 { 2149 if ( blend != NULL ) 2150 { 2151 FT_UInt i; 2152 2153 2154 FT_FREE( blend->normalizedcoords ); 2155 FT_FREE( blend->mmvar ); 2156 2157 if ( blend->avar_segment != NULL ) 2158 { 2159 for ( i = 0; i < blend->num_axis; i++ ) 2160 FT_FREE( blend->avar_segment[i].correspondence ); 2161 FT_FREE( blend->avar_segment ); 2162 } 2163 2164 FT_FREE( blend->tuplecoords ); 2165 FT_FREE( blend->glyphoffsets ); 2166 FT_FREE( blend ); 2167 } 2168 } 2169 2170 #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */ 2171 2172 2173 /* END */ 2174