1 /***************************************************************************/ 2 /* */ 3 /* ttgxvar.c */ 4 /* */ 5 /* TrueType GX Font Variation loader */ 6 /* */ 7 /* Copyright 2004-2015 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* 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* 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 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 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 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 617 /* It's not clear why (for intermediate tuples) we don't need */ 618 /* to check against start/end -- the documentation says we don't. */ 619 /* Similarly, it's unclear why we don't need to scale along the */ 620 /* axis. */ 621 622 if ( tuple_coords[i] == 0 ) 623 { 624 FT_TRACE6(( " tuple coordinate is zero, ignored\n", i )); 625 continue; 626 } 627 628 else if ( blend->normalizedcoords[i] == 0 ) 629 { 630 FT_TRACE6(( " axis coordinate is zero, stop\n" )); 631 apply = 0; 632 break; 633 } 634 635 else if ( ( blend->normalizedcoords[i] < 0 && tuple_coords[i] > 0 ) || 636 ( blend->normalizedcoords[i] > 0 && tuple_coords[i] < 0 ) ) 637 { 638 FT_TRACE6(( " tuple coordinate value %.4f is exceeded, stop\n", 639 tuple_coords[i] / 65536.0 )); 640 apply = 0; 641 break; 642 } 643 644 else if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) ) 645 { 646 FT_TRACE6(( " tuple coordinate value %.4f fits\n", 647 tuple_coords[i] / 65536.0 )); 648 /* not an intermediate tuple */ 649 apply = FT_MulFix( apply, 650 blend->normalizedcoords[i] > 0 651 ? blend->normalizedcoords[i] 652 : -blend->normalizedcoords[i] ); 653 } 654 655 else if ( blend->normalizedcoords[i] < im_start_coords[i] || 656 blend->normalizedcoords[i] > im_end_coords[i] ) 657 { 658 FT_TRACE6(( " intermediate tuple range [%.4f;%.4f] is exceeded," 659 " stop\n", 660 im_start_coords[i] / 65536.0, 661 im_end_coords[i] / 65536.0 )); 662 apply = 0; 663 break; 664 } 665 666 else if ( blend->normalizedcoords[i] < tuple_coords[i] ) 667 { 668 FT_TRACE6(( " intermediate tuple range [%.4f;%.4f] fits\n", 669 im_start_coords[i] / 65536.0, 670 im_end_coords[i] / 65536.0 )); 671 apply = FT_MulDiv( apply, 672 blend->normalizedcoords[i] - im_start_coords[i], 673 tuple_coords[i] - im_start_coords[i] ); 674 } 675 676 else 677 { 678 FT_TRACE6(( " intermediate tuple range [%.4f;%.4f] fits\n", 679 im_start_coords[i] / 65536.0, 680 im_end_coords[i] / 65536.0 )); 681 apply = FT_MulDiv( apply, 682 im_end_coords[i] - blend->normalizedcoords[i], 683 im_end_coords[i] - tuple_coords[i] ); 684 } 685 } 686 687 FT_TRACE6(( " apply factor is %.4f\n", apply / 65536.0 )); 688 689 return apply; 690 } 691 692 693 /*************************************************************************/ 694 /*************************************************************************/ 695 /***** *****/ 696 /***** MULTIPLE MASTERS SERVICE FUNCTIONS *****/ 697 /***** *****/ 698 /*************************************************************************/ 699 /*************************************************************************/ 700 701 702 typedef struct GX_FVar_Head_ 703 { 704 FT_Long version; 705 FT_UShort offsetToData; 706 FT_UShort countSizePairs; 707 FT_UShort axisCount; 708 FT_UShort axisSize; 709 FT_UShort instanceCount; 710 FT_UShort instanceSize; 711 712 } GX_FVar_Head; 713 714 715 typedef struct fvar_axis_ 716 { 717 FT_ULong axisTag; 718 FT_Fixed minValue; 719 FT_Fixed defaultValue; 720 FT_Fixed maxValue; 721 FT_UShort flags; 722 FT_UShort nameID; 723 724 } GX_FVar_Axis; 725 726 727 /*************************************************************************/ 728 /* */ 729 /* <Function> */ 730 /* TT_Get_MM_Var */ 731 /* */ 732 /* <Description> */ 733 /* Check that the font's `fvar' table is valid, parse it, and return */ 734 /* those data. */ 735 /* */ 736 /* <InOut> */ 737 /* face :: The font face. */ 738 /* TT_Get_MM_Var initializes the blend structure. */ 739 /* */ 740 /* <Output> */ 741 /* master :: The `fvar' data (must be freed by caller). Can be NULL, */ 742 /* which makes this function simply load MM support. */ 743 /* */ 744 /* <Return> */ 745 /* FreeType error code. 0 means success. */ 746 /* */ 747 FT_LOCAL_DEF( FT_Error ) 748 TT_Get_MM_Var( TT_Face face, 749 FT_MM_Var* *master ) 750 { 751 FT_Stream stream = face->root.stream; 752 FT_Memory memory = face->root.memory; 753 FT_ULong table_len; 754 FT_Error error = FT_Err_Ok; 755 FT_ULong fvar_start; 756 FT_Int i, j; 757 FT_MM_Var* mmvar = NULL; 758 FT_Fixed* next_coords; 759 FT_String* next_name; 760 FT_Var_Axis* a; 761 FT_Var_Named_Style* ns; 762 GX_FVar_Head fvar_head; 763 764 static const FT_Frame_Field fvar_fields[] = 765 { 766 767 #undef FT_STRUCTURE 768 #define FT_STRUCTURE GX_FVar_Head 769 770 FT_FRAME_START( 16 ), 771 FT_FRAME_LONG ( version ), 772 FT_FRAME_USHORT( offsetToData ), 773 FT_FRAME_USHORT( countSizePairs ), 774 FT_FRAME_USHORT( axisCount ), 775 FT_FRAME_USHORT( axisSize ), 776 FT_FRAME_USHORT( instanceCount ), 777 FT_FRAME_USHORT( instanceSize ), 778 FT_FRAME_END 779 }; 780 781 static const FT_Frame_Field fvaraxis_fields[] = 782 { 783 784 #undef FT_STRUCTURE 785 #define FT_STRUCTURE GX_FVar_Axis 786 787 FT_FRAME_START( 20 ), 788 FT_FRAME_ULONG ( axisTag ), 789 FT_FRAME_LONG ( minValue ), 790 FT_FRAME_LONG ( defaultValue ), 791 FT_FRAME_LONG ( maxValue ), 792 FT_FRAME_USHORT( flags ), 793 FT_FRAME_USHORT( nameID ), 794 FT_FRAME_END 795 }; 796 797 798 /* read the font data and set up the internal representation */ 799 /* if not already done */ 800 801 if ( face->blend == NULL ) 802 { 803 FT_TRACE2(( "FVAR " )); 804 805 /* both `fvar' and `gvar' must be present */ 806 if ( ( error = face->goto_table( face, TTAG_gvar, 807 stream, &table_len ) ) != 0 ) 808 { 809 FT_TRACE1(( "\n" 810 "TT_Get_MM_Var: `gvar' table is missing\n" )); 811 goto Exit; 812 } 813 814 if ( ( error = face->goto_table( face, TTAG_fvar, 815 stream, &table_len ) ) != 0 ) 816 { 817 FT_TRACE1(( "is missing\n" )); 818 goto Exit; 819 } 820 821 fvar_start = FT_STREAM_POS( ); 822 823 if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) ) 824 goto Exit; 825 826 if ( fvar_head.version != (FT_Long)0x00010000L || 827 #if 0 828 /* fonts like `JamRegular.ttf' have an incorrect value for */ 829 /* `countSizePairs'; since value 2 is hard-coded in `fvar' */ 830 /* version 1.0, we simply ignore it */ 831 fvar_head.countSizePairs != 2 || 832 #endif 833 fvar_head.axisSize != 20 || 834 /* axisCount limit implied by 16-bit instanceSize */ 835 fvar_head.axisCount > 0x3FFE || 836 fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount || 837 /* instanceCount limit implied by limited range of name IDs */ 838 fvar_head.instanceCount > 0x7EFF || 839 fvar_head.offsetToData + fvar_head.axisCount * 20U + 840 fvar_head.instanceCount * fvar_head.instanceSize > table_len ) 841 { 842 FT_TRACE1(( "\n" 843 "TT_Get_MM_Var: invalid `fvar' header\n" )); 844 error = FT_THROW( Invalid_Table ); 845 goto Exit; 846 } 847 848 FT_TRACE2(( "loaded\n" )); 849 850 FT_TRACE5(( "number of GX style axes: %d\n", fvar_head.axisCount )); 851 852 if ( FT_NEW( face->blend ) ) 853 goto Exit; 854 855 /* cannot overflow 32-bit arithmetic because of limits above */ 856 face->blend->mmvar_len = 857 sizeof ( FT_MM_Var ) + 858 fvar_head.axisCount * sizeof ( FT_Var_Axis ) + 859 fvar_head.instanceCount * sizeof ( FT_Var_Named_Style ) + 860 fvar_head.instanceCount * fvar_head.axisCount * sizeof ( FT_Fixed ) + 861 5 * fvar_head.axisCount; 862 863 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) ) 864 goto Exit; 865 face->blend->mmvar = mmvar; 866 867 /* set up pointers and offsets into the `mmvar' array; */ 868 /* the data gets filled in later on */ 869 870 mmvar->num_axis = 871 fvar_head.axisCount; 872 mmvar->num_designs = 873 ~0U; /* meaningless in this context; each glyph */ 874 /* may have a different number of designs */ 875 /* (or tuples, as called by Apple) */ 876 mmvar->num_namedstyles = 877 fvar_head.instanceCount; 878 mmvar->axis = 879 (FT_Var_Axis*)&( mmvar[1] ); 880 mmvar->namedstyle = 881 (FT_Var_Named_Style*)&( mmvar->axis[fvar_head.axisCount] ); 882 883 next_coords = 884 (FT_Fixed*)&( mmvar->namedstyle[fvar_head.instanceCount] ); 885 for ( i = 0; i < fvar_head.instanceCount; i++ ) 886 { 887 mmvar->namedstyle[i].coords = next_coords; 888 next_coords += fvar_head.axisCount; 889 } 890 891 next_name = (FT_String*)next_coords; 892 for ( i = 0; i < fvar_head.axisCount; i++ ) 893 { 894 mmvar->axis[i].name = next_name; 895 next_name += 5; 896 } 897 898 /* now fill in the data */ 899 900 if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) ) 901 goto Exit; 902 903 a = mmvar->axis; 904 for ( i = 0; i < fvar_head.axisCount; i++ ) 905 { 906 GX_FVar_Axis axis_rec; 907 908 909 if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) ) 910 goto Exit; 911 a->tag = axis_rec.axisTag; 912 a->minimum = axis_rec.minValue; 913 a->def = axis_rec.defaultValue; 914 a->maximum = axis_rec.maxValue; 915 a->strid = axis_rec.nameID; 916 917 a->name[0] = (FT_String)( a->tag >> 24 ); 918 a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF ); 919 a->name[2] = (FT_String)( ( a->tag >> 8 ) & 0xFF ); 920 a->name[3] = (FT_String)( ( a->tag ) & 0xFF ); 921 a->name[4] = '\0'; 922 923 FT_TRACE5(( " \"%s\": minimum=%.4f, default=%.4f, maximum=%.4f\n", 924 a->name, 925 a->minimum / 65536.0, 926 a->def / 65536.0, 927 a->maximum / 65536.0 )); 928 929 a++; 930 } 931 932 FT_TRACE5(( "\n" )); 933 934 ns = mmvar->namedstyle; 935 for ( i = 0; i < fvar_head.instanceCount; i++, ns++ ) 936 { 937 if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) ) 938 goto Exit; 939 940 ns->strid = FT_GET_USHORT(); 941 (void) /* flags = */ FT_GET_USHORT(); 942 943 for ( j = 0; j < fvar_head.axisCount; j++ ) 944 ns->coords[j] = FT_GET_LONG(); 945 946 FT_FRAME_EXIT(); 947 } 948 } 949 950 /* fill the output array if requested */ 951 952 if ( master != NULL ) 953 { 954 FT_UInt n; 955 956 957 if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) ) 958 goto Exit; 959 FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len ); 960 961 mmvar->axis = 962 (FT_Var_Axis*)&( mmvar[1] ); 963 mmvar->namedstyle = 964 (FT_Var_Named_Style*)&( mmvar->axis[mmvar->num_axis] ); 965 next_coords = 966 (FT_Fixed*)&( mmvar->namedstyle[mmvar->num_namedstyles] ); 967 968 for ( n = 0; n < mmvar->num_namedstyles; n++ ) 969 { 970 mmvar->namedstyle[n].coords = next_coords; 971 next_coords += mmvar->num_axis; 972 } 973 974 a = mmvar->axis; 975 next_name = (FT_String*)next_coords; 976 for ( n = 0; n < mmvar->num_axis; n++ ) 977 { 978 a->name = next_name; 979 980 /* standard PostScript names for some standard apple tags */ 981 if ( a->tag == TTAG_wght ) 982 a->name = (char*)"Weight"; 983 else if ( a->tag == TTAG_wdth ) 984 a->name = (char*)"Width"; 985 else if ( a->tag == TTAG_opsz ) 986 a->name = (char*)"OpticalSize"; 987 else if ( a->tag == TTAG_slnt ) 988 a->name = (char*)"Slant"; 989 990 next_name += 5; 991 a++; 992 } 993 994 *master = mmvar; 995 } 996 997 Exit: 998 return error; 999 } 1000 1001 1002 /*************************************************************************/ 1003 /* */ 1004 /* <Function> */ 1005 /* TT_Set_MM_Blend */ 1006 /* */ 1007 /* <Description> */ 1008 /* Set the blend (normalized) coordinates for this instance of the */ 1009 /* font. Check that the `gvar' table is reasonable and does some */ 1010 /* initial preparation. */ 1011 /* */ 1012 /* <InOut> */ 1013 /* face :: The font. */ 1014 /* Initialize the blend structure with `gvar' data. */ 1015 /* */ 1016 /* <Input> */ 1017 /* num_coords :: The number of available coordinates. If it is */ 1018 /* larger than the number of axes, ignore the excess */ 1019 /* values. If it is smaller than the number of axes, */ 1020 /* use the default value (0) for the remaining axes. */ 1021 /* */ 1022 /* coords :: An array of `num_coords', each between [-1,1]. */ 1023 /* */ 1024 /* <Return> */ 1025 /* FreeType error code. 0 means success. */ 1026 /* */ 1027 FT_LOCAL_DEF( FT_Error ) 1028 TT_Set_MM_Blend( TT_Face face, 1029 FT_UInt num_coords, 1030 FT_Fixed* coords ) 1031 { 1032 FT_Error error = FT_Err_Ok; 1033 GX_Blend blend; 1034 FT_MM_Var* mmvar; 1035 FT_UInt i; 1036 FT_Memory memory = face->root.memory; 1037 1038 enum 1039 { 1040 mcvt_retain, 1041 mcvt_modify, 1042 mcvt_load 1043 1044 } manageCvt; 1045 1046 1047 face->doblend = FALSE; 1048 1049 if ( face->blend == NULL ) 1050 { 1051 if ( ( error = TT_Get_MM_Var( face, NULL ) ) != 0 ) 1052 goto Exit; 1053 } 1054 1055 blend = face->blend; 1056 mmvar = blend->mmvar; 1057 1058 if ( num_coords > mmvar->num_axis ) 1059 { 1060 FT_TRACE2(( "TT_Set_MM_Blend: only using first %d of %d coordinates\n", 1061 mmvar->num_axis, num_coords )); 1062 num_coords = mmvar->num_axis; 1063 } 1064 1065 FT_TRACE5(( "normalized design coordinates:\n" )); 1066 1067 for ( i = 0; i < num_coords; i++ ) 1068 { 1069 FT_TRACE5(( " %.4f\n", coords[i] / 65536.0 )); 1070 if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L ) 1071 { 1072 FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.4f\n" 1073 " is out of range [-1;1]\n", 1074 coords[i] / 65536.0 )); 1075 error = FT_THROW( Invalid_Argument ); 1076 goto Exit; 1077 } 1078 } 1079 1080 FT_TRACE5(( "\n" )); 1081 1082 if ( blend->glyphoffsets == NULL ) 1083 if ( ( error = ft_var_load_gvar( face ) ) != 0 ) 1084 goto Exit; 1085 1086 if ( blend->normalizedcoords == NULL ) 1087 { 1088 if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) ) 1089 goto Exit; 1090 1091 manageCvt = mcvt_modify; 1092 1093 /* If we have not set the blend coordinates before this, then the */ 1094 /* cvt table will still be what we read from the `cvt ' table and */ 1095 /* we don't need to reload it. We may need to change it though... */ 1096 } 1097 else 1098 { 1099 manageCvt = mcvt_retain; 1100 1101 for ( i = 0; i < num_coords; i++ ) 1102 { 1103 if ( blend->normalizedcoords[i] != coords[i] ) 1104 { 1105 manageCvt = mcvt_load; 1106 break; 1107 } 1108 } 1109 1110 for ( ; i < mmvar->num_axis; i++ ) 1111 { 1112 if ( blend->normalizedcoords[i] != 0 ) 1113 { 1114 manageCvt = mcvt_load; 1115 break; 1116 } 1117 } 1118 1119 /* If we don't change the blend coords then we don't need to do */ 1120 /* anything to the cvt table. It will be correct. Otherwise we */ 1121 /* no longer have the original cvt (it was modified when we set */ 1122 /* the blend last time), so we must reload and then modify it. */ 1123 } 1124 1125 blend->num_axis = mmvar->num_axis; 1126 FT_MEM_COPY( blend->normalizedcoords, 1127 coords, 1128 num_coords * sizeof ( FT_Fixed ) ); 1129 1130 face->doblend = TRUE; 1131 1132 if ( face->cvt != NULL ) 1133 { 1134 switch ( manageCvt ) 1135 { 1136 case mcvt_load: 1137 /* The cvt table has been loaded already; every time we change the */ 1138 /* blend we may need to reload and remodify the cvt table. */ 1139 FT_FREE( face->cvt ); 1140 face->cvt = NULL; 1141 1142 error = tt_face_load_cvt( face, face->root.stream ); 1143 break; 1144 1145 case mcvt_modify: 1146 /* The original cvt table is in memory. All we need to do is */ 1147 /* apply the `cvar' table (if any). */ 1148 error = tt_face_vary_cvt( face, face->root.stream ); 1149 break; 1150 1151 case mcvt_retain: 1152 /* The cvt table is correct for this set of coordinates. */ 1153 break; 1154 } 1155 } 1156 1157 Exit: 1158 return error; 1159 } 1160 1161 1162 /*************************************************************************/ 1163 /* */ 1164 /* <Function> */ 1165 /* TT_Set_Var_Design */ 1166 /* */ 1167 /* <Description> */ 1168 /* Set the coordinates for the instance, measured in the user */ 1169 /* coordinate system. Parse the `avar' table (if present) to convert */ 1170 /* from user to normalized coordinates. */ 1171 /* */ 1172 /* <InOut> */ 1173 /* face :: The font face. */ 1174 /* Initialize the blend struct with `gvar' data. */ 1175 /* */ 1176 /* <Input> */ 1177 /* num_coords :: The number of available coordinates. If it is */ 1178 /* larger than the number of axes, ignore the excess */ 1179 /* values. If it is smaller than the number of axes, */ 1180 /* use the default values for the remaining axes. */ 1181 /* */ 1182 /* coords :: A coordinate array with `num_coords' elements. */ 1183 /* */ 1184 /* <Return> */ 1185 /* FreeType error code. 0 means success. */ 1186 /* */ 1187 FT_LOCAL_DEF( FT_Error ) 1188 TT_Set_Var_Design( TT_Face face, 1189 FT_UInt num_coords, 1190 FT_Fixed* coords ) 1191 { 1192 FT_Error error = FT_Err_Ok; 1193 FT_Fixed* normalized = NULL; 1194 GX_Blend blend; 1195 FT_MM_Var* mmvar; 1196 FT_UInt i, j; 1197 FT_Var_Axis* a; 1198 GX_AVarSegment av; 1199 FT_Memory memory = face->root.memory; 1200 1201 1202 if ( face->blend == NULL ) 1203 { 1204 if ( ( error = TT_Get_MM_Var( face, NULL ) ) != 0 ) 1205 goto Exit; 1206 } 1207 1208 blend = face->blend; 1209 mmvar = blend->mmvar; 1210 1211 if ( num_coords > mmvar->num_axis ) 1212 { 1213 FT_TRACE2(( "TT_Set_Var_Design:" 1214 " only using first %d of %d coordinates\n", 1215 mmvar->num_axis, num_coords )); 1216 num_coords = mmvar->num_axis; 1217 } 1218 1219 /* Axis normalization is a two stage process. First we normalize */ 1220 /* based on the [min,def,max] values for the axis to be [-1,0,1]. */ 1221 /* Then, if there's an `avar' table, we renormalize this range. */ 1222 1223 if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) ) 1224 goto Exit; 1225 1226 FT_TRACE5(( "design coordinates:\n" )); 1227 1228 a = mmvar->axis; 1229 for ( i = 0; i < num_coords; i++, a++ ) 1230 { 1231 FT_TRACE5(( " %.4f\n", coords[i] / 65536.0 )); 1232 if ( coords[i] > a->maximum || coords[i] < a->minimum ) 1233 { 1234 FT_TRACE1(( "TT_Set_Var_Design: normalized design coordinate %.4f\n" 1235 " is out of range [%.4f;%.4f]\n", 1236 coords[i] / 65536.0, 1237 a->minimum / 65536.0, 1238 a->maximum / 65536.0 )); 1239 error = FT_THROW( Invalid_Argument ); 1240 goto Exit; 1241 } 1242 1243 if ( coords[i] < a->def ) 1244 normalized[i] = -FT_DivFix( coords[i] - a->def, 1245 a->minimum - a->def ); 1246 else if ( a->maximum == a->def ) 1247 normalized[i] = 0; 1248 else 1249 normalized[i] = FT_DivFix( coords[i] - a->def, 1250 a->maximum - a->def ); 1251 } 1252 1253 FT_TRACE5(( "\n" )); 1254 1255 for ( ; i < mmvar->num_axis; i++ ) 1256 normalized[i] = 0; 1257 1258 if ( !blend->avar_checked ) 1259 ft_var_load_avar( face ); 1260 1261 if ( blend->avar_segment != NULL ) 1262 { 1263 FT_TRACE5(( "normalized design coordinates" 1264 " before applying `avar' data:\n" )); 1265 1266 av = blend->avar_segment; 1267 for ( i = 0; i < mmvar->num_axis; i++, av++ ) 1268 { 1269 for ( j = 1; j < (FT_UInt)av->pairCount; j++ ) 1270 { 1271 FT_TRACE5(( " %.4f\n", normalized[i] / 65536.0 )); 1272 if ( normalized[i] < av->correspondence[j].fromCoord ) 1273 { 1274 normalized[i] = 1275 FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord, 1276 av->correspondence[j].toCoord - 1277 av->correspondence[j - 1].toCoord, 1278 av->correspondence[j].fromCoord - 1279 av->correspondence[j - 1].fromCoord ) + 1280 av->correspondence[j - 1].toCoord; 1281 break; 1282 } 1283 } 1284 } 1285 } 1286 1287 error = TT_Set_MM_Blend( face, mmvar->num_axis, normalized ); 1288 1289 Exit: 1290 FT_FREE( normalized ); 1291 return error; 1292 } 1293 1294 1295 /*************************************************************************/ 1296 /*************************************************************************/ 1297 /***** *****/ 1298 /***** GX VAR PARSING ROUTINES *****/ 1299 /***** *****/ 1300 /*************************************************************************/ 1301 /*************************************************************************/ 1302 1303 1304 /*************************************************************************/ 1305 /* */ 1306 /* <Function> */ 1307 /* tt_face_vary_cvt */ 1308 /* */ 1309 /* <Description> */ 1310 /* Modify the loaded cvt table according to the `cvar' table and the */ 1311 /* font's blend. */ 1312 /* */ 1313 /* <InOut> */ 1314 /* face :: A handle to the target face object. */ 1315 /* */ 1316 /* <Input> */ 1317 /* stream :: A handle to the input stream. */ 1318 /* */ 1319 /* <Return> */ 1320 /* FreeType error code. 0 means success. */ 1321 /* */ 1322 /* Most errors are ignored. It is perfectly valid not to have a */ 1323 /* `cvar' table even if there is a `gvar' and `fvar' table. */ 1324 /* */ 1325 FT_LOCAL_DEF( FT_Error ) 1326 tt_face_vary_cvt( TT_Face face, 1327 FT_Stream stream ) 1328 { 1329 FT_Error error; 1330 FT_Memory memory = stream->memory; 1331 FT_ULong table_start; 1332 FT_ULong table_len; 1333 FT_UInt tupleCount; 1334 FT_ULong offsetToData; 1335 FT_ULong here; 1336 FT_UInt i, j; 1337 FT_Fixed* tuple_coords = NULL; 1338 FT_Fixed* im_start_coords = NULL; 1339 FT_Fixed* im_end_coords = NULL; 1340 GX_Blend blend = face->blend; 1341 FT_UInt point_count; 1342 FT_UShort* localpoints; 1343 FT_Short* deltas; 1344 1345 1346 FT_TRACE2(( "CVAR " )); 1347 1348 if ( blend == NULL ) 1349 { 1350 FT_TRACE2(( "\n" 1351 "tt_face_vary_cvt: no blend specified\n" )); 1352 error = FT_Err_Ok; 1353 goto Exit; 1354 } 1355 1356 if ( face->cvt == NULL ) 1357 { 1358 FT_TRACE2(( "\n" 1359 "tt_face_vary_cvt: no `cvt ' table\n" )); 1360 error = FT_Err_Ok; 1361 goto Exit; 1362 } 1363 1364 error = face->goto_table( face, TTAG_cvar, stream, &table_len ); 1365 if ( error ) 1366 { 1367 FT_TRACE2(( "is missing\n" )); 1368 1369 error = FT_Err_Ok; 1370 goto Exit; 1371 } 1372 1373 if ( FT_FRAME_ENTER( table_len ) ) 1374 { 1375 error = FT_Err_Ok; 1376 goto Exit; 1377 } 1378 1379 table_start = FT_Stream_FTell( stream ); 1380 if ( FT_GET_LONG() != 0x00010000L ) 1381 { 1382 FT_TRACE2(( "bad table version\n" )); 1383 1384 error = FT_Err_Ok; 1385 goto FExit; 1386 } 1387 1388 FT_TRACE2(( "loaded\n" )); 1389 1390 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || 1391 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || 1392 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) 1393 goto FExit; 1394 1395 tupleCount = FT_GET_USHORT(); 1396 offsetToData = FT_GET_USHORT(); 1397 1398 /* rough sanity test */ 1399 if ( offsetToData + tupleCount * 4 > table_len ) 1400 { 1401 FT_TRACE2(( "tt_face_vary_cvt:" 1402 " invalid CVT variation array header\n" )); 1403 1404 error = FT_THROW( Invalid_Table ); 1405 goto FExit; 1406 } 1407 1408 offsetToData += table_start; 1409 1410 /* The documentation implies there are flags packed into */ 1411 /* `tupleCount', but John Jenkins says that shared points don't apply */ 1412 /* to `cvar', and no other flags are defined. */ 1413 1414 FT_TRACE5(( "cvar: there are %d tuples:\n", tupleCount & 0xFFF )); 1415 1416 for ( i = 0; i < ( tupleCount & 0xFFF ); i++ ) 1417 { 1418 FT_UInt tupleDataSize; 1419 FT_UInt tupleIndex; 1420 FT_Fixed apply; 1421 1422 1423 FT_TRACE6(( " tuple %d:\n", i )); 1424 1425 tupleDataSize = FT_GET_USHORT(); 1426 tupleIndex = FT_GET_USHORT(); 1427 1428 /* There is no provision here for a global tuple coordinate section, */ 1429 /* so John says. There are no tuple indices, just embedded tuples. */ 1430 1431 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 1432 { 1433 for ( j = 0; j < blend->num_axis; j++ ) 1434 tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */ 1435 /* short frac to fixed */ 1436 } 1437 else 1438 { 1439 /* skip this tuple; it makes no sense */ 1440 1441 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 1442 for ( j = 0; j < 2 * blend->num_axis; j++ ) 1443 (void)FT_GET_SHORT(); 1444 1445 offsetToData += tupleDataSize; 1446 continue; 1447 } 1448 1449 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 1450 { 1451 for ( j = 0; j < blend->num_axis; j++ ) 1452 im_start_coords[j] = FT_GET_SHORT() * 4; 1453 for ( j = 0; j < blend->num_axis; j++ ) 1454 im_end_coords[j] = FT_GET_SHORT() * 4; 1455 } 1456 1457 apply = ft_var_apply_tuple( blend, 1458 (FT_UShort)tupleIndex, 1459 tuple_coords, 1460 im_start_coords, 1461 im_end_coords ); 1462 if ( /* tuple isn't active for our blend */ 1463 apply == 0 || 1464 /* global points not allowed, */ 1465 /* if they aren't local, makes no sense */ 1466 !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) ) 1467 { 1468 offsetToData += tupleDataSize; 1469 continue; 1470 } 1471 1472 here = FT_Stream_FTell( stream ); 1473 1474 FT_Stream_SeekSet( stream, offsetToData ); 1475 1476 localpoints = ft_var_readpackedpoints( stream, 1477 table_len, 1478 &point_count ); 1479 deltas = ft_var_readpackeddeltas( stream, 1480 table_len, 1481 point_count == 0 ? face->cvt_size 1482 : point_count ); 1483 if ( localpoints == NULL || deltas == NULL ) 1484 ; /* failure, ignore it */ 1485 1486 else if ( localpoints == ALL_POINTS ) 1487 { 1488 #ifdef FT_DEBUG_LEVEL_TRACE 1489 int count = 0; 1490 #endif 1491 1492 1493 FT_TRACE7(( " CVT deltas:\n" )); 1494 1495 /* this means that there are deltas for every entry in cvt */ 1496 for ( j = 0; j < face->cvt_size; j++ ) 1497 { 1498 FT_Long orig_cvt = face->cvt[j]; 1499 1500 1501 face->cvt[j] = (FT_Short)( orig_cvt + 1502 FT_MulFix( deltas[j], apply ) ); 1503 1504 #ifdef FT_DEBUG_LEVEL_TRACE 1505 if ( orig_cvt != face->cvt[j] ) 1506 { 1507 FT_TRACE7(( " %d: %d -> %d\n", 1508 j, orig_cvt, face->cvt[j] )); 1509 count++; 1510 } 1511 #endif 1512 } 1513 1514 #ifdef FT_DEBUG_LEVEL_TRACE 1515 if ( !count ) 1516 FT_TRACE7(( " none\n" )); 1517 #endif 1518 } 1519 1520 else 1521 { 1522 #ifdef FT_DEBUG_LEVEL_TRACE 1523 int count = 0; 1524 #endif 1525 1526 1527 FT_TRACE7(( " CVT deltas:\n" )); 1528 1529 for ( j = 0; j < point_count; j++ ) 1530 { 1531 int pindex = localpoints[j]; 1532 FT_Long orig_cvt = face->cvt[pindex]; 1533 1534 1535 face->cvt[pindex] = (FT_Short)( orig_cvt + 1536 FT_MulFix( deltas[j], apply ) ); 1537 1538 #ifdef FT_DEBUG_LEVEL_TRACE 1539 if ( orig_cvt != face->cvt[pindex] ) 1540 { 1541 FT_TRACE7(( " %d: %d -> %d\n", 1542 pindex, orig_cvt, face->cvt[pindex] )); 1543 count++; 1544 } 1545 #endif 1546 } 1547 1548 #ifdef FT_DEBUG_LEVEL_TRACE 1549 if ( !count ) 1550 FT_TRACE7(( " none\n" )); 1551 #endif 1552 } 1553 1554 if ( localpoints != ALL_POINTS ) 1555 FT_FREE( localpoints ); 1556 FT_FREE( deltas ); 1557 1558 offsetToData += tupleDataSize; 1559 1560 FT_Stream_SeekSet( stream, here ); 1561 } 1562 1563 FT_TRACE5(( "\n" )); 1564 1565 FExit: 1566 FT_FRAME_EXIT(); 1567 1568 Exit: 1569 FT_FREE( tuple_coords ); 1570 FT_FREE( im_start_coords ); 1571 FT_FREE( im_end_coords ); 1572 1573 return error; 1574 } 1575 1576 1577 /* Shift the original coordinates of all points between indices `p1' */ 1578 /* and `p2', using the same difference as given by index `ref'. */ 1579 1580 /* modeled after `af_iup_shift' */ 1581 1582 static void 1583 tt_delta_shift( int p1, 1584 int p2, 1585 int ref, 1586 FT_Vector* in_points, 1587 FT_Vector* out_points ) 1588 { 1589 int p; 1590 FT_Vector delta; 1591 1592 1593 delta.x = out_points[ref].x - in_points[ref].x; 1594 delta.y = out_points[ref].y - in_points[ref].y; 1595 1596 if ( delta.x == 0 && delta.y == 0 ) 1597 return; 1598 1599 for ( p = p1; p < ref; p++ ) 1600 { 1601 out_points[p].x += delta.x; 1602 out_points[p].y += delta.y; 1603 } 1604 1605 for ( p = ref + 1; p <= p2; p++ ) 1606 { 1607 out_points[p].x += delta.x; 1608 out_points[p].y += delta.y; 1609 } 1610 } 1611 1612 1613 /* Interpolate the original coordinates of all points with indices */ 1614 /* between `p1' and `p2', using `ref1' and `ref2' as the reference */ 1615 /* point indices. */ 1616 1617 /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */ 1618 /* `Ins_IUP' */ 1619 1620 static void 1621 tt_delta_interpolate( int p1, 1622 int p2, 1623 int ref1, 1624 int ref2, 1625 FT_Vector* in_points, 1626 FT_Vector* out_points ) 1627 { 1628 int p, i; 1629 1630 FT_Pos out, in1, in2, out1, out2, d1, d2; 1631 1632 1633 if ( p1 > p2 ) 1634 return; 1635 1636 /* handle both horizontal and vertical coordinates */ 1637 for ( i = 0; i <= 1; i++ ) 1638 { 1639 /* shift array pointers so that we can access `foo.y' as `foo.x' */ 1640 in_points = (FT_Vector*)( (FT_Pos*)in_points + i ); 1641 out_points = (FT_Vector*)( (FT_Pos*)out_points + i ); 1642 1643 if ( in_points[ref1].x > in_points[ref2].x ) 1644 { 1645 p = ref1; 1646 ref1 = ref2; 1647 ref2 = p; 1648 } 1649 1650 in1 = in_points[ref1].x; 1651 in2 = in_points[ref2].x; 1652 out1 = out_points[ref1].x; 1653 out2 = out_points[ref2].x; 1654 d1 = out1 - in1; 1655 d2 = out2 - in2; 1656 1657 if ( out1 == out2 || in1 == in2 ) 1658 { 1659 for ( p = p1; p <= p2; p++ ) 1660 { 1661 out = in_points[p].x; 1662 1663 if ( out <= in1 ) 1664 out += d1; 1665 else if ( out >= in2 ) 1666 out += d2; 1667 else 1668 out = out1; 1669 1670 out_points[p].x = out; 1671 } 1672 } 1673 else 1674 { 1675 FT_Fixed scale = FT_DivFix( out2 - out1, in2 - in1 ); 1676 1677 1678 for ( p = p1; p <= p2; p++ ) 1679 { 1680 out = in_points[p].x; 1681 1682 if ( out <= in1 ) 1683 out += d1; 1684 else if ( out >= in2 ) 1685 out += d2; 1686 else 1687 out = out1 + FT_MulFix( out - in1, scale ); 1688 1689 out_points[p].x = out; 1690 } 1691 } 1692 } 1693 } 1694 1695 1696 /* Interpolate points without delta values, similar to */ 1697 /* the `IUP' hinting instruction. */ 1698 1699 /* modeled after `Ins_IUP */ 1700 1701 static void 1702 tt_handle_deltas( FT_Outline* outline, 1703 FT_Vector* in_points, 1704 FT_Bool* has_delta ) 1705 { 1706 FT_Vector* out_points; 1707 1708 FT_Int first_point; 1709 FT_Int end_point; 1710 1711 FT_Int first_delta; 1712 FT_Int cur_delta; 1713 1714 FT_Int point; 1715 FT_Short contour; 1716 1717 1718 /* ignore empty outlines */ 1719 if ( !outline->n_contours ) 1720 return; 1721 1722 out_points = outline->points; 1723 1724 contour = 0; 1725 point = 0; 1726 1727 do 1728 { 1729 end_point = outline->contours[contour]; 1730 first_point = point; 1731 1732 /* search first point that has a delta */ 1733 while ( point <= end_point && !has_delta[point] ) 1734 point++; 1735 1736 if ( point <= end_point ) 1737 { 1738 first_delta = point; 1739 cur_delta = point; 1740 1741 point++; 1742 1743 while ( point <= end_point ) 1744 { 1745 /* search next point that has a delta */ 1746 /* and interpolate intermediate points */ 1747 if ( has_delta[point] ) 1748 { 1749 tt_delta_interpolate( cur_delta + 1, 1750 point - 1, 1751 cur_delta, 1752 point, 1753 in_points, 1754 out_points ); 1755 cur_delta = point; 1756 } 1757 1758 point++; 1759 } 1760 1761 /* shift contour if we only have a single delta */ 1762 if ( cur_delta == first_delta ) 1763 tt_delta_shift( first_point, 1764 end_point, 1765 cur_delta, 1766 in_points, 1767 out_points ); 1768 else 1769 { 1770 /* otherwise handle remaining points */ 1771 /* at the end and beginning of the contour */ 1772 tt_delta_interpolate( cur_delta + 1, 1773 end_point, 1774 cur_delta, 1775 first_delta, 1776 in_points, 1777 out_points ); 1778 1779 if ( first_delta > 0 ) 1780 tt_delta_interpolate( first_point, 1781 first_delta - 1, 1782 cur_delta, 1783 first_delta, 1784 in_points, 1785 out_points ); 1786 } 1787 } 1788 contour++; 1789 1790 } while ( contour < outline->n_contours ); 1791 } 1792 1793 1794 /*************************************************************************/ 1795 /* */ 1796 /* <Function> */ 1797 /* TT_Vary_Apply_Glyph_Deltas */ 1798 /* */ 1799 /* <Description> */ 1800 /* Apply the appropriate deltas to the current glyph. */ 1801 /* */ 1802 /* <Input> */ 1803 /* face :: A handle to the target face object. */ 1804 /* */ 1805 /* glyph_index :: The index of the glyph being modified. */ 1806 /* */ 1807 /* n_points :: The number of the points in the glyph, including */ 1808 /* phantom points. */ 1809 /* */ 1810 /* <InOut> */ 1811 /* outline :: The outline to change. */ 1812 /* */ 1813 /* <Return> */ 1814 /* FreeType error code. 0 means success. */ 1815 /* */ 1816 FT_LOCAL_DEF( FT_Error ) 1817 TT_Vary_Apply_Glyph_Deltas( TT_Face face, 1818 FT_UInt glyph_index, 1819 FT_Outline* outline, 1820 FT_UInt n_points ) 1821 { 1822 FT_Stream stream = face->root.stream; 1823 FT_Memory memory = stream->memory; 1824 GX_Blend blend = face->blend; 1825 1826 FT_Vector* points_org = NULL; 1827 FT_Bool* has_delta = NULL; 1828 1829 FT_Error error; 1830 FT_ULong glyph_start; 1831 FT_UInt tupleCount; 1832 FT_ULong offsetToData; 1833 FT_ULong here; 1834 FT_UInt i, j; 1835 FT_Fixed* tuple_coords = NULL; 1836 FT_Fixed* im_start_coords = NULL; 1837 FT_Fixed* im_end_coords = NULL; 1838 FT_UInt point_count, spoint_count = 0; 1839 FT_UShort* sharedpoints = NULL; 1840 FT_UShort* localpoints = NULL; 1841 FT_UShort* points; 1842 FT_Short *deltas_x, *deltas_y; 1843 1844 1845 if ( !face->doblend || blend == NULL ) 1846 return FT_THROW( Invalid_Argument ); 1847 1848 if ( glyph_index >= blend->gv_glyphcnt || 1849 blend->glyphoffsets[glyph_index] == 1850 blend->glyphoffsets[glyph_index + 1] ) 1851 { 1852 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 1853 " no variation data for this glyph\n" )); 1854 return FT_Err_Ok; 1855 } 1856 1857 if ( FT_NEW_ARRAY( points_org, n_points ) || 1858 FT_NEW_ARRAY( has_delta, n_points ) ) 1859 goto Fail1; 1860 1861 if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) || 1862 FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] - 1863 blend->glyphoffsets[glyph_index] ) ) 1864 goto Fail1; 1865 1866 glyph_start = FT_Stream_FTell( stream ); 1867 1868 /* each set of glyph variation data is formatted similarly to `cvar' */ 1869 /* (except we get shared points and global tuples) */ 1870 1871 if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis ) || 1872 FT_NEW_ARRAY( im_start_coords, blend->num_axis ) || 1873 FT_NEW_ARRAY( im_end_coords, blend->num_axis ) ) 1874 goto Fail2; 1875 1876 tupleCount = FT_GET_USHORT(); 1877 offsetToData = FT_GET_USHORT(); 1878 1879 /* rough sanity test */ 1880 if ( offsetToData + tupleCount * 4 > blend->gvar_size ) 1881 { 1882 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 1883 " invalid glyph variation array header\n" )); 1884 1885 error = FT_THROW( Invalid_Table ); 1886 goto Fail2; 1887 } 1888 1889 offsetToData += glyph_start; 1890 1891 if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS ) 1892 { 1893 here = FT_Stream_FTell( stream ); 1894 1895 FT_Stream_SeekSet( stream, offsetToData ); 1896 1897 sharedpoints = ft_var_readpackedpoints( stream, 1898 blend->gvar_size, 1899 &spoint_count ); 1900 offsetToData = FT_Stream_FTell( stream ); 1901 1902 FT_Stream_SeekSet( stream, here ); 1903 } 1904 1905 FT_TRACE5(( "gvar: there are %d tuples:\n", 1906 tupleCount & GX_TC_TUPLE_COUNT_MASK )); 1907 1908 for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ ) 1909 { 1910 FT_UInt tupleDataSize; 1911 FT_UInt tupleIndex; 1912 FT_Fixed apply; 1913 1914 1915 FT_TRACE6(( " tuple %d:\n", i )); 1916 1917 tupleDataSize = FT_GET_USHORT(); 1918 tupleIndex = FT_GET_USHORT(); 1919 1920 if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD ) 1921 { 1922 for ( j = 0; j < blend->num_axis; j++ ) 1923 tuple_coords[j] = FT_GET_SHORT() * 4; /* convert from */ 1924 /* short frac to fixed */ 1925 } 1926 else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount ) 1927 { 1928 FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:" 1929 " invalid tuple index\n" )); 1930 1931 error = FT_THROW( Invalid_Table ); 1932 goto Fail2; 1933 } 1934 else 1935 FT_MEM_COPY( 1936 tuple_coords, 1937 &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis], 1938 blend->num_axis * sizeof ( FT_Fixed ) ); 1939 1940 if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) 1941 { 1942 for ( j = 0; j < blend->num_axis; j++ ) 1943 im_start_coords[j] = FT_GET_SHORT() * 4; 1944 for ( j = 0; j < blend->num_axis; j++ ) 1945 im_end_coords[j] = FT_GET_SHORT() * 4; 1946 } 1947 1948 apply = ft_var_apply_tuple( blend, 1949 (FT_UShort)tupleIndex, 1950 tuple_coords, 1951 im_start_coords, 1952 im_end_coords ); 1953 1954 if ( apply == 0 ) /* tuple isn't active for our blend */ 1955 { 1956 offsetToData += tupleDataSize; 1957 continue; 1958 } 1959 1960 here = FT_Stream_FTell( stream ); 1961 1962 if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) 1963 { 1964 FT_Stream_SeekSet( stream, offsetToData ); 1965 1966 localpoints = ft_var_readpackedpoints( stream, 1967 blend->gvar_size, 1968 &point_count ); 1969 points = localpoints; 1970 } 1971 else 1972 { 1973 points = sharedpoints; 1974 point_count = spoint_count; 1975 } 1976 1977 deltas_x = ft_var_readpackeddeltas( stream, 1978 blend->gvar_size, 1979 point_count == 0 ? n_points 1980 : point_count ); 1981 deltas_y = ft_var_readpackeddeltas( stream, 1982 blend->gvar_size, 1983 point_count == 0 ? n_points 1984 : point_count ); 1985 1986 if ( points == NULL || deltas_y == NULL || deltas_x == NULL ) 1987 ; /* failure, ignore it */ 1988 1989 else if ( points == ALL_POINTS ) 1990 { 1991 #ifdef FT_DEBUG_LEVEL_TRACE 1992 int count = 0; 1993 #endif 1994 1995 1996 FT_TRACE7(( " point deltas:\n" )); 1997 1998 /* this means that there are deltas for every point in the glyph */ 1999 for ( j = 0; j < n_points; j++ ) 2000 { 2001 #ifdef FT_DEBUG_LEVEL_TRACE 2002 FT_Vector point_org = outline->points[j]; 2003 #endif 2004 2005 2006 outline->points[j].x += FT_MulFix( deltas_x[j], apply ); 2007 outline->points[j].y += FT_MulFix( deltas_y[j], apply ); 2008 2009 #ifdef FT_DEBUG_LEVEL_TRACE 2010 if ( ( point_org.x != outline->points[j].x ) || 2011 ( point_org.y != outline->points[j].y ) ) 2012 { 2013 FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n", 2014 j, 2015 point_org.x, 2016 point_org.y, 2017 outline->points[j].x, 2018 outline->points[j].y )); 2019 count++; 2020 } 2021 #endif 2022 } 2023 2024 #ifdef FT_DEBUG_LEVEL_TRACE 2025 if ( !count ) 2026 FT_TRACE7(( " none\n" )); 2027 #endif 2028 } 2029 2030 else if ( localpoints == NULL ) 2031 ; /* failure, ignore it */ 2032 2033 else 2034 { 2035 #ifdef FT_DEBUG_LEVEL_TRACE 2036 int count = 0; 2037 #endif 2038 2039 2040 /* we have to interpolate the missing deltas similar to the */ 2041 /* IUP bytecode instruction */ 2042 for ( j = 0; j < n_points; j++ ) 2043 { 2044 points_org[j] = outline->points[j]; 2045 has_delta[j] = FALSE; 2046 } 2047 2048 for ( j = 0; j < point_count; j++ ) 2049 { 2050 FT_UShort idx = localpoints[j]; 2051 2052 2053 if ( idx >= n_points ) 2054 continue; 2055 2056 has_delta[idx] = TRUE; 2057 2058 outline->points[idx].x += FT_MulFix( deltas_x[j], apply ); 2059 outline->points[idx].y += FT_MulFix( deltas_y[j], apply ); 2060 } 2061 2062 /* no need to handle phantom points here, */ 2063 /* since solitary points can't be interpolated */ 2064 tt_handle_deltas( outline, 2065 points_org, 2066 has_delta ); 2067 2068 #ifdef FT_DEBUG_LEVEL_TRACE 2069 FT_TRACE7(( " point deltas:\n" )); 2070 2071 for ( j = 0; j < n_points; j++) 2072 { 2073 if ( ( points_org[j].x != outline->points[j].x ) || 2074 ( points_org[j].y != outline->points[j].y ) ) 2075 { 2076 FT_TRACE7(( " %d: (%d, %d) -> (%d, %d)\n", 2077 j, 2078 points_org[j].x, 2079 points_org[j].y, 2080 outline->points[j].x, 2081 outline->points[j].y )); 2082 count++; 2083 } 2084 } 2085 2086 if ( !count ) 2087 FT_TRACE7(( " none\n" )); 2088 #endif 2089 } 2090 2091 if ( localpoints != ALL_POINTS ) 2092 FT_FREE( localpoints ); 2093 FT_FREE( deltas_x ); 2094 FT_FREE( deltas_y ); 2095 2096 offsetToData += tupleDataSize; 2097 2098 FT_Stream_SeekSet( stream, here ); 2099 } 2100 2101 FT_TRACE5(( "\n" )); 2102 2103 Fail2: 2104 if ( sharedpoints != ALL_POINTS ) 2105 FT_FREE( sharedpoints ); 2106 FT_FREE( tuple_coords ); 2107 FT_FREE( im_start_coords ); 2108 FT_FREE( im_end_coords ); 2109 2110 FT_FRAME_EXIT(); 2111 2112 Fail1: 2113 FT_FREE( points_org ); 2114 FT_FREE( has_delta ); 2115 2116 return error; 2117 } 2118 2119 2120 /*************************************************************************/ 2121 /* */ 2122 /* <Function> */ 2123 /* tt_done_blend */ 2124 /* */ 2125 /* <Description> */ 2126 /* Free the blend internal data structure. */ 2127 /* */ 2128 FT_LOCAL_DEF( void ) 2129 tt_done_blend( FT_Memory memory, 2130 GX_Blend blend ) 2131 { 2132 if ( blend != NULL ) 2133 { 2134 FT_UInt i; 2135 2136 2137 FT_FREE( blend->normalizedcoords ); 2138 FT_FREE( blend->mmvar ); 2139 2140 if ( blend->avar_segment != NULL ) 2141 { 2142 for ( i = 0; i < blend->num_axis; i++ ) 2143 FT_FREE( blend->avar_segment[i].correspondence ); 2144 FT_FREE( blend->avar_segment ); 2145 } 2146 2147 FT_FREE( blend->tuplecoords ); 2148 FT_FREE( blend->glyphoffsets ); 2149 FT_FREE( blend ); 2150 } 2151 } 2152 2153 #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */ 2154 2155 2156 /* END */ 2157