tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ttgxvar.c (135981B)


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