1 /****************************************************************************
   2  *
   3  * ttgxvar.c
   4  *
   5  *   TrueType GX Font Variation loader
   6  *
   7  * Copyright (C) 2004-2019 by
   8  * David Turner, Robert Wilhelm, Werner Lemberg, and George Williams.
   9  *
  10  * This file is part of the FreeType project, and may only be used,
  11  * modified, and distributed under the terms of the FreeType project
  12  * license, LICENSE.TXT.  By continuing to use, modify, or distribute
  13  * this file you indicate that you have read the license and
  14  * understand and accept it fully.
  15  *
  16  */
  17 
  18 
  19   /**************************************************************************
  20    *
  21    * Apple documents the `fvar', `gvar', `cvar', and `avar' tables at
  22    *
  23    *   https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6[fgca]var.html
  24    *
  25    * The documentation for `gvar' is not intelligible; `cvar' refers you
  26    * to `gvar' and is thus also incomprehensible.
  27    *
  28    * The documentation for `avar' appears correct, but Apple has no fonts
  29    * with an `avar' table, so it is hard to test.
  30    *
  31    * Many thanks to John Jenkins (at Apple) in figuring this out.
  32    *
  33    *
  34    * Apple's `kern' table has some references to tuple indices, but as
  35    * there is no indication where these indices are defined, nor how to
  36    * interpolate the kerning values (different tuples have different
  37    * classes) this issue is ignored.
  38    *
  39    */
  40 
  41 
  42 #include <ft2build.h>
  43 #include FT_INTERNAL_DEBUG_H
  44 #include FT_CONFIG_CONFIG_H
  45 #include FT_INTERNAL_STREAM_H
  46 #include FT_INTERNAL_SFNT_H
  47 #include FT_TRUETYPE_TAGS_H
  48 #include FT_TRUETYPE_IDS_H
  49 #include FT_MULTIPLE_MASTERS_H
  50 #include FT_LIST_H
  51 
  52 #include "ttpload.h"
  53 #include "ttgxvar.h"
  54 
  55 #include "tterrors.h"
  56 
  57 
  58 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
  59 
  60 
  61 #define FT_Stream_FTell( stream )                         \
  62           (FT_ULong)( (stream)->cursor - (stream)->base )
  63 #define FT_Stream_SeekSet( stream, off )                               \
  64           (stream)->cursor =                                           \
  65             ( (off) < (FT_ULong)( (stream)->limit - (stream)->base ) ) \
  66                         ? (stream)->base + (off)                       \
  67                         : (stream)->limit
  68 
  69 
  70   /* some macros we need */
  71 #define FT_fdot14ToFixed( x )                  \
  72           ( (FT_Fixed)( (FT_ULong)(x) << 2 ) )
  73 #define FT_intToFixed( i )                      \
  74           ( (FT_Fixed)( (FT_ULong)(i) << 16 ) )
  75 #define FT_fdot6ToFixed( i )                    \
  76           ( (FT_Fixed)( (FT_ULong)(i) << 10 ) )
  77 #define FT_fixedToInt( x )                          \
  78           ( (FT_Short)( ( (x) + 0x8000U ) >> 16 ) )
  79 #define FT_fixedToFdot6( x )                    \
  80           ( (FT_Pos)( ( (x) + 0x200 ) >> 10 ) )
  81 
  82 
  83   /**************************************************************************
  84    *
  85    * The macro FT_COMPONENT is used in trace mode.  It is an implicit
  86    * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
  87    * messages during execution.
  88    */
  89 #undef  FT_COMPONENT
  90 #define FT_COMPONENT  ttgxvar
  91 
  92 
  93   /*************************************************************************/
  94   /*************************************************************************/
  95   /*****                                                               *****/
  96   /*****                       Internal Routines                       *****/
  97   /*****                                                               *****/
  98   /*************************************************************************/
  99   /*************************************************************************/
 100 
 101 
 102   /**************************************************************************
 103    *
 104    * The macro ALL_POINTS is used in `ft_var_readpackedpoints'.  It
 105    * indicates that there is a delta for every point without needing to
 106    * enumerate all of them.
 107    */
 108 
 109   /* ensure that value `0' has the same width as a pointer */
 110 #define ALL_POINTS  (FT_UShort*)~(FT_PtrDist)0
 111 
 112 
 113 #define GX_PT_POINTS_ARE_WORDS      0x80U
 114 #define GX_PT_POINT_RUN_COUNT_MASK  0x7FU
 115 
 116 
 117   /**************************************************************************
 118    *
 119    * @Function:
 120    *   ft_var_readpackedpoints
 121    *
 122    * @Description:
 123    *   Read a set of points to which the following deltas will apply.
 124    *   Points are packed with a run length encoding.
 125    *
 126    * @Input:
 127    *   stream ::
 128    *     The data stream.
 129    *
 130    *   size ::
 131    *     The size of the table holding the data.
 132    *
 133    * @Output:
 134    *   point_cnt ::
 135    *     The number of points read.  A zero value means that
 136    *     all points in the glyph will be affected, without
 137    *     enumerating them individually.
 138    *
 139    * @Return:
 140    *   An array of FT_UShort containing the affected points or the
 141    *   special value ALL_POINTS.
 142    */
 143   static FT_UShort*
 144   ft_var_readpackedpoints( FT_Stream  stream,
 145                            FT_ULong   size,
 146                            FT_UInt   *point_cnt )
 147   {
 148     FT_UShort *points = NULL;
 149     FT_UInt    n;
 150     FT_UInt    runcnt;
 151     FT_UInt    i, j;
 152     FT_UShort  first;
 153     FT_Memory  memory = stream->memory;
 154     FT_Error   error  = FT_Err_Ok;
 155 
 156     FT_UNUSED( error );
 157 
 158 
 159     *point_cnt = 0;
 160 
 161     n = FT_GET_BYTE();
 162     if ( n == 0 )
 163       return ALL_POINTS;
 164 
 165     if ( n & GX_PT_POINTS_ARE_WORDS )
 166     {
 167       n  &= GX_PT_POINT_RUN_COUNT_MASK;
 168       n <<= 8;
 169       n  |= FT_GET_BYTE();
 170     }
 171 
 172     if ( n > size )
 173     {
 174       FT_TRACE1(( "ft_var_readpackedpoints: number of points too large\n" ));
 175       return NULL;
 176     }
 177 
 178     /* in the nested loops below we increase `i' twice; */
 179     /* it is faster to simply allocate one more slot    */
 180     /* than to add another test within the loop         */
 181     if ( FT_NEW_ARRAY( points, n + 1 ) )
 182       return NULL;
 183 
 184     *point_cnt = n;
 185 
 186     first = 0;
 187     i     = 0;
 188     while ( i < n )
 189     {
 190       runcnt = FT_GET_BYTE();
 191       if ( runcnt & GX_PT_POINTS_ARE_WORDS )
 192       {
 193         runcnt     &= GX_PT_POINT_RUN_COUNT_MASK;
 194         first      += FT_GET_USHORT();
 195         points[i++] = first;
 196 
 197         /* first point not included in run count */
 198         for ( j = 0; j < runcnt; j++ )
 199         {
 200           first      += FT_GET_USHORT();
 201           points[i++] = first;
 202           if ( i >= n )
 203             break;
 204         }
 205       }
 206       else
 207       {
 208         first      += FT_GET_BYTE();
 209         points[i++] = first;
 210 
 211         for ( j = 0; j < runcnt; j++ )
 212         {
 213           first      += FT_GET_BYTE();
 214           points[i++] = first;
 215           if ( i >= n )
 216             break;
 217         }
 218       }
 219     }
 220 
 221     return points;
 222   }
 223 
 224 
 225 #define GX_DT_DELTAS_ARE_ZERO       0x80U
 226 #define GX_DT_DELTAS_ARE_WORDS      0x40U
 227 #define GX_DT_DELTA_RUN_COUNT_MASK  0x3FU
 228 
 229 
 230   /**************************************************************************
 231    *
 232    * @Function:
 233    *   ft_var_readpackeddeltas
 234    *
 235    * @Description:
 236    *   Read a set of deltas.  These are packed slightly differently than
 237    *   points.  In particular there is no overall count.
 238    *
 239    * @Input:
 240    *   stream ::
 241    *     The data stream.
 242    *
 243    *   size ::
 244    *     The size of the table holding the data.
 245    *
 246    *   delta_cnt ::
 247    *     The number of deltas to be read.
 248    *
 249    * @Return:
 250    *   An array of FT_Fixed containing the deltas for the affected
 251    *   points.  (This only gets the deltas for one dimension.  It will
 252    *   generally be called twice, once for x, once for y.  When used in
 253    *   cvt table, it will only be called once.)
 254    *
 255    *   We use FT_Fixed to avoid accumulation errors while summing up all
 256    *   deltas (the rounding to integer values happens as the very last
 257    *   step).
 258    */
 259   static FT_Fixed*
 260   ft_var_readpackeddeltas( FT_Stream  stream,
 261                            FT_ULong   size,
 262                            FT_UInt    delta_cnt )
 263   {
 264     FT_Fixed  *deltas = NULL;
 265     FT_UInt    runcnt, cnt;
 266     FT_UInt    i, j;
 267     FT_Memory  memory = stream->memory;
 268     FT_Error   error  = FT_Err_Ok;
 269 
 270     FT_UNUSED( error );
 271 
 272 
 273     if ( delta_cnt > size )
 274     {
 275       FT_TRACE1(( "ft_var_readpackeddeltas: number of points too large\n" ));
 276       return NULL;
 277     }
 278 
 279     if ( FT_NEW_ARRAY( deltas, delta_cnt ) )
 280       return NULL;
 281 
 282     i = 0;
 283     while ( i < delta_cnt )
 284     {
 285       runcnt = FT_GET_BYTE();
 286       cnt    = runcnt & GX_DT_DELTA_RUN_COUNT_MASK;
 287 
 288       if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
 289       {
 290         /* `runcnt' zeroes get added */
 291         for ( j = 0; j <= cnt && i < delta_cnt; j++ )
 292           deltas[i++] = 0;
 293       }
 294       else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
 295       {
 296         /* `runcnt' shorts from the stack */
 297         for ( j = 0; j <= cnt && i < delta_cnt; j++ )
 298           deltas[i++] = FT_intToFixed( FT_GET_SHORT() );
 299       }
 300       else
 301       {
 302         /* `runcnt' signed bytes from the stack */
 303         for ( j = 0; j <= cnt && i < delta_cnt; j++ )
 304           deltas[i++] = FT_intToFixed( FT_GET_CHAR() );
 305       }
 306 
 307       if ( j <= cnt )
 308       {
 309         /* bad format */
 310         FT_FREE( deltas );
 311         return NULL;
 312       }
 313     }
 314 
 315     return deltas;
 316   }
 317 
 318 
 319   /**************************************************************************
 320    *
 321    * @Function:
 322    *   ft_var_load_avar
 323    *
 324    * @Description:
 325    *   Parse the `avar' table if present.  It need not be, so we return
 326    *   nothing.
 327    *
 328    * @InOut:
 329    *   face ::
 330    *     The font face.
 331    */
 332   static void
 333   ft_var_load_avar( TT_Face  face )
 334   {
 335     FT_Stream       stream = FT_FACE_STREAM( face );
 336     FT_Memory       memory = stream->memory;
 337     GX_Blend        blend  = face->blend;
 338     GX_AVarSegment  segment;
 339     FT_Error        error = FT_Err_Ok;
 340     FT_Long         version;
 341     FT_Long         axisCount;
 342     FT_Int          i, j;
 343     FT_ULong        table_len;
 344 
 345     FT_UNUSED( error );
 346 
 347 
 348     FT_TRACE2(( "AVAR " ));
 349 
 350     blend->avar_loaded = TRUE;
 351     error = face->goto_table( face, TTAG_avar, stream, &table_len );
 352     if ( error )
 353     {
 354       FT_TRACE2(( "is missing\n" ));
 355       return;
 356     }
 357 
 358     if ( FT_FRAME_ENTER( table_len ) )
 359       return;
 360 
 361     version   = FT_GET_LONG();
 362     axisCount = FT_GET_LONG();
 363 
 364     if ( version != 0x00010000L )
 365     {
 366       FT_TRACE2(( "bad table version\n" ));
 367       goto Exit;
 368     }
 369 
 370     FT_TRACE2(( "loaded\n" ));
 371 
 372     if ( axisCount != (FT_Long)blend->mmvar->num_axis )
 373     {
 374       FT_TRACE2(( "ft_var_load_avar: number of axes in `avar' and `fvar'\n"
 375                   "                  table are different\n" ));
 376       goto Exit;
 377     }
 378 
 379     if ( FT_NEW_ARRAY( blend->avar_segment, axisCount ) )
 380       goto Exit;
 381 
 382     segment = &blend->avar_segment[0];
 383     for ( i = 0; i < axisCount; i++, segment++ )
 384     {
 385       FT_TRACE5(( "  axis %d:\n", i ));
 386 
 387       segment->pairCount = FT_GET_USHORT();
 388       if ( (FT_ULong)segment->pairCount * 4 > table_len                ||
 389            FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) )
 390       {
 391         /* Failure.  Free everything we have done so far.  We must do */
 392         /* it right now since loading the `avar' table is optional.   */
 393 
 394         for ( j = i - 1; j >= 0; j-- )
 395           FT_FREE( blend->avar_segment[j].correspondence );
 396 
 397         FT_FREE( blend->avar_segment );
 398         blend->avar_segment = NULL;
 399         goto Exit;
 400       }
 401 
 402       for ( j = 0; j < segment->pairCount; j++ )
 403       {
 404         segment->correspondence[j].fromCoord =
 405           FT_fdot14ToFixed( FT_GET_SHORT() );
 406         segment->correspondence[j].toCoord =
 407           FT_fdot14ToFixed( FT_GET_SHORT() );
 408 
 409         FT_TRACE5(( "    mapping %.5f to %.5f\n",
 410                     segment->correspondence[j].fromCoord / 65536.0,
 411                     segment->correspondence[j].toCoord / 65536.0 ));
 412       }
 413 
 414       FT_TRACE5(( "\n" ));
 415     }
 416 
 417   Exit:
 418     FT_FRAME_EXIT();
 419   }
 420 
 421 
 422   static FT_Error
 423   ft_var_load_item_variation_store( TT_Face          face,
 424                                     FT_ULong         offset,
 425                                     GX_ItemVarStore  itemStore )
 426   {
 427     FT_Stream  stream = FT_FACE_STREAM( face );
 428     FT_Memory  memory = stream->memory;
 429 
 430     FT_Error   error;
 431     FT_UShort  format;
 432     FT_ULong   region_offset;
 433     FT_UInt    i, j, k;
 434     FT_UInt    shortDeltaCount;
 435 
 436     GX_Blend        blend = face->blend;
 437     GX_ItemVarData  varData;
 438 
 439     FT_ULong*  dataOffsetArray = NULL;
 440 
 441 
 442     if ( FT_STREAM_SEEK( offset ) ||
 443          FT_READ_USHORT( format ) )
 444       goto Exit;
 445 
 446     if ( format != 1 )
 447     {
 448       FT_TRACE2(( "ft_var_load_item_variation_store: bad store format %d\n",
 449                   format ));
 450       error = FT_THROW( Invalid_Table );
 451       goto Exit;
 452     }
 453 
 454     /* read top level fields */
 455     if ( FT_READ_ULONG( region_offset )         ||
 456          FT_READ_USHORT( itemStore->dataCount ) )
 457       goto Exit;
 458 
 459     /* we need at least one entry in `itemStore->varData' */
 460     if ( !itemStore->dataCount )
 461     {
 462       FT_TRACE2(( "ft_var_load_item_variation_store: missing varData\n" ));
 463       error = FT_THROW( Invalid_Table );
 464       goto Exit;
 465     }
 466 
 467     /* make temporary copy of item variation data offsets; */
 468     /* we will parse region list first, then come back     */
 469     if ( FT_NEW_ARRAY( dataOffsetArray, itemStore->dataCount ) )
 470       goto Exit;
 471 
 472     for ( i = 0; i < itemStore->dataCount; i++ )
 473     {
 474       if ( FT_READ_ULONG( dataOffsetArray[i] ) )
 475         goto Exit;
 476     }
 477 
 478     /* parse array of region records (region list) */
 479     if ( FT_STREAM_SEEK( offset + region_offset ) )
 480       goto Exit;
 481 
 482     if ( FT_READ_USHORT( itemStore->axisCount )   ||
 483          FT_READ_USHORT( itemStore->regionCount ) )
 484       goto Exit;
 485 
 486     if ( itemStore->axisCount != (FT_Long)blend->mmvar->num_axis )
 487     {
 488       FT_TRACE2(( "ft_var_load_item_variation_store:"
 489                   " number of axes in item variation store\n"
 490                   "                                 "
 491                   " and `fvar' table are different\n" ));
 492       error = FT_THROW( Invalid_Table );
 493       goto Exit;
 494     }
 495 
 496     if ( FT_NEW_ARRAY( itemStore->varRegionList, itemStore->regionCount ) )
 497       goto Exit;
 498 
 499     for ( i = 0; i < itemStore->regionCount; i++ )
 500     {
 501       GX_AxisCoords  axisCoords;
 502 
 503 
 504       if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList,
 505                          itemStore->axisCount ) )
 506         goto Exit;
 507 
 508       axisCoords = itemStore->varRegionList[i].axisList;
 509 
 510       for ( j = 0; j < itemStore->axisCount; j++ )
 511       {
 512         FT_Short  start, peak, end;
 513 
 514 
 515         if ( FT_READ_SHORT( start ) ||
 516              FT_READ_SHORT( peak )  ||
 517              FT_READ_SHORT( end )   )
 518           goto Exit;
 519 
 520         axisCoords[j].startCoord = FT_fdot14ToFixed( start );
 521         axisCoords[j].peakCoord  = FT_fdot14ToFixed( peak );
 522         axisCoords[j].endCoord   = FT_fdot14ToFixed( end );
 523       }
 524     }
 525 
 526     /* end of region list parse */
 527 
 528     /* use dataOffsetArray now to parse varData items */
 529     if ( FT_NEW_ARRAY( itemStore->varData, itemStore->dataCount ) )
 530       goto Exit;
 531 
 532     for ( i = 0; i < itemStore->dataCount; i++ )
 533     {
 534       varData = &itemStore->varData[i];
 535 
 536       if ( FT_STREAM_SEEK( offset + dataOffsetArray[i] ) )
 537         goto Exit;
 538 
 539       if ( FT_READ_USHORT( varData->itemCount )      ||
 540            FT_READ_USHORT( shortDeltaCount )         ||
 541            FT_READ_USHORT( varData->regionIdxCount ) )
 542         goto Exit;
 543 
 544       /* check some data consistency */
 545       if ( shortDeltaCount > varData->regionIdxCount )
 546       {
 547         FT_TRACE2(( "bad short count %d or region count %d\n",
 548                     shortDeltaCount,
 549                     varData->regionIdxCount ));
 550         error = FT_THROW( Invalid_Table );
 551         goto Exit;
 552       }
 553 
 554       if ( varData->regionIdxCount > itemStore->regionCount )
 555       {
 556         FT_TRACE2(( "inconsistent regionCount %d in varData[%d]\n",
 557                     varData->regionIdxCount,
 558                     i ));
 559         error = FT_THROW( Invalid_Table );
 560         goto Exit;
 561       }
 562 
 563       /* parse region indices */
 564       if ( FT_NEW_ARRAY( varData->regionIndices,
 565                          varData->regionIdxCount ) )
 566         goto Exit;
 567 
 568       for ( j = 0; j < varData->regionIdxCount; j++ )
 569       {
 570         if ( FT_READ_USHORT( varData->regionIndices[j] ) )
 571           goto Exit;
 572 
 573         if ( varData->regionIndices[j] >= itemStore->regionCount )
 574         {
 575           FT_TRACE2(( "bad region index %d\n",
 576                       varData->regionIndices[j] ));
 577           error = FT_THROW( Invalid_Table );
 578           goto Exit;
 579         }
 580       }
 581 
 582       /* Parse delta set.                                                */
 583       /*                                                                 */
 584       /* On input, deltas are (shortDeltaCount + regionIdxCount) bytes   */
 585       /* each; on output, deltas are expanded to `regionIdxCount' shorts */
 586       /* each.                                                           */
 587       if ( FT_NEW_ARRAY( varData->deltaSet,
 588                          varData->regionIdxCount * varData->itemCount ) )
 589         goto Exit;
 590 
 591       /* the delta set is stored as a 2-dimensional array of shorts; */
 592       /* sign-extend signed bytes to signed shorts                   */
 593       for ( j = 0; j < varData->itemCount * varData->regionIdxCount; )
 594       {
 595         for ( k = 0; k < shortDeltaCount; k++, j++ )
 596         {
 597           /* read the short deltas */
 598           FT_Short  delta;
 599 
 600 
 601           if ( FT_READ_SHORT( delta ) )
 602             goto Exit;
 603 
 604           varData->deltaSet[j] = delta;
 605         }
 606 
 607         for ( ; k < varData->regionIdxCount; k++, j++ )
 608         {
 609           /* read the (signed) byte deltas */
 610           FT_Char  delta;
 611 
 612 
 613           if ( FT_READ_CHAR( delta ) )
 614             goto Exit;
 615 
 616           varData->deltaSet[j] = delta;
 617         }
 618       }
 619     }
 620 
 621   Exit:
 622     FT_FREE( dataOffsetArray );
 623 
 624     return error;
 625   }
 626 
 627 
 628   static FT_Error
 629   ft_var_load_delta_set_index_mapping( TT_Face            face,
 630                                        FT_ULong           offset,
 631                                        GX_DeltaSetIdxMap  map,
 632                                        GX_ItemVarStore    itemStore )
 633   {
 634     FT_Stream  stream = FT_FACE_STREAM( face );
 635     FT_Memory  memory = stream->memory;
 636 
 637     FT_Error   error;
 638 
 639     FT_UShort  format;
 640     FT_UInt    entrySize;
 641     FT_UInt    innerBitCount;
 642     FT_UInt    innerIndexMask;
 643     FT_UInt    i, j;
 644 
 645 
 646     if ( FT_STREAM_SEEK( offset )        ||
 647          FT_READ_USHORT( format )        ||
 648          FT_READ_USHORT( map->mapCount ) )
 649       goto Exit;
 650 
 651     if ( format & 0xFFC0 )
 652     {
 653       FT_TRACE2(( "bad map format %d\n", format ));
 654       error = FT_THROW( Invalid_Table );
 655       goto Exit;
 656     }
 657 
 658     /* bytes per entry: 1, 2, 3, or 4 */
 659     entrySize      = ( ( format & 0x0030 ) >> 4 ) + 1;
 660     innerBitCount  = ( format & 0x000F ) + 1;
 661     innerIndexMask = ( 1 << innerBitCount ) - 1;
 662 
 663     if ( FT_NEW_ARRAY( map->innerIndex, map->mapCount ) )
 664       goto Exit;
 665 
 666     if ( FT_NEW_ARRAY( map->outerIndex, map->mapCount ) )
 667       goto Exit;
 668 
 669     for ( i = 0; i < map->mapCount; i++ )
 670     {
 671       FT_UInt  mapData = 0;
 672       FT_UInt  outerIndex, innerIndex;
 673 
 674 
 675       /* read map data one unsigned byte at a time, big endian */
 676       for ( j = 0; j < entrySize; j++ )
 677       {
 678         FT_Byte  data;
 679 
 680 
 681         if ( FT_READ_BYTE( data ) )
 682           goto Exit;
 683 
 684         mapData = ( mapData << 8 ) | data;
 685       }
 686 
 687       outerIndex = mapData >> innerBitCount;
 688 
 689       if ( outerIndex >= itemStore->dataCount )
 690       {
 691         FT_TRACE2(( "outerIndex[%d] == %d out of range\n",
 692                     i,
 693                     outerIndex ));
 694         error = FT_THROW( Invalid_Table );
 695         goto Exit;
 696       }
 697 
 698       map->outerIndex[i] = outerIndex;
 699 
 700       innerIndex = mapData & innerIndexMask;
 701 
 702       if ( innerIndex >= itemStore->varData[outerIndex].itemCount )
 703       {
 704         FT_TRACE2(( "innerIndex[%d] == %d out of range\n",
 705                     i,
 706                     innerIndex ));
 707         error = FT_THROW( Invalid_Table );
 708           goto Exit;
 709       }
 710 
 711       map->innerIndex[i] = innerIndex;
 712     }
 713 
 714   Exit:
 715     return error;
 716   }
 717 
 718 
 719   /**************************************************************************
 720    *
 721    * @Function:
 722    *   ft_var_load_hvvar
 723    *
 724    * @Description:
 725    *   If `vertical' is zero, parse the `HVAR' table and set
 726    *   `blend->hvar_loaded' to TRUE.  On success, `blend->hvar_checked'
 727    *   is set to TRUE.
 728    *
 729    *   If `vertical' is not zero, parse the `VVAR' table and set
 730    *   `blend->vvar_loaded' to TRUE.  On success, `blend->vvar_checked'
 731    *   is set to TRUE.
 732    *
 733    *   Some memory may remain allocated on error; it is always freed in
 734    *   `tt_done_blend', however.
 735    *
 736    * @InOut:
 737    *   face ::
 738    *     The font face.
 739    *
 740    * @Return:
 741    *   FreeType error code.  0 means success.
 742    */
 743   static FT_Error
 744   ft_var_load_hvvar( TT_Face  face,
 745                      FT_Bool  vertical )
 746   {
 747     FT_Stream  stream = FT_FACE_STREAM( face );
 748     FT_Memory  memory = stream->memory;
 749 
 750     GX_Blend  blend = face->blend;
 751 
 752     GX_HVVarTable  table;
 753 
 754     FT_Error   error;
 755     FT_UShort  majorVersion;
 756     FT_ULong   table_len;
 757     FT_ULong   table_offset;
 758     FT_ULong   store_offset;
 759     FT_ULong   widthMap_offset;
 760 
 761 
 762     if ( vertical )
 763     {
 764       blend->vvar_loaded = TRUE;
 765 
 766       FT_TRACE2(( "VVAR " ));
 767 
 768       error = face->goto_table( face, TTAG_VVAR, stream, &table_len );
 769     }
 770     else
 771     {
 772       blend->hvar_loaded = TRUE;
 773 
 774       FT_TRACE2(( "HVAR " ));
 775 
 776       error = face->goto_table( face, TTAG_HVAR, stream, &table_len );
 777     }
 778 
 779     if ( error )
 780     {
 781       FT_TRACE2(( "is missing\n" ));
 782       goto Exit;
 783     }
 784 
 785     table_offset = FT_STREAM_POS();
 786 
 787     /* skip minor version */
 788     if ( FT_READ_USHORT( majorVersion ) ||
 789          FT_STREAM_SKIP( 2 )            )
 790       goto Exit;
 791 
 792     if ( majorVersion != 1 )
 793     {
 794       FT_TRACE2(( "bad table version %d\n", majorVersion ));
 795       error = FT_THROW( Invalid_Table );
 796       goto Exit;
 797     }
 798 
 799     if ( FT_READ_ULONG( store_offset )    ||
 800          FT_READ_ULONG( widthMap_offset ) )
 801       goto Exit;
 802 
 803     if ( vertical )
 804     {
 805       if ( FT_NEW( blend->vvar_table ) )
 806         goto Exit;
 807       table = blend->vvar_table;
 808     }
 809     else
 810     {
 811       if ( FT_NEW( blend->hvar_table ) )
 812         goto Exit;
 813       table = blend->hvar_table;
 814     }
 815 
 816     error = ft_var_load_item_variation_store(
 817               face,
 818               table_offset + store_offset,
 819               &table->itemStore );
 820     if ( error )
 821       goto Exit;
 822 
 823     if ( widthMap_offset )
 824     {
 825       error = ft_var_load_delta_set_index_mapping(
 826                 face,
 827                 table_offset + widthMap_offset,
 828                 &table->widthMap,
 829                 &table->itemStore );
 830       if ( error )
 831         goto Exit;
 832     }
 833 
 834     FT_TRACE2(( "loaded\n" ));
 835     error = FT_Err_Ok;
 836 
 837   Exit:
 838     if ( !error )
 839     {
 840       if ( vertical )
 841       {
 842         blend->vvar_checked = TRUE;
 843 
 844         /* FreeType doesn't provide functions to quickly retrieve    */
 845         /* TSB, BSB, or VORG values; we thus don't have to implement */
 846         /* support for those three item variation stores.            */
 847 
 848         face->variation_support |= TT_FACE_FLAG_VAR_VADVANCE;
 849       }
 850       else
 851       {
 852         blend->hvar_checked = TRUE;
 853 
 854         /* FreeType doesn't provide functions to quickly retrieve */
 855         /* LSB or RSB values; we thus don't have to implement     */
 856         /* support for those two item variation stores.           */
 857 
 858         face->variation_support |= TT_FACE_FLAG_VAR_HADVANCE;
 859       }
 860     }
 861 
 862     return error;
 863   }
 864 
 865 
 866   static FT_Int
 867   ft_var_get_item_delta( TT_Face          face,
 868                          GX_ItemVarStore  itemStore,
 869                          FT_UInt          outerIndex,
 870                          FT_UInt          innerIndex )
 871   {
 872     GX_ItemVarData  varData;
 873     FT_Short*       deltaSet;
 874 
 875     FT_UInt   master, j;
 876     FT_Fixed  netAdjustment = 0;     /* accumulated adjustment */
 877     FT_Fixed  scaledDelta;
 878     FT_Fixed  delta;
 879 
 880 
 881     /* See pseudo code from `Font Variations Overview' */
 882     /* in the OpenType specification.                  */
 883 
 884     varData  = &itemStore->varData[outerIndex];
 885     deltaSet = &varData->deltaSet[varData->regionIdxCount * innerIndex];
 886 
 887     /* outer loop steps through master designs to be blended */
 888     for ( master = 0; master < varData->regionIdxCount; master++ )
 889     {
 890       FT_Fixed  scalar      = 0x10000L;
 891       FT_UInt   regionIndex = varData->regionIndices[master];
 892 
 893       GX_AxisCoords  axis = itemStore->varRegionList[regionIndex].axisList;
 894 
 895 
 896       /* inner loop steps through axes in this region */
 897       for ( j = 0; j < itemStore->axisCount; j++, axis++ )
 898       {
 899         /* compute the scalar contribution of this axis; */
 900         /* ignore invalid ranges                         */
 901         if ( axis->startCoord > axis->peakCoord ||
 902              axis->peakCoord > axis->endCoord   )
 903           continue;
 904 
 905         else if ( axis->startCoord < 0 &&
 906                   axis->endCoord > 0   &&
 907                   axis->peakCoord != 0 )
 908           continue;
 909 
 910         /* peak of 0 means ignore this axis */
 911         else if ( axis->peakCoord == 0 )
 912           continue;
 913 
 914         else if ( face->blend->normalizedcoords[j] == axis->peakCoord )
 915           continue;
 916 
 917         /* ignore this region if coords are out of range */
 918         else if ( face->blend->normalizedcoords[j] <= axis->startCoord ||
 919                   face->blend->normalizedcoords[j] >= axis->endCoord   )
 920         {
 921           scalar = 0;
 922           break;
 923         }
 924 
 925         /* cumulative product of all the axis scalars */
 926         else if ( face->blend->normalizedcoords[j] < axis->peakCoord )
 927           scalar =
 928             FT_MulDiv( scalar,
 929                        face->blend->normalizedcoords[j] - axis->startCoord,
 930                        axis->peakCoord - axis->startCoord );
 931         else
 932           scalar =
 933             FT_MulDiv( scalar,
 934                        axis->endCoord - face->blend->normalizedcoords[j],
 935                        axis->endCoord - axis->peakCoord );
 936       } /* per-axis loop */
 937 
 938       /* get the scaled delta for this region */
 939       delta       = FT_intToFixed( deltaSet[master] );
 940       scaledDelta = FT_MulFix( scalar, delta );
 941 
 942       /* accumulate the adjustments from each region */
 943       netAdjustment = netAdjustment + scaledDelta;
 944 
 945     } /* per-region loop */
 946 
 947     return FT_fixedToInt( netAdjustment );
 948   }
 949 
 950 
 951   /**************************************************************************
 952    *
 953    * @Function:
 954    *   tt_hvadvance_adjust
 955    *
 956    * @Description:
 957    *   Apply `HVAR' advance width or `VVAR' advance height adjustment of
 958    *   a given glyph.
 959    *
 960    * @Input:
 961    *   gindex ::
 962    *     The glyph index.
 963    *
 964    *   vertical ::
 965    *     If set, handle `VVAR' table.
 966    *
 967    * @InOut:
 968    *   face ::
 969    *     The font face.
 970    *
 971    *   adelta ::
 972    *     Points to width or height value that gets modified.
 973    */
 974   static FT_Error
 975   tt_hvadvance_adjust( TT_Face  face,
 976                        FT_UInt  gindex,
 977                        FT_Int  *avalue,
 978                        FT_Bool  vertical )
 979   {
 980     FT_Error  error = FT_Err_Ok;
 981     FT_UInt   innerIndex, outerIndex;
 982     FT_Int    delta;
 983 
 984     GX_HVVarTable  table;
 985 
 986 
 987     if ( !face->doblend || !face->blend )
 988       goto Exit;
 989 
 990     if ( vertical )
 991     {
 992       if ( !face->blend->vvar_loaded )
 993       {
 994         /* initialize vvar table */
 995         face->blend->vvar_error = ft_var_load_hvvar( face, 1 );
 996       }
 997 
 998       if ( !face->blend->vvar_checked )
 999       {
1000         error = face->blend->vvar_error;
1001         goto Exit;
1002       }
1003 
1004       table = face->blend->vvar_table;
1005     }
1006     else
1007     {
1008       if ( !face->blend->hvar_loaded )
1009       {
1010         /* initialize hvar table */
1011         face->blend->hvar_error = ft_var_load_hvvar( face, 0 );
1012       }
1013 
1014       if ( !face->blend->hvar_checked )
1015       {
1016         error = face->blend->hvar_error;
1017         goto Exit;
1018       }
1019 
1020       table = face->blend->hvar_table;
1021     }
1022 
1023     /* advance width or height adjustments are always present in an */
1024     /* `HVAR' or `VVAR' table; no need to test for this capability  */
1025 
1026     if ( table->widthMap.innerIndex )
1027     {
1028       FT_UInt  idx = gindex;
1029 
1030 
1031       if ( idx >= table->widthMap.mapCount )
1032         idx = table->widthMap.mapCount - 1;
1033 
1034       /* trust that HVAR parser has checked indices */
1035       outerIndex = table->widthMap.outerIndex[idx];
1036       innerIndex = table->widthMap.innerIndex[idx];
1037     }
1038     else
1039     {
1040       GX_ItemVarData  varData;
1041 
1042 
1043       /* no widthMap data */
1044       outerIndex = 0;
1045       innerIndex = gindex;
1046 
1047       varData = &table->itemStore.varData[outerIndex];
1048       if ( gindex >= varData->itemCount )
1049       {
1050         FT_TRACE2(( "gindex %d out of range\n", gindex ));
1051         error = FT_THROW( Invalid_Argument );
1052         goto Exit;
1053       }
1054     }
1055 
1056     delta = ft_var_get_item_delta( face,
1057                                    &table->itemStore,
1058                                    outerIndex,
1059                                    innerIndex );
1060 
1061     FT_TRACE5(( "%s value %d adjusted by %d unit%s (%s)\n",
1062                 vertical ? "vertical height" : "horizontal width",
1063                 *avalue,
1064                 delta,
1065                 delta == 1 ? "" : "s",
1066                 vertical ? "VVAR" : "HVAR" ));
1067 
1068     *avalue += delta;
1069 
1070   Exit:
1071     return error;
1072   }
1073 
1074 
1075   FT_LOCAL_DEF( FT_Error )
1076   tt_hadvance_adjust( TT_Face  face,
1077                       FT_UInt  gindex,
1078                       FT_Int  *avalue )
1079   {
1080     return tt_hvadvance_adjust( face, gindex, avalue, 0 );
1081   }
1082 
1083 
1084   FT_LOCAL_DEF( FT_Error )
1085   tt_vadvance_adjust( TT_Face  face,
1086                       FT_UInt  gindex,
1087                       FT_Int  *avalue )
1088   {
1089     return tt_hvadvance_adjust( face, gindex, avalue, 1 );
1090   }
1091 
1092 
1093 #define GX_VALUE_SIZE  8
1094 
1095   /* all values are FT_Short or FT_UShort entities; */
1096   /* we treat them consistently as FT_Short         */
1097 #define GX_VALUE_CASE( tag, dflt )      \
1098           case MVAR_TAG_ ## tag :       \
1099             p = (FT_Short*)&face->dflt; \
1100             break
1101 
1102 #define GX_GASP_CASE( idx )                                       \
1103           case MVAR_TAG_GASP_ ## idx :                            \
1104             if ( idx < face->gasp.numRanges - 1 )                 \
1105               p = (FT_Short*)&face->gasp.gaspRanges[idx].maxPPEM; \
1106             else                                                  \
1107               p = NULL;                                           \
1108             break
1109 
1110 
1111   static FT_Short*
1112   ft_var_get_value_pointer( TT_Face   face,
1113                             FT_ULong  mvar_tag )
1114   {
1115     FT_Short*  p;
1116 
1117 
1118     switch ( mvar_tag )
1119     {
1120       GX_GASP_CASE( 0 );
1121       GX_GASP_CASE( 1 );
1122       GX_GASP_CASE( 2 );
1123       GX_GASP_CASE( 3 );
1124       GX_GASP_CASE( 4 );
1125       GX_GASP_CASE( 5 );
1126       GX_GASP_CASE( 6 );
1127       GX_GASP_CASE( 7 );
1128       GX_GASP_CASE( 8 );
1129       GX_GASP_CASE( 9 );
1130 
1131       GX_VALUE_CASE( CPHT, os2.sCapHeight );
1132       GX_VALUE_CASE( HASC, os2.sTypoAscender );
1133       GX_VALUE_CASE( HCLA, os2.usWinAscent );
1134       GX_VALUE_CASE( HCLD, os2.usWinDescent );
1135       GX_VALUE_CASE( HCOF, horizontal.caret_Offset );
1136       GX_VALUE_CASE( HCRN, horizontal.caret_Slope_Run );
1137       GX_VALUE_CASE( HCRS, horizontal.caret_Slope_Rise );
1138       GX_VALUE_CASE( HDSC, os2.sTypoDescender );
1139       GX_VALUE_CASE( HLGP, os2.sTypoLineGap );
1140       GX_VALUE_CASE( SBXO, os2.ySubscriptXOffset);
1141       GX_VALUE_CASE( SBXS, os2.ySubscriptXSize );
1142       GX_VALUE_CASE( SBYO, os2.ySubscriptYOffset );
1143       GX_VALUE_CASE( SBYS, os2.ySubscriptYSize );
1144       GX_VALUE_CASE( SPXO, os2.ySuperscriptXOffset );
1145       GX_VALUE_CASE( SPXS, os2.ySuperscriptXSize );
1146       GX_VALUE_CASE( SPYO, os2.ySuperscriptYOffset );
1147       GX_VALUE_CASE( SPYS, os2.ySuperscriptYSize );
1148       GX_VALUE_CASE( STRO, os2.yStrikeoutPosition );
1149       GX_VALUE_CASE( STRS, os2.yStrikeoutSize );
1150       GX_VALUE_CASE( UNDO, postscript.underlinePosition );
1151       GX_VALUE_CASE( UNDS, postscript.underlineThickness );
1152       GX_VALUE_CASE( VASC, vertical.Ascender );
1153       GX_VALUE_CASE( VCOF, vertical.caret_Offset );
1154       GX_VALUE_CASE( VCRN, vertical.caret_Slope_Run );
1155       GX_VALUE_CASE( VCRS, vertical.caret_Slope_Rise );
1156       GX_VALUE_CASE( VDSC, vertical.Descender );
1157       GX_VALUE_CASE( VLGP, vertical.Line_Gap );
1158       GX_VALUE_CASE( XHGT, os2.sxHeight );
1159 
1160     default:
1161       /* ignore unknown tag */
1162       p = NULL;
1163     }
1164 
1165     return p;
1166   }
1167 
1168 
1169   /**************************************************************************
1170    *
1171    * @Function:
1172    *   ft_var_load_mvar
1173    *
1174    * @Description:
1175    *   Parse the `MVAR' table.
1176    *
1177    *   Some memory may remain allocated on error; it is always freed in
1178    *   `tt_done_blend', however.
1179    *
1180    * @InOut:
1181    *   face ::
1182    *     The font face.
1183    */
1184   static void
1185   ft_var_load_mvar( TT_Face  face )
1186   {
1187     FT_Stream  stream = FT_FACE_STREAM( face );
1188     FT_Memory  memory = stream->memory;
1189 
1190     GX_Blend         blend = face->blend;
1191     GX_ItemVarStore  itemStore;
1192     GX_Value         value, limit;
1193 
1194     FT_Error   error;
1195     FT_UShort  majorVersion;
1196     FT_ULong   table_len;
1197     FT_ULong   table_offset;
1198     FT_UShort  store_offset;
1199     FT_ULong   records_offset;
1200 
1201 
1202     FT_TRACE2(( "MVAR " ));
1203 
1204     error = face->goto_table( face, TTAG_MVAR, stream, &table_len );
1205     if ( error )
1206     {
1207       FT_TRACE2(( "is missing\n" ));
1208       return;
1209     }
1210 
1211     table_offset = FT_STREAM_POS();
1212 
1213     /* skip minor version */
1214     if ( FT_READ_USHORT( majorVersion ) ||
1215          FT_STREAM_SKIP( 2 )            )
1216       return;
1217 
1218     if ( majorVersion != 1 )
1219     {
1220       FT_TRACE2(( "bad table version %d\n", majorVersion ));
1221       return;
1222     }
1223 
1224     if ( FT_NEW( blend->mvar_table ) )
1225       return;
1226 
1227     /* skip reserved entry and value record size */
1228     if ( FT_STREAM_SKIP( 4 )                             ||
1229          FT_READ_USHORT( blend->mvar_table->valueCount ) ||
1230          FT_READ_USHORT( store_offset )                  )
1231       return;
1232 
1233     records_offset = FT_STREAM_POS();
1234 
1235     error = ft_var_load_item_variation_store(
1236               face,
1237               table_offset + store_offset,
1238               &blend->mvar_table->itemStore );
1239     if ( error )
1240       return;
1241 
1242     if ( FT_NEW_ARRAY( blend->mvar_table->values,
1243                        blend->mvar_table->valueCount ) )
1244       return;
1245 
1246     if ( FT_STREAM_SEEK( records_offset )                                ||
1247          FT_FRAME_ENTER( blend->mvar_table->valueCount * GX_VALUE_SIZE ) )
1248       return;
1249 
1250     value     = blend->mvar_table->values;
1251     limit     = value + blend->mvar_table->valueCount;
1252     itemStore = &blend->mvar_table->itemStore;
1253 
1254     for ( ; value < limit; value++ )
1255     {
1256       value->tag        = FT_GET_ULONG();
1257       value->outerIndex = FT_GET_USHORT();
1258       value->innerIndex = FT_GET_USHORT();
1259 
1260       if ( value->outerIndex >= itemStore->dataCount                  ||
1261            value->innerIndex >= itemStore->varData[value->outerIndex]
1262                                                   .itemCount          )
1263       {
1264         error = FT_THROW( Invalid_Table );
1265         break;
1266       }
1267     }
1268 
1269     FT_FRAME_EXIT();
1270 
1271     if ( error )
1272       return;
1273 
1274     FT_TRACE2(( "loaded\n" ));
1275 
1276     value = blend->mvar_table->values;
1277     limit = value + blend->mvar_table->valueCount;
1278 
1279     /* save original values of the data MVAR is going to modify */
1280     for ( ; value < limit; value++ )
1281     {
1282       FT_Short*  p = ft_var_get_value_pointer( face, value->tag );
1283 
1284 
1285       if ( p )
1286         value->unmodified = *p;
1287 #ifdef FT_DEBUG_LEVEL_TRACE
1288       else
1289         FT_TRACE1(( "ft_var_load_mvar: Ignoring unknown tag `%c%c%c%c'\n",
1290                     (FT_Char)( value->tag >> 24 ),
1291                     (FT_Char)( value->tag >> 16 ),
1292                     (FT_Char)( value->tag >> 8 ),
1293                     (FT_Char)( value->tag ) ));
1294 #endif
1295     }
1296 
1297     face->variation_support |= TT_FACE_FLAG_VAR_MVAR;
1298   }
1299 
1300 
1301   static FT_Error
1302   tt_size_reset_iterator( FT_ListNode  node,
1303                           void*        user )
1304   {
1305     TT_Size  size = (TT_Size)node->data;
1306 
1307     FT_UNUSED( user );
1308 
1309 
1310     tt_size_reset( size, 1 );
1311 
1312     return FT_Err_Ok;
1313   }
1314 
1315 
1316   /**************************************************************************
1317    *
1318    * @Function:
1319    *   tt_apply_mvar
1320    *
1321    * @Description:
1322    *   Apply `MVAR' table adjustments.
1323    *
1324    * @InOut:
1325    *   face ::
1326    *     The font face.
1327    */
1328   FT_LOCAL_DEF( void )
1329   tt_apply_mvar( TT_Face  face )
1330   {
1331     GX_Blend  blend = face->blend;
1332     GX_Value  value, limit;
1333     FT_Short  mvar_hasc_delta = 0;
1334     FT_Short  mvar_hdsc_delta = 0;
1335     FT_Short  mvar_hlgp_delta = 0;
1336 
1337 
1338     if ( !( face->variation_support & TT_FACE_FLAG_VAR_MVAR ) )
1339       return;
1340 
1341     value = blend->mvar_table->values;
1342     limit = value + blend->mvar_table->valueCount;
1343 
1344     for ( ; value < limit; value++ )
1345     {
1346       FT_Short*  p = ft_var_get_value_pointer( face, value->tag );
1347       FT_Int     delta;
1348 
1349 
1350       delta = ft_var_get_item_delta( face,
1351                                      &blend->mvar_table->itemStore,
1352                                      value->outerIndex,
1353                                      value->innerIndex );
1354 
1355       if ( p )
1356       {
1357         FT_TRACE5(( "value %c%c%c%c (%d unit%s) adjusted by %d unit%s (MVAR)\n",
1358                     (FT_Char)( value->tag >> 24 ),
1359                     (FT_Char)( value->tag >> 16 ),
1360                     (FT_Char)( value->tag >> 8 ),
1361                     (FT_Char)( value->tag ),
1362                     value->unmodified,
1363                     value->unmodified == 1 ? "" : "s",
1364                     delta,
1365                     delta == 1 ? "" : "s" ));
1366 
1367         /* since we handle both signed and unsigned values as FT_Short, */
1368         /* ensure proper overflow arithmetic                            */
1369         *p = (FT_Short)( value->unmodified + (FT_Short)delta );
1370 
1371         /* Treat hasc, hdsc and hlgp specially, see below. */
1372         if ( value->tag == MVAR_TAG_HASC )
1373           mvar_hasc_delta = (FT_Short)delta;
1374         else if ( value->tag == MVAR_TAG_HDSC )
1375           mvar_hdsc_delta = (FT_Short)delta;
1376         else if ( value->tag == MVAR_TAG_HLGP )
1377           mvar_hlgp_delta = (FT_Short)delta;
1378       }
1379     }
1380 
1381     /* adjust all derived values */
1382     {
1383       FT_Face  root = &face->root;
1384 
1385       /*
1386        * Apply the deltas of hasc, hdsc and hlgp to the FT_Face's ascender,
1387        * descender and height attributes, no matter how they were originally
1388        * computed.
1389        *
1390        * (Code that ignores those and accesses the font's metrics values
1391        * directly is already served by the delta application code above.)
1392        *
1393        * The MVAR table supports variations for both typo and win metrics.
1394        * According to Behdad Esfahbod, the thinking of the working group was
1395        * that no one uses win metrics anymore for setting line metrics (the
1396        * specification even calls these metrics "horizontal clipping
1397        * ascent/descent", probably for their role on the Windows platform in
1398        * computing clipping boxes), and new fonts should use typo metrics, so
1399        * typo deltas should be applied to whatever sfnt_load_face decided the
1400        * line metrics should be.
1401        *
1402        * Before, the following led to different line metrics between default
1403        * outline and instances, visible when e.g. the default outlines were
1404        * used as the regular face and instances for everything else:
1405        *
1406        * 1. sfnt_load_face applied the hhea metrics by default.
1407        * 2. This code later applied the typo metrics by default, regardless of
1408        *    whether they were actually changed or the font had the OS/2 table's
1409        *    fsSelection's bit 7 (USE_TYPO_METRICS) set.
1410        */
1411       FT_Short  current_line_gap = root->height - root->ascender +
1412                                    root->descender;
1413 
1414 
1415       root->ascender  = root->ascender + mvar_hasc_delta;
1416       root->descender = root->descender + mvar_hdsc_delta;
1417       root->height    = root->ascender - root->descender +
1418                         current_line_gap + mvar_hlgp_delta;
1419 
1420       root->underline_position  = face->postscript.underlinePosition -
1421                                   face->postscript.underlineThickness / 2;
1422       root->underline_thickness = face->postscript.underlineThickness;
1423 
1424       /* iterate over all FT_Size objects and call `tt_size_reset' */
1425       /* to propagate the metrics changes                          */
1426       FT_List_Iterate( &root->sizes_list,
1427                        tt_size_reset_iterator,
1428                        NULL );
1429     }
1430   }
1431 
1432 
1433   typedef struct  GX_GVar_Head_
1434   {
1435     FT_Long    version;
1436     FT_UShort  axisCount;
1437     FT_UShort  globalCoordCount;
1438     FT_ULong   offsetToCoord;
1439     FT_UShort  glyphCount;
1440     FT_UShort  flags;
1441     FT_ULong   offsetToData;
1442 
1443   } GX_GVar_Head;
1444 
1445 
1446   /**************************************************************************
1447    *
1448    * @Function:
1449    *   ft_var_load_gvar
1450    *
1451    * @Description:
1452    *   Parse the `gvar' table if present.  If `fvar' is there, `gvar' had
1453    *   better be there too.
1454    *
1455    * @InOut:
1456    *   face ::
1457    *     The font face.
1458    *
1459    * @Return:
1460    *   FreeType error code.  0 means success.
1461    */
1462   static FT_Error
1463   ft_var_load_gvar( TT_Face  face )
1464   {
1465     FT_Stream     stream = FT_FACE_STREAM( face );
1466     FT_Memory     memory = stream->memory;
1467     GX_Blend      blend  = face->blend;
1468     FT_Error      error;
1469     FT_UInt       i, j;
1470     FT_ULong      table_len;
1471     FT_ULong      gvar_start;
1472     FT_ULong      offsetToData;
1473     GX_GVar_Head  gvar_head;
1474 
1475     static const FT_Frame_Field  gvar_fields[] =
1476     {
1477 
1478 #undef  FT_STRUCTURE
1479 #define FT_STRUCTURE  GX_GVar_Head
1480 
1481       FT_FRAME_START( 20 ),
1482         FT_FRAME_LONG  ( version ),
1483         FT_FRAME_USHORT( axisCount ),
1484         FT_FRAME_USHORT( globalCoordCount ),
1485         FT_FRAME_ULONG ( offsetToCoord ),
1486         FT_FRAME_USHORT( glyphCount ),
1487         FT_FRAME_USHORT( flags ),
1488         FT_FRAME_ULONG ( offsetToData ),
1489       FT_FRAME_END
1490     };
1491 
1492 
1493     FT_TRACE2(( "GVAR " ));
1494 
1495     if ( FT_SET_ERROR( face->goto_table( face,
1496                                          TTAG_gvar,
1497                                          stream,
1498                                          &table_len ) ) )
1499     {
1500       FT_TRACE2(( "is missing\n" ));
1501       goto Exit;
1502     }
1503 
1504     gvar_start = FT_STREAM_POS( );
1505     if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) )
1506       goto Exit;
1507 
1508     if ( gvar_head.version != 0x00010000L )
1509     {
1510       FT_TRACE1(( "bad table version\n" ));
1511       error = FT_THROW( Invalid_Table );
1512       goto Exit;
1513     }
1514 
1515     if ( gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
1516     {
1517       FT_TRACE1(( "ft_var_load_gvar: number of axes in `gvar' and `cvar'\n"
1518                   "                  table are different\n" ));
1519       error = FT_THROW( Invalid_Table );
1520       goto Exit;
1521     }
1522 
1523     /* rough sanity check, ignoring offsets */
1524     if ( (FT_ULong)gvar_head.globalCoordCount * gvar_head.axisCount >
1525            table_len / 2 )
1526     {
1527       FT_TRACE1(( "ft_var_load_gvar:"
1528                   " invalid number of global coordinates\n" ));
1529       error = FT_THROW( Invalid_Table );
1530       goto Exit;
1531     }
1532 
1533     /* rough sanity check: offsets can be either 2 or 4 bytes */
1534     if ( (FT_ULong)gvar_head.glyphCount *
1535            ( ( gvar_head.flags & 1 ) ? 4 : 2 ) > table_len )
1536     {
1537       FT_TRACE1(( "ft_var_load_gvar: invalid number of glyphs\n" ));
1538       error = FT_THROW( Invalid_Table );
1539       goto Exit;
1540     }
1541 
1542     FT_TRACE2(( "loaded\n" ));
1543 
1544     blend->gvar_size   = table_len;
1545     blend->tuplecount  = gvar_head.globalCoordCount;
1546     blend->gv_glyphcnt = gvar_head.glyphCount;
1547     offsetToData       = gvar_start + gvar_head.offsetToData;
1548 
1549     FT_TRACE5(( "gvar: there %s %d shared coordinate%s:\n",
1550                 blend->tuplecount == 1 ? "is" : "are",
1551                 blend->tuplecount,
1552                 blend->tuplecount == 1 ? "" : "s" ));
1553 
1554     if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) )
1555       goto Exit;
1556 
1557     if ( gvar_head.flags & 1 )
1558     {
1559       FT_ULong  limit = gvar_start + table_len;
1560 
1561 
1562       /* long offsets (one more offset than glyphs, to mark size of last) */
1563       if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) )
1564         goto Exit;
1565 
1566       for ( i = 0; i <= blend->gv_glyphcnt; i++ )
1567       {
1568         blend->glyphoffsets[i] = offsetToData + FT_GET_ULONG();
1569         /* use `>', not `>=' */
1570         if ( blend->glyphoffsets[i] > limit )
1571         {
1572           FT_TRACE2(( "ft_var_load_gvar:"
1573                       " invalid glyph variation data offset for index %d\n",
1574                       i ));
1575           error = FT_THROW( Invalid_Table );
1576           break;
1577         }
1578       }
1579     }
1580     else
1581     {
1582       FT_ULong  limit = gvar_start + table_len;
1583 
1584 
1585       /* short offsets (one more offset than glyphs, to mark size of last) */
1586       if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) )
1587         goto Exit;
1588 
1589       for ( i = 0; i <= blend->gv_glyphcnt; i++ )
1590       {
1591         blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
1592         /* use `>', not `>=' */
1593         if ( blend->glyphoffsets[i] > limit )
1594         {
1595           FT_TRACE2(( "ft_var_load_gvar:"
1596                       " invalid glyph variation data offset for index %d\n",
1597                       i ));
1598           error = FT_THROW( Invalid_Table );
1599           break;
1600         }
1601       }
1602     }
1603 
1604     FT_FRAME_EXIT();
1605     if ( error )
1606       goto Exit;
1607 
1608     if ( blend->tuplecount != 0 )
1609     {
1610       if ( FT_NEW_ARRAY( blend->tuplecoords,
1611                          gvar_head.axisCount * blend->tuplecount ) )
1612         goto Exit;
1613 
1614       if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord )         ||
1615            FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L ) )
1616         goto Exit;
1617 
1618       for ( i = 0; i < blend->tuplecount; i++ )
1619       {
1620         FT_TRACE5(( "  [ " ));
1621         for ( j = 0; j < (FT_UInt)gvar_head.axisCount; j++ )
1622         {
1623           blend->tuplecoords[i * gvar_head.axisCount + j] =
1624             FT_fdot14ToFixed( FT_GET_SHORT() );
1625           FT_TRACE5(( "%.5f ",
1626             blend->tuplecoords[i * gvar_head.axisCount + j] / 65536.0 ));
1627         }
1628         FT_TRACE5(( "]\n" ));
1629       }
1630 
1631       FT_TRACE5(( "\n" ));
1632 
1633       FT_FRAME_EXIT();
1634     }
1635 
1636   Exit:
1637     return error;
1638   }
1639 
1640 
1641   /**************************************************************************
1642    *
1643    * @Function:
1644    *   ft_var_apply_tuple
1645    *
1646    * @Description:
1647    *   Figure out whether a given tuple (design) applies to the current
1648    *   blend, and if so, what is the scaling factor.
1649    *
1650    * @Input:
1651    *   blend ::
1652    *     The current blend of the font.
1653    *
1654    *   tupleIndex ::
1655    *     A flag saying whether this is an intermediate
1656    *     tuple or not.
1657    *
1658    *   tuple_coords ::
1659    *     The coordinates of the tuple in normalized axis
1660    *     units.
1661    *
1662    *   im_start_coords ::
1663    *     The initial coordinates where this tuple starts
1664    *     to apply (for intermediate coordinates).
1665    *
1666    *   im_end_coords ::
1667    *     The final coordinates after which this tuple no
1668    *     longer applies (for intermediate coordinates).
1669    *
1670    * @Return:
1671    *   An FT_Fixed value containing the scaling factor.
1672    */
1673   static FT_Fixed
1674   ft_var_apply_tuple( GX_Blend   blend,
1675                       FT_UShort  tupleIndex,
1676                       FT_Fixed*  tuple_coords,
1677                       FT_Fixed*  im_start_coords,
1678                       FT_Fixed*  im_end_coords )
1679   {
1680     FT_UInt   i;
1681     FT_Fixed  apply = 0x10000L;
1682 
1683 
1684     for ( i = 0; i < blend->num_axis; i++ )
1685     {
1686       FT_TRACE6(( "    axis %d coordinate %.5f:\n",
1687                   i, blend->normalizedcoords[i] / 65536.0 ));
1688 
1689       /* It's not clear why (for intermediate tuples) we don't need     */
1690       /* to check against start/end -- the documentation says we don't. */
1691       /* Similarly, it's unclear why we don't need to scale along the   */
1692       /* axis.                                                          */
1693 
1694       if ( tuple_coords[i] == 0 )
1695       {
1696         FT_TRACE6(( "      tuple coordinate is zero, ignore\n", i ));
1697         continue;
1698       }
1699 
1700       if ( blend->normalizedcoords[i] == 0 )
1701       {
1702         FT_TRACE6(( "      axis coordinate is zero, stop\n" ));
1703         apply = 0;
1704         break;
1705       }
1706 
1707       if ( blend->normalizedcoords[i] == tuple_coords[i] )
1708       {
1709         FT_TRACE6(( "      tuple coordinate %.5f fits perfectly\n",
1710                     tuple_coords[i] / 65536.0 ));
1711         /* `apply' does not change */
1712         continue;
1713       }
1714 
1715       if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
1716       {
1717         /* not an intermediate tuple */
1718 
1719         if ( blend->normalizedcoords[i] < FT_MIN( 0, tuple_coords[i] ) ||
1720              blend->normalizedcoords[i] > FT_MAX( 0, tuple_coords[i] ) )
1721         {
1722           FT_TRACE6(( "      tuple coordinate %.5f is exceeded, stop\n",
1723                       tuple_coords[i] / 65536.0 ));
1724           apply = 0;
1725           break;
1726         }
1727 
1728         FT_TRACE6(( "      tuple coordinate %.5f fits\n",
1729                     tuple_coords[i] / 65536.0 ));
1730         apply = FT_MulDiv( apply,
1731                            blend->normalizedcoords[i],
1732                            tuple_coords[i] );
1733       }
1734       else
1735       {
1736         /* intermediate tuple */
1737 
1738         if ( blend->normalizedcoords[i] <= im_start_coords[i] ||
1739              blend->normalizedcoords[i] >= im_end_coords[i]   )
1740         {
1741           FT_TRACE6(( "      intermediate tuple range ]%.5f;%.5f[ is exceeded,"
1742                       " stop\n",
1743                       im_start_coords[i] / 65536.0,
1744                       im_end_coords[i] / 65536.0 ));
1745           apply = 0;
1746           break;
1747         }
1748 
1749         FT_TRACE6(( "      intermediate tuple range ]%.5f;%.5f[ fits\n",
1750                     im_start_coords[i] / 65536.0,
1751                     im_end_coords[i] / 65536.0 ));
1752         if ( blend->normalizedcoords[i] < tuple_coords[i] )
1753           apply = FT_MulDiv( apply,
1754                              blend->normalizedcoords[i] - im_start_coords[i],
1755                              tuple_coords[i] - im_start_coords[i] );
1756         else
1757           apply = FT_MulDiv( apply,
1758                              im_end_coords[i] - blend->normalizedcoords[i],
1759                              im_end_coords[i] - tuple_coords[i] );
1760       }
1761     }
1762 
1763     FT_TRACE6(( "    apply factor is %.5f\n", apply / 65536.0 ));
1764 
1765     return apply;
1766   }
1767 
1768 
1769   /* convert from design coordinates to normalized coordinates */
1770 
1771   static void
1772   ft_var_to_normalized( TT_Face    face,
1773                         FT_UInt    num_coords,
1774                         FT_Fixed*  coords,
1775                         FT_Fixed*  normalized )
1776   {
1777     GX_Blend        blend;
1778     FT_MM_Var*      mmvar;
1779     FT_UInt         i, j;
1780     FT_Var_Axis*    a;
1781     GX_AVarSegment  av;
1782 
1783 
1784     blend = face->blend;
1785     mmvar = blend->mmvar;
1786 
1787     if ( num_coords > mmvar->num_axis )
1788     {
1789       FT_TRACE2(( "ft_var_to_normalized:"
1790                   " only using first %d of %d coordinates\n",
1791                   mmvar->num_axis, num_coords ));
1792       num_coords = mmvar->num_axis;
1793     }
1794 
1795     /* Axis normalization is a two-stage process.  First we normalize */
1796     /* based on the [min,def,max] values for the axis to be [-1,0,1]. */
1797     /* Then, if there's an `avar' table, we renormalize this range.   */
1798 
1799     a = mmvar->axis;
1800     for ( i = 0; i < num_coords; i++, a++ )
1801     {
1802       FT_Fixed  coord = coords[i];
1803 
1804 
1805       FT_TRACE5(( "    %d: %.5f\n", i, coord / 65536.0 ));
1806       if ( coord > a->maximum || coord < a->minimum )
1807       {
1808         FT_TRACE1((
1809           "ft_var_to_normalized: design coordinate %.5f\n"
1810           "                      is out of range [%.5f;%.5f]; clamping\n",
1811           coord / 65536.0,
1812           a->minimum / 65536.0,
1813           a->maximum / 65536.0 ));
1814 
1815         if ( coord > a->maximum )
1816           coord = a->maximum;
1817         else
1818           coord = a->minimum;
1819       }
1820 
1821       if ( coord < a->def )
1822         normalized[i] = -FT_DivFix( SUB_LONG( coord, a->def ),
1823                                     SUB_LONG( a->minimum, a->def ) );
1824       else if ( coord > a->def )
1825         normalized[i] = FT_DivFix( SUB_LONG( coord, a->def ),
1826                                    SUB_LONG( a->maximum, a->def ) );
1827       else
1828         normalized[i] = 0;
1829     }
1830 
1831     FT_TRACE5(( "\n" ));
1832 
1833     for ( ; i < mmvar->num_axis; i++ )
1834       normalized[i] = 0;
1835 
1836     if ( blend->avar_segment )
1837     {
1838       FT_TRACE5(( "normalized design coordinates"
1839                   " before applying `avar' data:\n" ));
1840 
1841       av = blend->avar_segment;
1842       for ( i = 0; i < mmvar->num_axis; i++, av++ )
1843       {
1844         for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
1845         {
1846           if ( normalized[i] < av->correspondence[j].fromCoord )
1847           {
1848             FT_TRACE5(( "  %.5f\n", normalized[i] / 65536.0 ));
1849 
1850             normalized[i] =
1851               FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord,
1852                          av->correspondence[j].toCoord -
1853                            av->correspondence[j - 1].toCoord,
1854                          av->correspondence[j].fromCoord -
1855                            av->correspondence[j - 1].fromCoord ) +
1856               av->correspondence[j - 1].toCoord;
1857             break;
1858           }
1859         }
1860       }
1861     }
1862   }
1863 
1864 
1865   /* convert from normalized coordinates to design coordinates */
1866 
1867   static void
1868   ft_var_to_design( TT_Face    face,
1869                     FT_UInt    num_coords,
1870                     FT_Fixed*  coords,
1871                     FT_Fixed*  design )
1872   {
1873     GX_Blend      blend;
1874     FT_MM_Var*    mmvar;
1875     FT_Var_Axis*  a;
1876 
1877     FT_UInt  i, j, nc;
1878 
1879 
1880     blend = face->blend;
1881 
1882     nc = num_coords;
1883     if ( num_coords > blend->num_axis )
1884     {
1885       FT_TRACE2(( "ft_var_to_design:"
1886                   " only using first %d of %d coordinates\n",
1887                   blend->num_axis, num_coords ));
1888       nc = blend->num_axis;
1889     }
1890 
1891     for ( i = 0; i < nc; i++ )
1892       design[i] = coords[i];
1893 
1894     for ( ; i < num_coords; i++ )
1895       design[i] = 0;
1896 
1897     if ( blend->avar_segment )
1898     {
1899       GX_AVarSegment  av = blend->avar_segment;
1900 
1901 
1902       FT_TRACE5(( "design coordinates"
1903                   " after removing `avar' distortion:\n" ));
1904 
1905       for ( i = 0; i < nc; i++, av++ )
1906       {
1907         for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
1908         {
1909           if ( design[i] < av->correspondence[j].toCoord )
1910           {
1911             design[i] =
1912               FT_MulDiv( design[i] - av->correspondence[j - 1].toCoord,
1913                          av->correspondence[j].fromCoord -
1914                            av->correspondence[j - 1].fromCoord,
1915                          av->correspondence[j].toCoord -
1916                            av->correspondence[j - 1].toCoord ) +
1917               av->correspondence[j - 1].fromCoord;
1918 
1919             FT_TRACE5(( "  %.5f\n", design[i] / 65536.0 ));
1920             break;
1921           }
1922         }
1923       }
1924     }
1925 
1926     mmvar = blend->mmvar;
1927     a     = mmvar->axis;
1928 
1929     for ( i = 0; i < nc; i++, a++ )
1930     {
1931       if ( design[i] < 0 )
1932         design[i] = a->def + FT_MulFix( design[i],
1933                                         a->def - a->minimum );
1934       else if ( design[i] > 0 )
1935         design[i] = a->def + FT_MulFix( design[i],
1936                                         a->maximum - a->def );
1937       else
1938         design[i] = a->def;
1939     }
1940   }
1941 
1942 
1943   /*************************************************************************/
1944   /*************************************************************************/
1945   /*****                                                               *****/
1946   /*****               MULTIPLE MASTERS SERVICE FUNCTIONS              *****/
1947   /*****                                                               *****/
1948   /*************************************************************************/
1949   /*************************************************************************/
1950 
1951 
1952   typedef struct  GX_FVar_Head_
1953   {
1954     FT_Long    version;
1955     FT_UShort  offsetToData;
1956     FT_UShort  axisCount;
1957     FT_UShort  axisSize;
1958     FT_UShort  instanceCount;
1959     FT_UShort  instanceSize;
1960 
1961   } GX_FVar_Head;
1962 
1963 
1964   typedef struct  fvar_axis_
1965   {
1966     FT_ULong   axisTag;
1967     FT_Fixed   minValue;
1968     FT_Fixed   defaultValue;
1969     FT_Fixed   maxValue;
1970     FT_UShort  flags;
1971     FT_UShort  nameID;
1972 
1973   } GX_FVar_Axis;
1974 
1975 
1976   /**************************************************************************
1977    *
1978    * @Function:
1979    *   TT_Get_MM_Var
1980    *
1981    * @Description:
1982    *   Check that the font's `fvar' table is valid, parse it, and return
1983    *   those data.  It also loads (and parses) the `MVAR' table, if
1984    *   possible.
1985    *
1986    * @InOut:
1987    *   face ::
1988    *     The font face.
1989    *     TT_Get_MM_Var initializes the blend structure.
1990    *
1991    * @Output:
1992    *   master ::
1993    *     The `fvar' data (must be freed by caller).  Can be NULL,
1994    *     which makes this function simply load MM support.
1995    *
1996    * @Return:
1997    *   FreeType error code.  0 means success.
1998    */
1999   FT_LOCAL_DEF( FT_Error )
2000   TT_Get_MM_Var( TT_Face      face,
2001                  FT_MM_Var*  *master )
2002   {
2003     FT_Stream            stream     = face->root.stream;
2004     FT_Memory            memory     = face->root.memory;
2005     FT_ULong             table_len;
2006     FT_Error             error      = FT_Err_Ok;
2007     FT_ULong             fvar_start = 0;
2008     FT_UInt              i, j;
2009     FT_MM_Var*           mmvar = NULL;
2010     FT_Fixed*            next_coords;
2011     FT_Fixed*            nsc;
2012     FT_String*           next_name;
2013     FT_Var_Axis*         a;
2014     FT_Fixed*            c;
2015     FT_Var_Named_Style*  ns;
2016     GX_FVar_Head         fvar_head;
2017     FT_Bool              usePsName  = 0;
2018     FT_UInt              num_instances;
2019     FT_UInt              num_axes;
2020     FT_UShort*           axis_flags;
2021 
2022     FT_Offset  mmvar_size;
2023     FT_Offset  axis_flags_size;
2024     FT_Offset  axis_size;
2025     FT_Offset  namedstyle_size;
2026     FT_Offset  next_coords_size;
2027     FT_Offset  next_name_size;
2028 
2029     FT_Bool  need_init;
2030 
2031     static const FT_Frame_Field  fvar_fields[] =
2032     {
2033 
2034 #undef  FT_STRUCTURE
2035 #define FT_STRUCTURE  GX_FVar_Head
2036 
2037       FT_FRAME_START( 16 ),
2038         FT_FRAME_LONG      ( version ),
2039         FT_FRAME_USHORT    ( offsetToData ),
2040         FT_FRAME_SKIP_SHORT,
2041         FT_FRAME_USHORT    ( axisCount ),
2042         FT_FRAME_USHORT    ( axisSize ),
2043         FT_FRAME_USHORT    ( instanceCount ),
2044         FT_FRAME_USHORT    ( instanceSize ),
2045       FT_FRAME_END
2046     };
2047 
2048     static const FT_Frame_Field  fvaraxis_fields[] =
2049     {
2050 
2051 #undef  FT_STRUCTURE
2052 #define FT_STRUCTURE  GX_FVar_Axis
2053 
2054       FT_FRAME_START( 20 ),
2055         FT_FRAME_ULONG ( axisTag ),
2056         FT_FRAME_LONG  ( minValue ),
2057         FT_FRAME_LONG  ( defaultValue ),
2058         FT_FRAME_LONG  ( maxValue ),
2059         FT_FRAME_USHORT( flags ),
2060         FT_FRAME_USHORT( nameID ),
2061       FT_FRAME_END
2062     };
2063 
2064 
2065     /* read the font data and set up the internal representation */
2066     /* if not already done                                       */
2067 
2068     need_init = !face->blend;
2069 
2070     if ( need_init )
2071     {
2072       FT_TRACE2(( "FVAR " ));
2073 
2074       /* both `fvar' and `gvar' must be present */
2075       if ( FT_SET_ERROR( face->goto_table( face, TTAG_gvar,
2076                                            stream, &table_len ) ) )
2077       {
2078         /* CFF2 is an alternate to gvar here */
2079         if ( FT_SET_ERROR( face->goto_table( face, TTAG_CFF2,
2080                                              stream, &table_len ) ) )
2081         {
2082           FT_TRACE1(( "\n"
2083                       "TT_Get_MM_Var: `gvar' or `CFF2' table is missing\n" ));
2084           goto Exit;
2085         }
2086       }
2087 
2088       if ( FT_SET_ERROR( face->goto_table( face, TTAG_fvar,
2089                                            stream, &table_len ) ) )
2090       {
2091         FT_TRACE1(( "is missing\n" ));
2092         goto Exit;
2093       }
2094 
2095       fvar_start = FT_STREAM_POS( );
2096 
2097       /* the validity of the `fvar' header data was already checked */
2098       /* in function `sfnt_init_face'                               */
2099       if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
2100         goto Exit;
2101 
2102       usePsName = FT_BOOL( fvar_head.instanceSize ==
2103                            6 + 4 * fvar_head.axisCount );
2104 
2105       FT_TRACE2(( "loaded\n" ));
2106 
2107       FT_TRACE5(( "%d variation ax%s\n",
2108                   fvar_head.axisCount,
2109                   fvar_head.axisCount == 1 ? "is" : "es" ));
2110 
2111       if ( FT_NEW( face->blend ) )
2112         goto Exit;
2113 
2114       num_axes              = fvar_head.axisCount;
2115       face->blend->num_axis = num_axes;
2116     }
2117     else
2118       num_axes = face->blend->num_axis;
2119 
2120     /* `num_instances' holds the number of all named instances, */
2121     /* including the default instance which might be missing    */
2122     /* in fvar's table of named instances                       */
2123     num_instances = (FT_UInt)face->root.style_flags >> 16;
2124 
2125     /* prepare storage area for MM data; this cannot overflow   */
2126     /* 32-bit arithmetic because of the size limits used in the */
2127     /* `fvar' table validity check in `sfnt_init_face'          */
2128 
2129     /* the various `*_size' variables, which we also use as     */
2130     /* offsets into the `mmlen' array, must be multiples of the */
2131     /* pointer size (except the last one); without such an      */
2132     /* alignment there might be runtime errors due to           */
2133     /* misaligned addresses                                     */
2134 #undef  ALIGN_SIZE
2135 #define ALIGN_SIZE( n ) \
2136           ( ( (n) + sizeof (void*) - 1 ) & ~( sizeof (void*) - 1 ) )
2137 
2138     mmvar_size       = ALIGN_SIZE( sizeof ( FT_MM_Var ) );
2139     axis_flags_size  = ALIGN_SIZE( num_axes *
2140                                    sizeof ( FT_UShort ) );
2141     axis_size        = ALIGN_SIZE( num_axes *
2142                                    sizeof ( FT_Var_Axis ) );
2143     namedstyle_size  = ALIGN_SIZE( num_instances *
2144                                    sizeof ( FT_Var_Named_Style ) );
2145     next_coords_size = ALIGN_SIZE( num_instances *
2146                                    num_axes *
2147                                    sizeof ( FT_Fixed ) );
2148     next_name_size   = num_axes * 5;
2149 
2150     if ( need_init )
2151     {
2152       face->blend->mmvar_len = mmvar_size       +
2153                                axis_flags_size  +
2154                                axis_size        +
2155                                namedstyle_size  +
2156                                next_coords_size +
2157                                next_name_size;
2158 
2159       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
2160         goto Exit;
2161       face->blend->mmvar = mmvar;
2162 
2163       /* set up pointers and offsets into the `mmvar' array; */
2164       /* the data gets filled in later on                    */
2165 
2166       mmvar->num_axis =
2167         num_axes;
2168       mmvar->num_designs =
2169         ~0U;                   /* meaningless in this context; each glyph */
2170                                /* may have a different number of designs  */
2171                                /* (or tuples, as called by Apple)         */
2172       mmvar->num_namedstyles =
2173         num_instances;
2174 
2175       /* alas, no public field in `FT_Var_Axis' for axis flags */
2176       axis_flags =
2177         (FT_UShort*)( (char*)mmvar + mmvar_size );
2178       mmvar->axis =
2179         (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
2180       mmvar->namedstyle =
2181         (FT_Var_Named_Style*)( (char*)mmvar->axis + axis_size );
2182 
2183       next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
2184                                  namedstyle_size );
2185       for ( i = 0; i < num_instances; i++ )
2186       {
2187         mmvar->namedstyle[i].coords  = next_coords;
2188         next_coords                 += num_axes;
2189       }
2190 
2191       next_name = (FT_String*)( (char*)mmvar->namedstyle +
2192                                 namedstyle_size + next_coords_size );
2193       for ( i = 0; i < num_axes; i++ )
2194       {
2195         mmvar->axis[i].name  = next_name;
2196         next_name           += 5;
2197       }
2198 
2199       /* now fill in the data */
2200 
2201       if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
2202         goto Exit;
2203 
2204       a = mmvar->axis;
2205       for ( i = 0; i < num_axes; i++ )
2206       {
2207         GX_FVar_Axis  axis_rec;
2208 
2209 #ifdef FT_DEBUG_LEVEL_TRACE
2210         int  invalid = 0;
2211 #endif
2212 
2213 
2214         if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
2215           goto Exit;
2216         a->tag     = axis_rec.axisTag;
2217         a->minimum = axis_rec.minValue;
2218         a->def     = axis_rec.defaultValue;
2219         a->maximum = axis_rec.maxValue;
2220         a->strid   = axis_rec.nameID;
2221 
2222         a->name[0] = (FT_String)(   a->tag >> 24 );
2223         a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
2224         a->name[2] = (FT_String)( ( a->tag >>  8 ) & 0xFF );
2225         a->name[3] = (FT_String)( ( a->tag       ) & 0xFF );
2226         a->name[4] = '\0';
2227 
2228         *axis_flags = axis_rec.flags;
2229 
2230         if ( a->minimum > a->def ||
2231              a->def > a->maximum )
2232         {
2233           a->minimum = a->def;
2234           a->maximum = a->def;
2235 
2236 #ifdef FT_DEBUG_LEVEL_TRACE
2237           invalid = 1;
2238 #endif
2239         }
2240 
2241 #ifdef FT_DEBUG_LEVEL_TRACE
2242         if ( i == 0 )
2243           FT_TRACE5(( "  idx   tag  "
2244                    /* "  XXX  `XXXX'" */
2245                       "    minimum     default     maximum   flags\n" ));
2246                    /* "  XXXX.XXXXX  XXXX.XXXXX  XXXX.XXXXX  0xXXXX" */
2247 
2248         FT_TRACE5(( "  %3d  `%s'"
2249                     "  %10.5f  %10.5f  %10.5f  0x%04X%s\n",
2250                     i,
2251                     a->name,
2252                     a->minimum / 65536.0,
2253                     a->def / 65536.0,
2254                     a->maximum / 65536.0,
2255                     *axis_flags,
2256                     invalid ? " (invalid, disabled)" : "" ));
2257 #endif
2258 
2259         a++;
2260         axis_flags++;
2261       }
2262 
2263       FT_TRACE5(( "\n" ));
2264 
2265       /* named instance coordinates are stored as design coordinates; */
2266       /* we have to convert them to normalized coordinates also       */
2267       if ( FT_NEW_ARRAY( face->blend->normalized_stylecoords,
2268                          num_axes * num_instances ) )
2269         goto Exit;
2270 
2271       if ( fvar_head.instanceCount && !face->blend->avar_loaded )
2272       {
2273         FT_ULong  offset = FT_STREAM_POS();
2274 
2275 
2276         ft_var_load_avar( face );
2277 
2278         if ( FT_STREAM_SEEK( offset ) )
2279           goto Exit;
2280       }
2281 
2282       FT_TRACE5(( "%d instance%s\n",
2283                   fvar_head.instanceCount,
2284                   fvar_head.instanceCount == 1 ? "" : "s" ));
2285 
2286       ns  = mmvar->namedstyle;
2287       nsc = face->blend->normalized_stylecoords;
2288       for ( i = 0; i < fvar_head.instanceCount; i++, ns++ )
2289       {
2290         /* PostScript names add 2 bytes to the instance record size */
2291         if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) +
2292                              4L * num_axes ) )
2293           goto Exit;
2294 
2295         ns->strid       =    FT_GET_USHORT();
2296         (void) /* flags = */ FT_GET_USHORT();
2297 
2298         c = ns->coords;
2299         for ( j = 0; j < num_axes; j++, c++ )
2300           *c = FT_GET_LONG();
2301 
2302         /* valid psid values are 6, [256;32767], and 0xFFFF */
2303         if ( usePsName )
2304           ns->psid = FT_GET_USHORT();
2305         else
2306           ns->psid = 0xFFFF;
2307 
2308 #ifdef FT_DEBUG_LEVEL_TRACE
2309         {
2310           SFNT_Service  sfnt = (SFNT_Service)face->sfnt;
2311 
2312           FT_String*  strname = NULL;
2313           FT_String*  psname  = NULL;
2314 
2315           FT_ULong  pos;
2316 
2317 
2318           pos = FT_STREAM_POS();
2319 
2320           if ( ns->strid != 0xFFFF )
2321           {
2322             (void)sfnt->get_name( face,
2323                                   (FT_UShort)ns->strid,
2324                                   &strname );
2325             if ( strname && !ft_strcmp( strname, ".notdef" ) )
2326               strname = NULL;
2327           }
2328 
2329           if ( ns->psid != 0xFFFF )
2330           {
2331             (void)sfnt->get_name( face,
2332                                   (FT_UShort)ns->psid,
2333                                   &psname );
2334             if ( psname && !ft_strcmp( psname, ".notdef" ) )
2335               psname = NULL;
2336           }
2337 
2338           (void)FT_STREAM_SEEK( pos );
2339 
2340           FT_TRACE5(( "  instance %d (%s%s%s, %s%s%s)\n",
2341                       i,
2342                       strname ? "name: `" : "",
2343                       strname ? strname : "unnamed",
2344                       strname ? "'" : "",
2345                       psname ? "PS name: `" : "",
2346                       psname ? psname : "no PS name",
2347                       psname ? "'" : "" ));
2348 
2349           FT_FREE( strname );
2350           FT_FREE( psname );
2351         }
2352 #endif /* FT_DEBUG_LEVEL_TRACE */
2353 
2354         ft_var_to_normalized( face, num_axes, ns->coords, nsc );
2355         nsc += num_axes;
2356 
2357         FT_FRAME_EXIT();
2358       }
2359 
2360       if ( num_instances != fvar_head.instanceCount )
2361       {
2362         SFNT_Service  sfnt = (SFNT_Service)face->sfnt;
2363 
2364         FT_Int   found, dummy1, dummy2;
2365         FT_UInt  strid = ~0U;
2366 
2367 
2368         /* the default instance is missing in array the   */
2369         /* of named instances; try to synthesize an entry */
2370         found = sfnt->get_name_id( face,
2371                                    TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY,
2372                                    &dummy1,
2373                                    &dummy2 );
2374         if ( found )
2375           strid = TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY;
2376         else
2377         {
2378           found = sfnt->get_name_id( face,
2379                                      TT_NAME_ID_FONT_SUBFAMILY,
2380                                      &dummy1,
2381                                      &dummy2 );
2382           if ( found )
2383             strid = TT_NAME_ID_FONT_SUBFAMILY;
2384         }
2385 
2386         if ( found )
2387         {
2388           found = sfnt->get_name_id( face,
2389                                      TT_NAME_ID_PS_NAME,
2390                                      &dummy1,
2391                                      &dummy2 );
2392           if ( found )
2393           {
2394             FT_TRACE5(( "TT_Get_MM_Var:"
2395                         " Adding default instance to named instances\n" ));
2396 
2397             ns = &mmvar->namedstyle[fvar_head.instanceCount];
2398 
2399             ns->strid = strid;
2400             ns->psid  = TT_NAME_ID_PS_NAME;
2401 
2402             a = mmvar->axis;
2403             c = ns->coords;
2404             for ( j = 0; j < num_axes; j++, a++, c++ )
2405               *c = a->def;
2406           }
2407         }
2408       }
2409 
2410       ft_var_load_mvar( face );
2411     }
2412 
2413     /* fill the output array if requested */
2414 
2415     if ( master )
2416     {
2417       FT_UInt  n;
2418 
2419 
2420       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
2421         goto Exit;
2422       FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
2423 
2424       axis_flags =
2425         (FT_UShort*)( (char*)mmvar + mmvar_size );
2426       mmvar->axis =
2427         (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
2428       mmvar->namedstyle =
2429         (FT_Var_Named_Style*)( (char*)mmvar->axis+ axis_size );
2430 
2431       next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
2432                                  namedstyle_size );
2433       for ( n = 0; n < mmvar->num_namedstyles; n++ )
2434       {
2435         mmvar->namedstyle[n].coords  = next_coords;
2436         next_coords                 += num_axes;
2437       }
2438 
2439       a         = mmvar->axis;
2440       next_name = (FT_String*)( (char*)mmvar->namedstyle +
2441                                 namedstyle_size + next_coords_size );
2442       for ( n = 0; n < num_axes; n++ )
2443       {
2444         a->name = next_name;
2445 
2446         /* standard PostScript names for some standard apple tags */
2447         if ( a->tag == TTAG_wght )
2448           a->name = (char*)"Weight";
2449         else if ( a->tag == TTAG_wdth )
2450           a->name = (char*)"Width";
2451         else if ( a->tag == TTAG_opsz )
2452           a->name = (char*)"OpticalSize";
2453         else if ( a->tag == TTAG_slnt )
2454           a->name = (char*)"Slant";
2455 
2456         next_name += 5;
2457         a++;
2458       }
2459 
2460       *master = mmvar;
2461     }
2462 
2463   Exit:
2464     return error;
2465   }
2466 
2467 
2468   static FT_Error
2469   tt_set_mm_blend( TT_Face    face,
2470                    FT_UInt    num_coords,
2471                    FT_Fixed*  coords,
2472                    FT_Bool    set_design_coords )
2473   {
2474     FT_Error    error = FT_Err_Ok;
2475     GX_Blend    blend;
2476     FT_MM_Var*  mmvar;
2477     FT_UInt     i;
2478 
2479     FT_Bool     all_design_coords = FALSE;
2480 
2481     FT_Memory   memory = face->root.memory;
2482 
2483     enum
2484     {
2485       mcvt_retain,
2486       mcvt_modify,
2487       mcvt_load
2488 
2489     } manageCvt;
2490 
2491 
2492     face->doblend = FALSE;
2493 
2494     if ( !face->blend )
2495     {
2496       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2497         goto Exit;
2498     }
2499 
2500     blend = face->blend;
2501     mmvar = blend->mmvar;
2502 
2503     if ( num_coords > mmvar->num_axis )
2504     {
2505       FT_TRACE2(( "TT_Set_MM_Blend:"
2506                   " only using first %d of %d coordinates\n",
2507                   mmvar->num_axis, num_coords ));
2508       num_coords = mmvar->num_axis;
2509     }
2510 
2511     FT_TRACE5(( "TT_Set_MM_Blend:\n"
2512                 "  normalized design coordinates:\n" ));
2513 
2514     for ( i = 0; i < num_coords; i++ )
2515     {
2516       FT_TRACE5(( "    %.5f\n", coords[i] / 65536.0 ));
2517       if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
2518       {
2519         FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n"
2520                     "                 is out of range [-1;1]\n",
2521                     coords[i] / 65536.0 ));
2522         error = FT_THROW( Invalid_Argument );
2523         goto Exit;
2524       }
2525     }
2526 
2527     FT_TRACE5(( "\n" ));
2528 
2529     if ( !face->is_cff2 && !blend->glyphoffsets )
2530       if ( FT_SET_ERROR( ft_var_load_gvar( face ) ) )
2531         goto Exit;
2532 
2533     if ( !blend->coords )
2534     {
2535       if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
2536         goto Exit;
2537 
2538       /* the first time we have to compute all design coordinates */
2539       all_design_coords = TRUE;
2540     }
2541 
2542     if ( !blend->normalizedcoords )
2543     {
2544       if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) )
2545         goto Exit;
2546 
2547       manageCvt = mcvt_modify;
2548 
2549       /* If we have not set the blend coordinates before this, then the  */
2550       /* cvt table will still be what we read from the `cvt ' table and  */
2551       /* we don't need to reload it.  We may need to change it though... */
2552     }
2553     else
2554     {
2555       FT_Bool    have_diff = 0;
2556       FT_UInt    j;
2557       FT_Fixed*  c;
2558       FT_Fixed*  n;
2559 
2560 
2561       manageCvt = mcvt_retain;
2562 
2563       for ( i = 0; i < num_coords; i++ )
2564       {
2565         if ( blend->normalizedcoords[i] != coords[i] )
2566         {
2567           manageCvt = mcvt_load;
2568           have_diff = 1;
2569           break;
2570         }
2571       }
2572 
2573       if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2574       {
2575         FT_UInt  instance_index = (FT_UInt)face->root.face_index >> 16;
2576 
2577 
2578         c = blend->normalizedcoords + i;
2579         n = blend->normalized_stylecoords            +
2580             ( instance_index - 1 ) * mmvar->num_axis +
2581             i;
2582 
2583         for ( j = i; j < mmvar->num_axis; j++, n++, c++ )
2584           if ( *c != *n )
2585             have_diff = 1;
2586       }
2587       else
2588       {
2589         c = blend->normalizedcoords + i;
2590         for ( j = i; j < mmvar->num_axis; j++, c++ )
2591           if ( *c != 0 )
2592             have_diff = 1;
2593       }
2594 
2595       /* return value -1 indicates `no change' */
2596       if ( !have_diff )
2597       {
2598         face->doblend = TRUE;
2599 
2600         return -1;
2601       }
2602 
2603       for ( ; i < mmvar->num_axis; i++ )
2604       {
2605         if ( blend->normalizedcoords[i] != 0 )
2606         {
2607           manageCvt = mcvt_load;
2608           break;
2609         }
2610       }
2611 
2612       /* If we don't change the blend coords then we don't need to do  */
2613       /* anything to the cvt table.  It will be correct.  Otherwise we */
2614       /* no longer have the original cvt (it was modified when we set  */
2615       /* the blend last time), so we must reload and then modify it.   */
2616     }
2617 
2618     blend->num_axis = mmvar->num_axis;
2619     FT_MEM_COPY( blend->normalizedcoords,
2620                  coords,
2621                  num_coords * sizeof ( FT_Fixed ) );
2622 
2623     if ( set_design_coords )
2624       ft_var_to_design( face,
2625                         all_design_coords ? blend->num_axis : num_coords,
2626                         blend->normalizedcoords,
2627                         blend->coords );
2628 
2629     face->doblend = TRUE;
2630 
2631     if ( face->cvt )
2632     {
2633       switch ( manageCvt )
2634       {
2635       case mcvt_load:
2636         /* The cvt table has been loaded already; every time we change the */
2637         /* blend we may need to reload and remodify the cvt table.         */
2638         FT_FREE( face->cvt );
2639         face->cvt = NULL;
2640 
2641         error = tt_face_load_cvt( face, face->root.stream );
2642         break;
2643 
2644       case mcvt_modify:
2645         /* The original cvt table is in memory.  All we need to do is */
2646         /* apply the `cvar' table (if any).                           */
2647         error = tt_face_vary_cvt( face, face->root.stream );
2648         break;
2649 
2650       case mcvt_retain:
2651         /* The cvt table is correct for this set of coordinates. */
2652         break;
2653       }
2654     }
2655 
2656     /* enforce recomputation of the PostScript name; */
2657     FT_FREE( face->postscript_name );
2658     face->postscript_name = NULL;
2659 
2660   Exit:
2661     return error;
2662   }
2663 
2664 
2665   /**************************************************************************
2666    *
2667    * @Function:
2668    *   TT_Set_MM_Blend
2669    *
2670    * @Description:
2671    *   Set the blend (normalized) coordinates for this instance of the
2672    *   font.  Check that the `gvar' table is reasonable and does some
2673    *   initial preparation.
2674    *
2675    * @InOut:
2676    *   face ::
2677    *     The font.
2678    *     Initialize the blend structure with `gvar' data.
2679    *
2680    * @Input:
2681    *   num_coords ::
2682    *     The number of available coordinates.  If it is
2683    *     larger than the number of axes, ignore the excess
2684    *     values.  If it is smaller than the number of axes,
2685    *     use the default value (0) for the remaining axes.
2686    *
2687    *   coords ::
2688    *     An array of `num_coords', each between [-1,1].
2689    *
2690    * @Return:
2691    *   FreeType error code.  0 means success.
2692    */
2693   FT_LOCAL_DEF( FT_Error )
2694   TT_Set_MM_Blend( TT_Face    face,
2695                    FT_UInt    num_coords,
2696                    FT_Fixed*  coords )
2697   {
2698     FT_Error  error;
2699 
2700 
2701     error = tt_set_mm_blend( face, num_coords, coords, 1 );
2702     if ( error )
2703       return error;
2704 
2705     if ( num_coords )
2706       face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2707     else
2708       face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2709 
2710     return FT_Err_Ok;
2711   }
2712 
2713 
2714   /**************************************************************************
2715    *
2716    * @Function:
2717    *   TT_Get_MM_Blend
2718    *
2719    * @Description:
2720    *   Get the blend (normalized) coordinates for this instance of the
2721    *   font.
2722    *
2723    * @InOut:
2724    *   face ::
2725    *     The font.
2726    *     Initialize the blend structure with `gvar' data.
2727    *
2728    * @Input:
2729    *   num_coords ::
2730    *     The number of available coordinates.  If it is
2731    *     larger than the number of axes, set the excess
2732    *     values to 0.
2733    *
2734    *   coords ::
2735    *     An array of `num_coords', each between [-1,1].
2736    *
2737    * @Return:
2738    *   FreeType error code.  0 means success.
2739    */
2740   FT_LOCAL_DEF( FT_Error )
2741   TT_Get_MM_Blend( TT_Face    face,
2742                    FT_UInt    num_coords,
2743                    FT_Fixed*  coords )
2744   {
2745     FT_Error  error = FT_Err_Ok;
2746     GX_Blend  blend;
2747     FT_UInt   i, nc;
2748 
2749 
2750     if ( !face->blend )
2751     {
2752       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2753         return error;
2754     }
2755 
2756     blend = face->blend;
2757 
2758     if ( !blend->coords )
2759     {
2760       /* select default instance coordinates */
2761       /* if no instance is selected yet      */
2762       if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
2763         return error;
2764     }
2765 
2766     nc = num_coords;
2767     if ( num_coords > blend->num_axis )
2768     {
2769       FT_TRACE2(( "TT_Get_MM_Blend:"
2770                   " only using first %d of %d coordinates\n",
2771                   blend->num_axis, num_coords ));
2772       nc = blend->num_axis;
2773     }
2774 
2775     if ( face->doblend )
2776     {
2777       for ( i = 0; i < nc; i++ )
2778         coords[i] = blend->normalizedcoords[i];
2779     }
2780     else
2781     {
2782       for ( i = 0; i < nc; i++ )
2783         coords[i] = 0;
2784     }
2785 
2786     for ( ; i < num_coords; i++ )
2787       coords[i] = 0;
2788 
2789     return FT_Err_Ok;
2790   }
2791 
2792 
2793   /**************************************************************************
2794    *
2795    * @Function:
2796    *   TT_Set_Var_Design
2797    *
2798    * @Description:
2799    *   Set the coordinates for the instance, measured in the user
2800    *   coordinate system.  Parse the `avar' table (if present) to convert
2801    *   from user to normalized coordinates.
2802    *
2803    * @InOut:
2804    *   face ::
2805    *     The font face.
2806    *     Initialize the blend struct with `gvar' data.
2807    *
2808    * @Input:
2809    *   num_coords ::
2810    *     The number of available coordinates.  If it is
2811    *     larger than the number of axes, ignore the excess
2812    *     values.  If it is smaller than the number of axes,
2813    *     use the default values for the remaining axes.
2814    *
2815    *   coords ::
2816    *     A coordinate array with `num_coords' elements.
2817    *
2818    * @Return:
2819    *   FreeType error code.  0 means success.
2820    */
2821   FT_LOCAL_DEF( FT_Error )
2822   TT_Set_Var_Design( TT_Face    face,
2823                      FT_UInt    num_coords,
2824                      FT_Fixed*  coords )
2825   {
2826     FT_Error    error  = FT_Err_Ok;
2827     GX_Blend    blend;
2828     FT_MM_Var*  mmvar;
2829     FT_UInt     i;
2830     FT_Memory   memory = face->root.memory;
2831 
2832     FT_Fixed*  c;
2833     FT_Fixed*  n;
2834     FT_Fixed*  normalized = NULL;
2835 
2836     FT_Bool  have_diff = 0;
2837 
2838 
2839     if ( !face->blend )
2840     {
2841       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2842         goto Exit;
2843     }
2844 
2845     blend = face->blend;
2846     mmvar = blend->mmvar;
2847 
2848     if ( num_coords > mmvar->num_axis )
2849     {
2850       FT_TRACE2(( "TT_Set_Var_Design:"
2851                   " only using first %d of %d coordinates\n",
2852                   mmvar->num_axis, num_coords ));
2853       num_coords = mmvar->num_axis;
2854     }
2855 
2856     if ( !blend->coords )
2857     {
2858       if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
2859         goto Exit;
2860     }
2861 
2862     c = blend->coords;
2863     n = coords;
2864     for ( i = 0; i < num_coords; i++, n++, c++ )
2865     {
2866       if ( *c != *n )
2867       {
2868         *c        = *n;
2869         have_diff = 1;
2870       }
2871     }
2872 
2873     if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2874     {
2875       FT_UInt              instance_index;
2876       FT_Var_Named_Style*  named_style;
2877 
2878 
2879       instance_index = (FT_UInt)face->root.face_index >> 16;
2880       named_style    = mmvar->namedstyle + instance_index - 1;
2881 
2882       n = named_style->coords + num_coords;
2883       for ( ; i < mmvar->num_axis; i++, n++, c++ )
2884       {
2885         if ( *c != *n )
2886         {
2887           *c        = *n;
2888           have_diff = 1;
2889         }
2890       }
2891     }
2892     else
2893     {
2894       FT_Var_Axis*  a;
2895 
2896 
2897       a = mmvar->axis + num_coords;
2898       for ( ; i < mmvar->num_axis; i++, a++, c++ )
2899       {
2900         if ( *c != a->def )
2901         {
2902           *c        = a->def;
2903           have_diff = 1;
2904         }
2905       }
2906     }
2907 
2908     /* return value -1 indicates `no change';                      */
2909     /* we can exit early if `normalizedcoords' is already computed */
2910     if ( blend->normalizedcoords && !have_diff )
2911       return -1;
2912 
2913     if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
2914       goto Exit;
2915 
2916     if ( !face->blend->avar_loaded )
2917       ft_var_load_avar( face );
2918 
2919     FT_TRACE5(( "TT_Set_Var_Design:\n"
2920                 "  normalized design coordinates:\n" ));
2921     ft_var_to_normalized( face, num_coords, blend->coords, normalized );
2922 
2923     error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 );
2924     if ( error )
2925       goto Exit;
2926 
2927     if ( num_coords )
2928       face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2929     else
2930       face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2931 
2932   Exit:
2933     FT_FREE( normalized );
2934     return error;
2935   }
2936 
2937 
2938   /**************************************************************************
2939    *
2940    * @Function:
2941    *   TT_Get_Var_Design
2942    *
2943    * @Description:
2944    *   Get the design coordinates of the currently selected interpolated
2945    *   font.
2946    *
2947    * @Input:
2948    *   face ::
2949    *     A handle to the source face.
2950    *
2951    *   num_coords ::
2952    *     The number of design coordinates to retrieve.  If it
2953    *     is larger than the number of axes, set the excess
2954    *     values to~0.
2955    *
2956    * @Output:
2957    *   coords ::
2958    *     The design coordinates array.
2959    *
2960    * @Return:
2961    *   FreeType error code.  0~means success.
2962    */
2963   FT_LOCAL_DEF( FT_Error )
2964   TT_Get_Var_Design( TT_Face    face,
2965                      FT_UInt    num_coords,
2966                      FT_Fixed*  coords )
2967   {
2968     FT_Error  error = FT_Err_Ok;
2969     GX_Blend  blend;
2970     FT_UInt   i, nc;
2971 
2972 
2973     if ( !face->blend )
2974     {
2975       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2976         return error;
2977     }
2978 
2979     blend = face->blend;
2980 
2981     if ( !blend->coords )
2982     {
2983       /* select default instance coordinates */
2984       /* if no instance is selected yet      */
2985       if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
2986         return error;
2987     }
2988 
2989     nc = num_coords;
2990     if ( num_coords > blend->num_axis )
2991     {
2992       FT_TRACE2(( "TT_Get_Var_Design:"
2993                   " only using first %d of %d coordinates\n",
2994                   blend->num_axis, num_coords ));
2995       nc = blend->num_axis;
2996     }
2997 
2998     if ( face->doblend )
2999     {
3000       for ( i = 0; i < nc; i++ )
3001         coords[i] = blend->coords[i];
3002     }
3003     else
3004     {
3005       for ( i = 0; i < nc; i++ )
3006         coords[i] = 0;
3007     }
3008 
3009     for ( ; i < num_coords; i++ )
3010       coords[i] = 0;
3011 
3012     return FT_Err_Ok;
3013   }
3014 
3015 
3016   /**************************************************************************
3017    *
3018    * @Function:
3019    *   TT_Set_Named_Instance
3020    *
3021    * @Description:
3022    *   Set the given named instance, also resetting any further
3023    *   variation.
3024    *
3025    * @Input:
3026    *   face ::
3027    *     A handle to the source face.
3028    *
3029    *   instance_index ::
3030    *     The instance index, starting with value 1.
3031    *     Value 0 indicates to not use an instance.
3032    *
3033    * @Return:
3034    *   FreeType error code.  0~means success.
3035    */
3036   FT_LOCAL_DEF( FT_Error )
3037   TT_Set_Named_Instance( TT_Face  face,
3038                          FT_UInt  instance_index )
3039   {
3040     FT_Error    error = FT_ERR( Invalid_Argument );
3041     GX_Blend    blend;
3042     FT_MM_Var*  mmvar;
3043 
3044     FT_UInt  num_instances;
3045 
3046 
3047     if ( !face->blend )
3048     {
3049       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
3050         goto Exit;
3051     }
3052 
3053     blend = face->blend;
3054     mmvar = blend->mmvar;
3055 
3056     num_instances = (FT_UInt)face->root.style_flags >> 16;
3057 
3058     /* `instance_index' starts with value 1, thus `>' */
3059     if ( instance_index > num_instances )
3060       goto Exit;
3061 
3062     if ( instance_index > 0 )
3063     {
3064       FT_Memory     memory = face->root.memory;
3065       SFNT_Service  sfnt   = (SFNT_Service)face->sfnt;
3066 
3067       FT_Var_Named_Style*  named_style;
3068       FT_String*           style_name;
3069 
3070 
3071       named_style = mmvar->namedstyle + instance_index - 1;
3072 
3073       error = sfnt->get_name( face,
3074                               (FT_UShort)named_style->strid,
3075                               &style_name );
3076       if ( error )
3077         goto Exit;
3078 
3079       /* set (or replace) style name */
3080       FT_FREE( face->root.style_name );
3081       face->root.style_name = style_name;
3082 
3083       /* finally, select the named instance */
3084       error = TT_Set_Var_Design( face,
3085                                  mmvar->num_axis,
3086                                  named_style->coords );
3087       if ( error )
3088       {
3089         /* internal error code -1 means `no change' */
3090         if ( error == -1 )
3091           error = FT_Err_Ok;
3092         goto Exit;
3093       }
3094     }
3095     else
3096       error = TT_Set_Var_Design( face, 0, NULL );
3097 
3098     face->root.face_index  = ( instance_index << 16 )             |
3099                              ( face->root.face_index & 0xFFFFL );
3100     face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
3101 
3102   Exit:
3103     return error;
3104   }
3105 
3106 
3107   /*************************************************************************/
3108   /*************************************************************************/
3109   /*****                                                               *****/
3110   /*****                     GX VAR PARSING ROUTINES                   *****/
3111   /*****                                                               *****/
3112   /*************************************************************************/
3113   /*************************************************************************/
3114 
3115 
3116   static FT_Error
3117   tt_cvt_ready_iterator( FT_ListNode  node,
3118                          void*        user )
3119   {
3120     TT_Size  size = (TT_Size)node->data;
3121 
3122     FT_UNUSED( user );
3123 
3124 
3125     size->cvt_ready = -1;
3126 
3127     return FT_Err_Ok;
3128   }
3129 
3130 
3131   /**************************************************************************
3132    *
3133    * @Function:
3134    *   tt_face_vary_cvt
3135    *
3136    * @Description:
3137    *   Modify the loaded cvt table according to the `cvar' table and the
3138    *   font's blend.
3139    *
3140    * @InOut:
3141    *   face ::
3142    *     A handle to the target face object.
3143    *
3144    * @Input:
3145    *   stream ::
3146    *     A handle to the input stream.
3147    *
3148    * @Return:
3149    *   FreeType error code.  0 means success.
3150    *
3151    *   Most errors are ignored.  It is perfectly valid not to have a
3152    *   `cvar' table even if there is a `gvar' and `fvar' table.
3153    */
3154   FT_LOCAL_DEF( FT_Error )
3155   tt_face_vary_cvt( TT_Face    face,
3156                     FT_Stream  stream )
3157   {
3158     FT_Error   error;
3159     FT_Memory  memory = stream->memory;
3160 
3161     FT_Face  root = &face->root;
3162 
3163     FT_ULong  table_start;
3164     FT_ULong  table_len;
3165 
3166     FT_UInt   tupleCount;
3167     FT_ULong  offsetToData;
3168 
3169     FT_ULong  here;
3170     FT_UInt   i, j;
3171 
3172     FT_Fixed*  tuple_coords    = NULL;
3173     FT_Fixed*  im_start_coords = NULL;
3174     FT_Fixed*  im_end_coords   = NULL;
3175 
3176     GX_Blend  blend = face->blend;
3177 
3178     FT_UInt  point_count;
3179     FT_UInt  spoint_count = 0;
3180 
3181     FT_UShort*  sharedpoints = NULL;
3182     FT_UShort*  localpoints  = NULL;
3183     FT_UShort*  points;
3184 
3185     FT_Fixed*  deltas     = NULL;
3186     FT_Fixed*  cvt_deltas = NULL;
3187 
3188 
3189     FT_TRACE2(( "CVAR " ));
3190 
3191     if ( !blend )
3192     {
3193       FT_TRACE2(( "\n"
3194                   "tt_face_vary_cvt: no blend specified\n" ));
3195       error = FT_Err_Ok;
3196       goto Exit;
3197     }
3198 
3199     if ( !face->cvt )
3200     {
3201       FT_TRACE2(( "\n"
3202                   "tt_face_vary_cvt: no `cvt ' table\n" ));
3203       error = FT_Err_Ok;
3204       goto Exit;
3205     }
3206 
3207     error = face->goto_table( face, TTAG_cvar, stream, &table_len );
3208     if ( error )
3209     {
3210       FT_TRACE2(( "is missing\n" ));
3211 
3212       error = FT_Err_Ok;
3213       goto Exit;
3214     }
3215 
3216     if ( FT_FRAME_ENTER( table_len ) )
3217     {
3218       error = FT_Err_Ok;
3219       goto Exit;
3220     }
3221 
3222     table_start = FT_Stream_FTell( stream );
3223     if ( FT_GET_LONG() != 0x00010000L )
3224     {
3225       FT_TRACE2(( "bad table version\n" ));
3226 
3227       error = FT_Err_Ok;
3228       goto FExit;
3229     }
3230 
3231     FT_TRACE2(( "loaded\n" ));
3232 
3233     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
3234          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3235          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
3236       goto FExit;
3237 
3238     tupleCount   = FT_GET_USHORT();
3239     offsetToData = FT_GET_USHORT();
3240 
3241     /* rough sanity test */
3242     if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
3243            table_len )
3244     {
3245       FT_TRACE2(( "tt_face_vary_cvt:"
3246                   " invalid CVT variation array header\n" ));
3247 
3248       error = FT_THROW( Invalid_Table );
3249       goto FExit;
3250     }
3251 
3252     offsetToData += table_start;
3253 
3254     if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3255     {
3256       here = FT_Stream_FTell( stream );
3257 
3258       FT_Stream_SeekSet( stream, offsetToData );
3259 
3260       sharedpoints = ft_var_readpackedpoints( stream,
3261                                               table_len,
3262                                               &spoint_count );
3263       offsetToData = FT_Stream_FTell( stream );
3264 
3265       FT_Stream_SeekSet( stream, here );
3266     }
3267 
3268     FT_TRACE5(( "cvar: there %s %d tuple%s:\n",
3269                 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
3270                 tupleCount & GX_TC_TUPLE_COUNT_MASK,
3271                 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
3272 
3273     if ( FT_NEW_ARRAY( cvt_deltas, face->cvt_size ) )
3274       goto FExit;
3275 
3276     for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
3277     {
3278       FT_UInt   tupleDataSize;
3279       FT_UInt   tupleIndex;
3280       FT_Fixed  apply;
3281 
3282 
3283       FT_TRACE6(( "  tuple %d:\n", i ));
3284 
3285       tupleDataSize = FT_GET_USHORT();
3286       tupleIndex    = FT_GET_USHORT();
3287 
3288       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3289       {
3290         for ( j = 0; j < blend->num_axis; j++ )
3291           tuple_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
3292       }
3293       else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3294       {
3295         FT_TRACE2(( "tt_face_vary_cvt:"
3296                     " invalid tuple index\n" ));
3297 
3298         error = FT_THROW( Invalid_Table );
3299         goto FExit;
3300       }
3301       else
3302       {
3303         if ( !blend->tuplecoords )
3304         {
3305           FT_TRACE2(( "tt_face_vary_cvt:"
3306                       " no valid tuple coordinates available\n" ));
3307 
3308           error = FT_THROW( Invalid_Table );
3309           goto FExit;
3310         }
3311 
3312         FT_MEM_COPY(
3313           tuple_coords,
3314           blend->tuplecoords +
3315             ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis,
3316           blend->num_axis * sizeof ( FT_Fixed ) );
3317       }
3318 
3319       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3320       {
3321         for ( j = 0; j < blend->num_axis; j++ )
3322           im_start_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
3323         for ( j = 0; j < blend->num_axis; j++ )
3324           im_end_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
3325       }
3326 
3327       apply = ft_var_apply_tuple( blend,
3328                                   (FT_UShort)tupleIndex,
3329                                   tuple_coords,
3330                                   im_start_coords,
3331                                   im_end_coords );
3332 
3333       if ( apply == 0 )              /* tuple isn't active for our blend */
3334       {
3335         offsetToData += tupleDataSize;
3336         continue;
3337       }
3338 
3339       here = FT_Stream_FTell( stream );
3340 
3341       FT_Stream_SeekSet( stream, offsetToData );
3342 
3343       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
3344       {
3345         localpoints = ft_var_readpackedpoints( stream,
3346                                                table_len,
3347                                                &point_count );
3348         points      = localpoints;
3349       }
3350       else
3351       {
3352         points      = sharedpoints;
3353         point_count = spoint_count;
3354       }
3355 
3356       deltas = ft_var_readpackeddeltas( stream,
3357                                         table_len,
3358                                         point_count == 0 ? face->cvt_size
3359                                                          : point_count );
3360 
3361       if ( !points                                                        ||
3362            !deltas                                                        ||
3363            ( localpoints == ALL_POINTS && point_count != face->cvt_size ) )
3364         ; /* failure, ignore it */
3365 
3366       else if ( localpoints == ALL_POINTS )
3367       {
3368 #ifdef FT_DEBUG_LEVEL_TRACE
3369         int  count = 0;
3370 #endif
3371 
3372 
3373         FT_TRACE7(( "    CVT deltas:\n" ));
3374 
3375         /* this means that there are deltas for every entry in cvt */
3376         for ( j = 0; j < face->cvt_size; j++ )
3377         {
3378           FT_Fixed  old_cvt_delta;
3379 
3380 
3381           old_cvt_delta = cvt_deltas[j];
3382           cvt_deltas[j] = old_cvt_delta + FT_MulFix( deltas[j], apply );
3383 
3384 #ifdef FT_DEBUG_LEVEL_TRACE
3385           if ( old_cvt_delta != cvt_deltas[j] )
3386           {
3387             FT_TRACE7(( "      %d: %f -> %f\n",
3388                         j,
3389                         ( FT_fdot6ToFixed( face->cvt[j] ) +
3390                           old_cvt_delta ) / 65536.0,
3391                         ( FT_fdot6ToFixed( face->cvt[j] ) +
3392                           cvt_deltas[j] ) / 65536.0 ));
3393             count++;
3394           }
3395 #endif
3396         }
3397 
3398 #ifdef FT_DEBUG_LEVEL_TRACE
3399         if ( !count )
3400           FT_TRACE7(( "      none\n" ));
3401 #endif
3402       }
3403 
3404       else
3405       {
3406 #ifdef FT_DEBUG_LEVEL_TRACE
3407         int  count = 0;
3408 #endif
3409 
3410 
3411         FT_TRACE7(( "    CVT deltas:\n" ));
3412 
3413         for ( j = 0; j < point_count; j++ )
3414         {
3415           int       pindex;
3416           FT_Fixed  old_cvt_delta;
3417 
3418 
3419           pindex = points[j];
3420           if ( (FT_ULong)pindex >= face->cvt_size )
3421             continue;
3422 
3423           old_cvt_delta      = cvt_deltas[pindex];
3424           cvt_deltas[pindex] = old_cvt_delta + FT_MulFix( deltas[j], apply );
3425 
3426 #ifdef FT_DEBUG_LEVEL_TRACE
3427           if ( old_cvt_delta != cvt_deltas[pindex] )
3428           {
3429             FT_TRACE7(( "      %d: %f -> %f\n",
3430                         pindex,
3431                         ( FT_fdot6ToFixed( face->cvt[pindex] ) +
3432                           old_cvt_delta ) / 65536.0,
3433                         ( FT_fdot6ToFixed( face->cvt[pindex] ) +
3434                           cvt_deltas[pindex] ) / 65536.0 ));
3435             count++;
3436           }
3437 #endif
3438         }
3439 
3440 #ifdef FT_DEBUG_LEVEL_TRACE
3441         if ( !count )
3442           FT_TRACE7(( "      none\n" ));
3443 #endif
3444       }
3445 
3446       if ( localpoints != ALL_POINTS )
3447         FT_FREE( localpoints );
3448       FT_FREE( deltas );
3449 
3450       offsetToData += tupleDataSize;
3451 
3452       FT_Stream_SeekSet( stream, here );
3453     }
3454 
3455     FT_TRACE5(( "\n" ));
3456 
3457     for ( i = 0; i < face->cvt_size; i++ )
3458       face->cvt[i] += FT_fixedToFdot6( cvt_deltas[i] );
3459 
3460   FExit:
3461     FT_FRAME_EXIT();
3462 
3463   Exit:
3464     if ( sharedpoints != ALL_POINTS )
3465       FT_FREE( sharedpoints );
3466     FT_FREE( tuple_coords );
3467     FT_FREE( im_start_coords );
3468     FT_FREE( im_end_coords );
3469     FT_FREE( cvt_deltas );
3470 
3471     /* iterate over all FT_Size objects and set `cvt_ready' to -1 */
3472     /* to trigger rescaling of all CVT values                     */
3473     FT_List_Iterate( &root->sizes_list,
3474                      tt_cvt_ready_iterator,
3475                      NULL );
3476 
3477     return error;
3478   }
3479 
3480 
3481   /* Shift the original coordinates of all points between indices `p1' */
3482   /* and `p2', using the same difference as given by index `ref'.      */
3483 
3484   /* modeled after `af_iup_shift' */
3485 
3486   static void
3487   tt_delta_shift( int         p1,
3488                   int         p2,
3489                   int         ref,
3490                   FT_Vector*  in_points,
3491                   FT_Vector*  out_points )
3492   {
3493     int        p;
3494     FT_Vector  delta;
3495 
3496 
3497     delta.x = out_points[ref].x - in_points[ref].x;
3498     delta.y = out_points[ref].y - in_points[ref].y;
3499 
3500     if ( delta.x == 0 && delta.y == 0 )
3501       return;
3502 
3503     for ( p = p1; p < ref; p++ )
3504     {
3505       out_points[p].x += delta.x;
3506       out_points[p].y += delta.y;
3507     }
3508 
3509     for ( p = ref + 1; p <= p2; p++ )
3510     {
3511       out_points[p].x += delta.x;
3512       out_points[p].y += delta.y;
3513     }
3514   }
3515 
3516 
3517   /* Interpolate the original coordinates of all points with indices */
3518   /* between `p1' and `p2', using `ref1' and `ref2' as the reference */
3519   /* point indices.                                                  */
3520 
3521   /* modeled after `af_iup_interp', `_iup_worker_interpolate', and   */
3522   /* `Ins_IUP' with spec differences in handling ill-defined cases.  */
3523   static void
3524   tt_delta_interpolate( int         p1,
3525                         int         p2,
3526                         int         ref1,
3527                         int         ref2,
3528                         FT_Vector*  in_points,
3529                         FT_Vector*  out_points )
3530   {
3531     int  p, i;
3532 
3533     FT_Pos  out, in1, in2, out1, out2, d1, d2;
3534 
3535 
3536     if ( p1 > p2 )
3537       return;
3538 
3539     /* handle both horizontal and vertical coordinates */
3540     for ( i = 0; i <= 1; i++ )
3541     {
3542       /* shift array pointers so that we can access `foo.y' as `foo.x' */
3543       in_points  = (FT_Vector*)( (FT_Pos*)in_points + i );
3544       out_points = (FT_Vector*)( (FT_Pos*)out_points + i );
3545 
3546       if ( in_points[ref1].x > in_points[ref2].x )
3547       {
3548         p    = ref1;
3549         ref1 = ref2;
3550         ref2 = p;
3551       }
3552 
3553       in1  = in_points[ref1].x;
3554       in2  = in_points[ref2].x;
3555       out1 = out_points[ref1].x;
3556       out2 = out_points[ref2].x;
3557       d1   = out1 - in1;
3558       d2   = out2 - in2;
3559 
3560       /* If the reference points have the same coordinate but different */
3561       /* delta, inferred delta is zero.  Otherwise interpolate.         */
3562       if ( in1 != in2 || out1 == out2 )
3563       {
3564         FT_Fixed  scale = in1 != in2 ? FT_DivFix( out2 - out1, in2 - in1 )
3565                                      : 0;
3566 
3567 
3568         for ( p = p1; p <= p2; p++ )
3569         {
3570           out = in_points[p].x;
3571 
3572           if ( out <= in1 )
3573             out += d1;
3574           else if ( out >= in2 )
3575             out += d2;
3576           else
3577             out = out1 + FT_MulFix( out - in1, scale );
3578 
3579           out_points[p].x = out;
3580         }
3581       }
3582     }
3583   }
3584 
3585 
3586   /* Interpolate points without delta values, similar to */
3587   /* the `IUP' hinting instruction.                      */
3588 
3589   /* modeled after `Ins_IUP */
3590 
3591   static void
3592   tt_interpolate_deltas( FT_Outline*  outline,
3593                          FT_Vector*   out_points,
3594                          FT_Vector*   in_points,
3595                          FT_Bool*     has_delta )
3596   {
3597     FT_Int  first_point;
3598     FT_Int  end_point;
3599 
3600     FT_Int  first_delta;
3601     FT_Int  cur_delta;
3602 
3603     FT_Int    point;
3604     FT_Short  contour;
3605 
3606 
3607     /* ignore empty outlines */
3608     if ( !outline->n_contours )
3609       return;
3610 
3611     contour = 0;
3612     point   = 0;
3613 
3614     do
3615     {
3616       end_point   = outline->contours[contour];
3617       first_point = point;
3618 
3619       /* search first point that has a delta */
3620       while ( point <= end_point && !has_delta[point] )
3621         point++;
3622 
3623       if ( point <= end_point )
3624       {
3625         first_delta = point;
3626         cur_delta   = point;
3627 
3628         point++;
3629 
3630         while ( point <= end_point )
3631         {
3632           /* search next point that has a delta  */
3633           /* and interpolate intermediate points */
3634           if ( has_delta[point] )
3635           {
3636             tt_delta_interpolate( cur_delta + 1,
3637                                   point - 1,
3638                                   cur_delta,
3639                                   point,
3640                                   in_points,
3641                                   out_points );
3642             cur_delta = point;
3643           }
3644 
3645           point++;
3646         }
3647 
3648         /* shift contour if we only have a single delta */
3649         if ( cur_delta == first_delta )
3650           tt_delta_shift( first_point,
3651                           end_point,
3652                           cur_delta,
3653                           in_points,
3654                           out_points );
3655         else
3656         {
3657           /* otherwise handle remaining points       */
3658           /* at the end and beginning of the contour */
3659           tt_delta_interpolate( cur_delta + 1,
3660                                 end_point,
3661                                 cur_delta,
3662                                 first_delta,
3663                                 in_points,
3664                                 out_points );
3665 
3666           if ( first_delta > 0 )
3667             tt_delta_interpolate( first_point,
3668                                   first_delta - 1,
3669                                   cur_delta,
3670                                   first_delta,
3671                                   in_points,
3672                                   out_points );
3673         }
3674       }
3675       contour++;
3676 
3677     } while ( contour < outline->n_contours );
3678   }
3679 
3680 
3681   /**************************************************************************
3682    *
3683    * @Function:
3684    *   TT_Vary_Apply_Glyph_Deltas
3685    *
3686    * @Description:
3687    *   Apply the appropriate deltas to the current glyph.
3688    *
3689    * @Input:
3690    *   face ::
3691    *     A handle to the target face object.
3692    *
3693    *   glyph_index ::
3694    *     The index of the glyph being modified.
3695    *
3696    *   n_points ::
3697    *     The number of the points in the glyph, including
3698    *     phantom points.
3699    *
3700    * @InOut:
3701    *   outline ::
3702    *     The outline to change.
3703    *
3704    * @Output:
3705    *   unrounded ::
3706    *     An array with `n_points' elements that is filled with unrounded
3707    *     point coordinates (in 26.6 format).
3708    *
3709    * @Return:
3710    *   FreeType error code.  0 means success.
3711    */
3712   FT_LOCAL_DEF( FT_Error )
3713   TT_Vary_Apply_Glyph_Deltas( TT_Face      face,
3714                               FT_UInt      glyph_index,
3715                               FT_Outline*  outline,
3716                               FT_Vector*   unrounded,
3717                               FT_UInt      n_points )
3718   {
3719     FT_Error   error;
3720     FT_Stream  stream = face->root.stream;
3721     FT_Memory  memory = stream->memory;
3722 
3723     FT_Vector*  points_org = NULL;  /* coordinates in 16.16 format */
3724     FT_Vector*  points_out = NULL;  /* coordinates in 16.16 format */
3725     FT_Bool*    has_delta  = NULL;
3726 
3727     FT_ULong  glyph_start;
3728 
3729     FT_UInt   tupleCount;
3730     FT_ULong  offsetToData;
3731     FT_ULong  dataSize;
3732 
3733     FT_ULong  here;
3734     FT_UInt   i, j;
3735 
3736     FT_Fixed*  tuple_coords    = NULL;
3737     FT_Fixed*  im_start_coords = NULL;
3738     FT_Fixed*  im_end_coords   = NULL;
3739 
3740     GX_Blend  blend = face->blend;
3741 
3742     FT_UInt  point_count;
3743     FT_UInt  spoint_count = 0;
3744 
3745     FT_UShort*  sharedpoints = NULL;
3746     FT_UShort*  localpoints  = NULL;
3747     FT_UShort*  points;
3748 
3749     FT_Fixed*  deltas_x       = NULL;
3750     FT_Fixed*  deltas_y       = NULL;
3751     FT_Fixed*  point_deltas_x = NULL;
3752     FT_Fixed*  point_deltas_y = NULL;
3753 
3754 
3755     if ( !face->doblend || !blend )
3756       return FT_THROW( Invalid_Argument );
3757 
3758     for ( i = 0; i < n_points; i++ )
3759     {
3760       unrounded[i].x = INT_TO_F26DOT6( outline->points[i].x );
3761       unrounded[i].y = INT_TO_F26DOT6( outline->points[i].y );
3762     }
3763 
3764     if ( glyph_index >= blend->gv_glyphcnt      ||
3765          blend->glyphoffsets[glyph_index] ==
3766            blend->glyphoffsets[glyph_index + 1] )
3767     {
3768       FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3769                   " no variation data for this glyph\n" ));
3770       return FT_Err_Ok;
3771     }
3772 
3773     if ( FT_NEW_ARRAY( points_org, n_points ) ||
3774          FT_NEW_ARRAY( points_out, n_points ) ||
3775          FT_NEW_ARRAY( has_delta, n_points )  )
3776       goto Fail1;
3777 
3778     dataSize = blend->glyphoffsets[glyph_index + 1] -
3779                  blend->glyphoffsets[glyph_index];
3780 
3781     if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) ||
3782          FT_FRAME_ENTER( dataSize )                         )
3783       goto Fail1;
3784 
3785     glyph_start = FT_Stream_FTell( stream );
3786 
3787     /* each set of glyph variation data is formatted similarly to `cvar' */
3788 
3789     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
3790          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3791          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
3792       goto Fail2;
3793 
3794     tupleCount   = FT_GET_USHORT();
3795     offsetToData = FT_GET_USHORT();
3796 
3797     /* rough sanity test */
3798     if ( offsetToData > dataSize                                ||
3799          ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 > dataSize )
3800     {
3801       FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3802                   " invalid glyph variation array header\n" ));
3803 
3804       error = FT_THROW( Invalid_Table );
3805       goto Fail2;
3806     }
3807 
3808     offsetToData += glyph_start;
3809 
3810     if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3811     {
3812       here = FT_Stream_FTell( stream );
3813 
3814       FT_Stream_SeekSet( stream, offsetToData );
3815 
3816       sharedpoints = ft_var_readpackedpoints( stream,
3817                                               blend->gvar_size,
3818                                               &spoint_count );
3819       offsetToData = FT_Stream_FTell( stream );
3820 
3821       FT_Stream_SeekSet( stream, here );
3822     }
3823 
3824     FT_TRACE5(( "gvar: there %s %d tuple%s:\n",
3825                 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
3826                 tupleCount & GX_TC_TUPLE_COUNT_MASK,
3827                 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
3828 
3829     if ( FT_NEW_ARRAY( point_deltas_x, n_points ) ||
3830          FT_NEW_ARRAY( point_deltas_y, n_points ) )
3831       goto Fail3;
3832 
3833     for ( j = 0; j < n_points; j++ )
3834     {
3835       points_org[j].x = FT_intToFixed( outline->points[j].x );
3836       points_org[j].y = FT_intToFixed( outline->points[j].y );
3837     }
3838 
3839     for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
3840     {
3841       FT_UInt   tupleDataSize;
3842       FT_UInt   tupleIndex;
3843       FT_Fixed  apply;
3844 
3845 
3846       FT_TRACE6(( "  tuple %d:\n", i ));
3847 
3848       tupleDataSize = FT_GET_USHORT();
3849       tupleIndex    = FT_GET_USHORT();
3850 
3851       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3852       {
3853         for ( j = 0; j < blend->num_axis; j++ )
3854           tuple_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
3855       }
3856       else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3857       {
3858         FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3859                     " invalid tuple index\n" ));
3860 
3861         error = FT_THROW( Invalid_Table );
3862         goto Fail3;
3863       }
3864       else
3865         FT_MEM_COPY(
3866           tuple_coords,
3867           blend->tuplecoords +
3868             ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis,
3869           blend->num_axis * sizeof ( FT_Fixed ) );
3870 
3871       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3872       {
3873         for ( j = 0; j < blend->num_axis; j++ )
3874           im_start_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
3875         for ( j = 0; j < blend->num_axis; j++ )
3876           im_end_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
3877       }
3878 
3879       apply = ft_var_apply_tuple( blend,
3880                                   (FT_UShort)tupleIndex,
3881                                   tuple_coords,
3882                                   im_start_coords,
3883                                   im_end_coords );
3884 
3885       if ( apply == 0 )              /* tuple isn't active for our blend */
3886       {
3887         offsetToData += tupleDataSize;
3888         continue;
3889       }
3890 
3891       here = FT_Stream_FTell( stream );
3892 
3893       FT_Stream_SeekSet( stream, offsetToData );
3894 
3895       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
3896       {
3897         localpoints = ft_var_readpackedpoints( stream,
3898                                                blend->gvar_size,
3899                                                &point_count );
3900         points      = localpoints;
3901       }
3902       else
3903       {
3904         points      = sharedpoints;
3905         point_count = spoint_count;
3906       }
3907 
3908       deltas_x = ft_var_readpackeddeltas( stream,
3909                                           blend->gvar_size,
3910                                           point_count == 0 ? n_points
3911                                                            : point_count );
3912       deltas_y = ft_var_readpackeddeltas( stream,
3913                                           blend->gvar_size,
3914                                           point_count == 0 ? n_points
3915                                                            : point_count );
3916 
3917       if ( !points || !deltas_y || !deltas_x )
3918         ; /* failure, ignore it */
3919 
3920       else if ( points == ALL_POINTS )
3921       {
3922 #ifdef FT_DEBUG_LEVEL_TRACE
3923         int  count = 0;
3924 #endif
3925 
3926 
3927         FT_TRACE7(( "    point deltas:\n" ));
3928 
3929         /* this means that there are deltas for every point in the glyph */
3930         for ( j = 0; j < n_points; j++ )
3931         {
3932           FT_Fixed  old_point_delta_x = point_deltas_x[j];
3933           FT_Fixed  old_point_delta_y = point_deltas_y[j];
3934 
3935           FT_Fixed  point_delta_x = FT_MulFix( deltas_x[j], apply );
3936           FT_Fixed  point_delta_y = FT_MulFix( deltas_y[j], apply );
3937 
3938 
3939           if ( j < n_points - 4 )
3940           {
3941             point_deltas_x[j] = old_point_delta_x + point_delta_x;
3942             point_deltas_y[j] = old_point_delta_y + point_delta_y;
3943           }
3944           else
3945           {
3946             /* To avoid double adjustment of advance width or height, */
3947             /* adjust phantom points only if there is no HVAR or VVAR */
3948             /* support, respectively.                                 */
3949             if ( j == ( n_points - 4 )        &&
3950                  !( face->variation_support &
3951                     TT_FACE_FLAG_VAR_LSB    ) )
3952               point_deltas_x[j] = old_point_delta_x + point_delta_x;
3953 
3954             else if ( j == ( n_points - 3 )          &&
3955                       !( face->variation_support   &
3956                          TT_FACE_FLAG_VAR_HADVANCE ) )
3957               point_deltas_x[j] = old_point_delta_x + point_delta_x;
3958 
3959             else if ( j == ( n_points - 2 )        &&
3960                       !( face->variation_support &
3961                          TT_FACE_FLAG_VAR_TSB    ) )
3962               point_deltas_y[j] = old_point_delta_y + point_delta_y;
3963 
3964             else if ( j == ( n_points - 1 )          &&
3965                       !( face->variation_support   &
3966                          TT_FACE_FLAG_VAR_VADVANCE ) )
3967               point_deltas_y[j] = old_point_delta_y + point_delta_y;
3968           }
3969 
3970 #ifdef FT_DEBUG_LEVEL_TRACE
3971           if ( point_delta_x || point_delta_y )
3972           {
3973             FT_TRACE7(( "      %d: (%f, %f) -> (%f, %f)\n",
3974                         j,
3975                         ( FT_intToFixed( outline->points[j].x ) +
3976                           old_point_delta_x ) / 65536.0,
3977                         ( FT_intToFixed( outline->points[j].y ) +
3978                           old_point_delta_y ) / 65536.0,
3979                         ( FT_intToFixed( outline->points[j].x ) +
3980                           point_deltas_x[j] ) / 65536.0,
3981                         ( FT_intToFixed( outline->points[j].y ) +
3982                           point_deltas_y[j] ) / 65536.0 ));
3983             count++;
3984           }
3985 #endif
3986         }
3987 
3988 #ifdef FT_DEBUG_LEVEL_TRACE
3989         if ( !count )
3990           FT_TRACE7(( "      none\n" ));
3991 #endif
3992       }
3993 
3994       else
3995       {
3996 #ifdef FT_DEBUG_LEVEL_TRACE
3997         int  count = 0;
3998 #endif
3999 
4000 
4001         /* we have to interpolate the missing deltas similar to the */
4002         /* IUP bytecode instruction                                 */
4003         for ( j = 0; j < n_points; j++ )
4004         {
4005           has_delta[j]  = FALSE;
4006           points_out[j] = points_org[j];
4007         }
4008 
4009         for ( j = 0; j < point_count; j++ )
4010         {
4011           FT_UShort  idx = points[j];
4012 
4013 
4014           if ( idx >= n_points )
4015             continue;
4016 
4017           has_delta[idx] = TRUE;
4018 
4019           points_out[idx].x += FT_MulFix( deltas_x[j], apply );
4020           points_out[idx].y += FT_MulFix( deltas_y[j], apply );
4021         }
4022 
4023         /* no need to handle phantom points here,      */
4024         /* since solitary points can't be interpolated */
4025         tt_interpolate_deltas( outline,
4026                                points_out,
4027                                points_org,
4028                                has_delta );
4029 
4030         FT_TRACE7(( "    point deltas:\n" ));
4031 
4032         for ( j = 0; j < n_points; j++ )
4033         {
4034           FT_Fixed  old_point_delta_x = point_deltas_x[j];
4035           FT_Fixed  old_point_delta_y = point_deltas_y[j];
4036 
4037           FT_Pos  point_delta_x = points_out[j].x - points_org[j].x;
4038           FT_Pos  point_delta_y = points_out[j].y - points_org[j].y;
4039 
4040 
4041           if ( j < n_points - 4 )
4042           {
4043             point_deltas_x[j] = old_point_delta_x + point_delta_x;
4044             point_deltas_y[j] = old_point_delta_y + point_delta_y;
4045           }
4046           else
4047           {
4048             /* To avoid double adjustment of advance width or height, */
4049             /* adjust phantom points only if there is no HVAR or VVAR */
4050             /* support, respectively.                                 */
4051             if ( j == ( n_points - 4 )        &&
4052                  !( face->variation_support &
4053                     TT_FACE_FLAG_VAR_LSB    ) )
4054               point_deltas_x[j] = old_point_delta_x + point_delta_x;
4055 
4056             else if ( j == ( n_points - 3 )          &&
4057                       !( face->variation_support   &
4058                          TT_FACE_FLAG_VAR_HADVANCE ) )
4059               point_deltas_x[j] = old_point_delta_x + point_delta_x;
4060 
4061             else if ( j == ( n_points - 2 )        &&
4062                       !( face->variation_support &
4063                          TT_FACE_FLAG_VAR_TSB    ) )
4064               point_deltas_y[j] = old_point_delta_y + point_delta_y;
4065 
4066             else if ( j == ( n_points - 1 )          &&
4067                       !( face->variation_support   &
4068                          TT_FACE_FLAG_VAR_VADVANCE ) )
4069               point_deltas_y[j] = old_point_delta_y + point_delta_y;
4070           }
4071 
4072 #ifdef FT_DEBUG_LEVEL_TRACE
4073           if ( point_delta_x || point_delta_y )
4074           {
4075             FT_TRACE7(( "      %d: (%f, %f) -> (%f, %f)\n",
4076                         j,
4077                         ( FT_intToFixed( outline->points[j].x ) +
4078                           old_point_delta_x ) / 65536.0,
4079                         ( FT_intToFixed( outline->points[j].y ) +
4080                           old_point_delta_y ) / 65536.0,
4081                         ( FT_intToFixed( outline->points[j].x ) +
4082                           point_deltas_x[j] ) / 65536.0,
4083                         ( FT_intToFixed( outline->points[j].y ) +
4084                           point_deltas_y[j] ) / 65536.0 ));
4085             count++;
4086           }
4087 #endif
4088         }
4089 
4090 #ifdef FT_DEBUG_LEVEL_TRACE
4091         if ( !count )
4092           FT_TRACE7(( "      none\n" ));
4093 #endif
4094       }
4095 
4096       if ( localpoints != ALL_POINTS )
4097         FT_FREE( localpoints );
4098       FT_FREE( deltas_x );
4099       FT_FREE( deltas_y );
4100 
4101       offsetToData += tupleDataSize;
4102 
4103       FT_Stream_SeekSet( stream, here );
4104     }
4105 
4106     FT_TRACE5(( "\n" ));
4107 
4108     for ( i = 0; i < n_points; i++ )
4109     {
4110       unrounded[i].x += FT_fixedToFdot6( point_deltas_x[i] );
4111       unrounded[i].y += FT_fixedToFdot6( point_deltas_y[i] );
4112 
4113       outline->points[i].x += FT_fixedToInt( point_deltas_x[i] );
4114       outline->points[i].y += FT_fixedToInt( point_deltas_y[i] );
4115     }
4116 
4117   Fail3:
4118     FT_FREE( point_deltas_x );
4119     FT_FREE( point_deltas_y );
4120 
4121   Fail2:
4122     if ( sharedpoints != ALL_POINTS )
4123       FT_FREE( sharedpoints );
4124     FT_FREE( tuple_coords );
4125     FT_FREE( im_start_coords );
4126     FT_FREE( im_end_coords );
4127 
4128     FT_FRAME_EXIT();
4129 
4130   Fail1:
4131     FT_FREE( points_org );
4132     FT_FREE( points_out );
4133     FT_FREE( has_delta );
4134 
4135     return error;
4136   }
4137 
4138 
4139   /**************************************************************************
4140    *
4141    * @Function:
4142    *   tt_get_var_blend
4143    *
4144    * @Description:
4145    *   An extended internal version of `TT_Get_MM_Blend' that returns
4146    *   pointers instead of copying data, without any initialization of
4147    *   the MM machinery in case it isn't loaded yet.
4148    */
4149   FT_LOCAL_DEF( FT_Error )
4150   tt_get_var_blend( TT_Face      face,
4151                     FT_UInt     *num_coords,
4152                     FT_Fixed*   *coords,
4153                     FT_Fixed*   *normalizedcoords,
4154                     FT_MM_Var*  *mm_var )
4155   {
4156     if ( face->blend )
4157     {
4158       if ( num_coords )
4159         *num_coords       = face->blend->num_axis;
4160       if ( coords )
4161         *coords           = face->blend->coords;
4162       if ( normalizedcoords )
4163         *normalizedcoords = face->blend->normalizedcoords;
4164       if ( mm_var )
4165         *mm_var           = face->blend->mmvar;
4166     }
4167     else
4168     {
4169       if ( num_coords )
4170         *num_coords = 0;
4171       if ( coords )
4172         *coords     = NULL;
4173       if ( mm_var )
4174         *mm_var     = NULL;
4175     }
4176 
4177     return FT_Err_Ok;
4178   }
4179 
4180 
4181   static void
4182   ft_var_done_item_variation_store( TT_Face          face,
4183                                     GX_ItemVarStore  itemStore )
4184   {
4185     FT_Memory  memory = FT_FACE_MEMORY( face );
4186     FT_UInt    i;
4187 
4188 
4189     if ( itemStore->varData )
4190     {
4191       for ( i = 0; i < itemStore->dataCount; i++ )
4192       {
4193         FT_FREE( itemStore->varData[i].regionIndices );
4194         FT_FREE( itemStore->varData[i].deltaSet );
4195       }
4196 
4197       FT_FREE( itemStore->varData );
4198     }
4199 
4200     if ( itemStore->varRegionList )
4201     {
4202       for ( i = 0; i < itemStore->regionCount; i++ )
4203         FT_FREE( itemStore->varRegionList[i].axisList );
4204 
4205       FT_FREE( itemStore->varRegionList );
4206     }
4207   }
4208 
4209 
4210   /**************************************************************************
4211    *
4212    * @Function:
4213    *   tt_done_blend
4214    *
4215    * @Description:
4216    *   Free the blend internal data structure.
4217    */
4218   FT_LOCAL_DEF( void )
4219   tt_done_blend( TT_Face  face )
4220   {
4221     FT_Memory  memory = FT_FACE_MEMORY( face );
4222     GX_Blend   blend  = face->blend;
4223 
4224 
4225     if ( blend )
4226     {
4227       FT_UInt  i, num_axes;
4228 
4229 
4230       /* blend->num_axis might not be set up yet */
4231       num_axes = blend->mmvar->num_axis;
4232 
4233       FT_FREE( blend->coords );
4234       FT_FREE( blend->normalizedcoords );
4235       FT_FREE( blend->normalized_stylecoords );
4236       FT_FREE( blend->mmvar );
4237 
4238       if ( blend->avar_segment )
4239       {
4240         for ( i = 0; i < num_axes; i++ )
4241           FT_FREE( blend->avar_segment[i].correspondence );
4242         FT_FREE( blend->avar_segment );
4243       }
4244 
4245       if ( blend->hvar_table )
4246       {
4247         ft_var_done_item_variation_store( face,
4248                                           &blend->hvar_table->itemStore );
4249 
4250         FT_FREE( blend->hvar_table->widthMap.innerIndex );
4251         FT_FREE( blend->hvar_table->widthMap.outerIndex );
4252         FT_FREE( blend->hvar_table );
4253       }
4254 
4255       if ( blend->vvar_table )
4256       {
4257         ft_var_done_item_variation_store( face,
4258                                           &blend->vvar_table->itemStore );
4259 
4260         FT_FREE( blend->vvar_table->widthMap.innerIndex );
4261         FT_FREE( blend->vvar_table->widthMap.outerIndex );
4262         FT_FREE( blend->vvar_table );
4263       }
4264 
4265       if ( blend->mvar_table )
4266       {
4267         ft_var_done_item_variation_store( face,
4268                                           &blend->mvar_table->itemStore );
4269 
4270         FT_FREE( blend->mvar_table->values );
4271         FT_FREE( blend->mvar_table );
4272       }
4273 
4274       FT_FREE( blend->tuplecoords );
4275       FT_FREE( blend->glyphoffsets );
4276       FT_FREE( blend );
4277     }
4278   }
4279 
4280 #else /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
4281 
4282   /* ANSI C doesn't like empty source files */
4283   typedef int  _tt_gxvar_dummy;
4284 
4285 #endif /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
4286 
4287 
4288 /* END */