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