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