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