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