Home | History | Annotate | Download | only in truetype
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  ttgxvar.c                                                              */
      4 /*                                                                         */
      5 /*    TrueType GX Font Variation loader                                    */
      6 /*                                                                         */
      7 /*  Copyright 2004-2011 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   /*   http://developer.apple.com/fonts/TTRefMan/RM06/Chap6[fgca]var.html  */
     24   /*                                                                       */
     25   /* The documentation for `fvar' is inconsistent.  At one point it says   */
     26   /* that `countSizePairs' should be 3, at another point 2.  It should     */
     27   /* be 2.                                                                 */
     28   /*                                                                       */
     29   /* The documentation for `gvar' is not intelligible; `cvar' refers you   */
     30   /* to `gvar' and is thus also incomprehensible.                          */
     31   /*                                                                       */
     32   /* The documentation for `avar' appears correct, but Apple has no fonts  */
     33   /* with an `avar' table, so it is hard to test.                          */
     34   /*                                                                       */
     35   /* Many thanks to John Jenkins (at Apple) in figuring this out.          */
     36   /*                                                                       */
     37   /*                                                                       */
     38   /* Apple's `kern' table has some references to tuple indices, but as     */
     39   /* there is no indication where these indices are defined, nor how to    */
     40   /* interpolate the kerning values (different tuples have different       */
     41   /* classes) this issue is ignored.                                       */
     42   /*                                                                       */
     43   /*************************************************************************/
     44 
     45 
     46 #include <ft2build.h>
     47 #include FT_INTERNAL_DEBUG_H
     48 #include FT_CONFIG_CONFIG_H
     49 #include FT_INTERNAL_STREAM_H
     50 #include FT_INTERNAL_SFNT_H
     51 #include FT_TRUETYPE_TAGS_H
     52 #include FT_MULTIPLE_MASTERS_H
     53 
     54 #include "ttpload.h"
     55 #include "ttgxvar.h"
     56 
     57 #include "tterrors.h"
     58 
     59 
     60 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
     61 
     62 
     63 #define FT_Stream_FTell( stream )  \
     64           ( (stream)->cursor - (stream)->base )
     65 #define FT_Stream_SeekSet( stream, off ) \
     66               ( (stream)->cursor = (stream)->base+(off) )
     67 
     68 
     69   /*************************************************************************/
     70   /*                                                                       */
     71   /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
     72   /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
     73   /* messages during execution.                                            */
     74   /*                                                                       */
     75 #undef  FT_COMPONENT
     76 #define FT_COMPONENT  trace_ttgxvar
     77 
     78 
     79   /*************************************************************************/
     80   /*************************************************************************/
     81   /*****                                                               *****/
     82   /*****                       Internal Routines                       *****/
     83   /*****                                                               *****/
     84   /*************************************************************************/
     85   /*************************************************************************/
     86 
     87 
     88   /*************************************************************************/
     89   /*                                                                       */
     90   /* The macro ALL_POINTS is used in `ft_var_readpackedpoints'.  It        */
     91   /* indicates that there is a delta for every point without needing to    */
     92   /* enumerate all of them.                                                */
     93   /*                                                                       */
     94 #define ALL_POINTS  (FT_UShort*)( -1 )
     95 
     96 
     97 #define GX_PT_POINTS_ARE_WORDS      0x80
     98 #define GX_PT_POINT_RUN_COUNT_MASK  0x7F
     99 
    100 
    101   /*************************************************************************/
    102   /*                                                                       */
    103   /* <Function>                                                            */
    104   /*    ft_var_readpackedpoints                                            */
    105   /*                                                                       */
    106   /* <Description>                                                         */
    107   /*    Read a set of points to which the following deltas will apply.     */
    108   /*    Points are packed with a run length encoding.                      */
    109   /*                                                                       */
    110   /* <Input>                                                               */
    111   /*    stream    :: The data stream.                                      */
    112   /*                                                                       */
    113   /* <Output>                                                              */
    114   /*    point_cnt :: The number of points read.  A zero value means that   */
    115   /*                 all points in the glyph will be affected, without     */
    116   /*                 enumerating them individually.                        */
    117   /*                                                                       */
    118   /* <Return>                                                              */
    119   /*    An array of FT_UShort containing the affected points or the        */
    120   /*    special value ALL_POINTS.                                          */
    121   /*                                                                       */
    122   static FT_UShort*
    123   ft_var_readpackedpoints( FT_Stream  stream,
    124                            FT_UInt   *point_cnt )
    125   {
    126     FT_UShort *points = NULL;
    127     FT_Int     n;
    128     FT_Int     runcnt;
    129     FT_Int     i;
    130     FT_Int     j;
    131     FT_Int     first;
    132     FT_Memory  memory = stream->memory;
    133     FT_Error   error  = TT_Err_Ok;
    134 
    135     FT_UNUSED( error );
    136 
    137 
    138     *point_cnt = n = FT_GET_BYTE();
    139     if ( n == 0 )
    140       return ALL_POINTS;
    141 
    142     if ( n & GX_PT_POINTS_ARE_WORDS )
    143       n = FT_GET_BYTE() | ( ( n & GX_PT_POINT_RUN_COUNT_MASK ) << 8 );
    144 
    145     if ( FT_NEW_ARRAY( points, n ) )
    146       return NULL;
    147 
    148     i = 0;
    149     while ( i < n )
    150     {
    151       runcnt = FT_GET_BYTE();
    152       if ( runcnt & GX_PT_POINTS_ARE_WORDS )
    153       {
    154         runcnt = runcnt & GX_PT_POINT_RUN_COUNT_MASK;
    155         first  = points[i++] = FT_GET_USHORT();
    156 
    157         if ( runcnt < 1 || i + runcnt >= n )
    158           goto Exit;
    159 
    160         /* first point not included in runcount */
    161         for ( j = 0; j < runcnt; ++j )
    162           points[i++] = (FT_UShort)( first += FT_GET_USHORT() );
    163       }
    164       else
    165       {
    166         first = points[i++] = FT_GET_BYTE();
    167 
    168         if ( runcnt < 1 || i + runcnt >= n )
    169           goto Exit;
    170 
    171         for ( j = 0; j < runcnt; ++j )
    172           points[i++] = (FT_UShort)( first += FT_GET_BYTE() );
    173       }
    174     }
    175 
    176   Exit:
    177     return points;
    178   }
    179 
    180 
    181   enum
    182   {
    183     GX_DT_DELTAS_ARE_ZERO      = 0x80,
    184     GX_DT_DELTAS_ARE_WORDS     = 0x40,
    185     GX_DT_DELTA_RUN_COUNT_MASK = 0x3F
    186   };
    187 
    188 
    189   /*************************************************************************/
    190   /*                                                                       */
    191   /* <Function>                                                            */
    192   /*    ft_var_readpackeddeltas                                            */
    193   /*                                                                       */
    194   /* <Description>                                                         */
    195   /*    Read a set of deltas.  These are packed slightly differently than  */
    196   /*    points.  In particular there is no overall count.                  */
    197   /*                                                                       */
    198   /* <Input>                                                               */
    199   /*    stream    :: The data stream.                                      */
    200   /*                                                                       */
    201   /*    delta_cnt :: The number of to be read.                             */
    202   /*                                                                       */
    203   /* <Return>                                                              */
    204   /*    An array of FT_Short containing the deltas for the affected        */
    205   /*    points.  (This only gets the deltas for one dimension.  It will    */
    206   /*    generally be called twice, once for x, once for y.  When used in   */
    207   /*    cvt table, it will only be called once.)                           */
    208   /*                                                                       */
    209   static FT_Short*
    210   ft_var_readpackeddeltas( FT_Stream  stream,
    211                            FT_Offset  delta_cnt )
    212   {
    213     FT_Short  *deltas = NULL;
    214     FT_UInt    runcnt;
    215     FT_Offset  i;
    216     FT_UInt    j;
    217     FT_Memory  memory = stream->memory;
    218     FT_Error   error  = TT_Err_Ok;
    219 
    220     FT_UNUSED( error );
    221 
    222 
    223     if ( FT_NEW_ARRAY( deltas, delta_cnt ) )
    224       return NULL;
    225 
    226     i = 0;
    227     while ( i < delta_cnt )
    228     {
    229       runcnt = FT_GET_BYTE();
    230       if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
    231       {
    232         /* runcnt zeroes get added */
    233         for ( j = 0;
    234               j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
    235               ++j )
    236           deltas[i++] = 0;
    237       }
    238       else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
    239       {
    240         /* runcnt shorts from the stack */
    241         for ( j = 0;
    242               j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
    243               ++j )
    244           deltas[i++] = FT_GET_SHORT();
    245       }
    246       else
    247       {
    248         /* runcnt signed bytes from the stack */
    249         for ( j = 0;
    250               j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
    251               ++j )
    252           deltas[i++] = FT_GET_CHAR();
    253       }
    254 
    255       if ( j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) )
    256       {
    257         /* Bad format */
    258         FT_FREE( deltas );
    259         return NULL;
    260       }
    261     }
    262 
    263     return deltas;
    264   }
    265 
    266 
    267   /*************************************************************************/
    268   /*                                                                       */
    269   /* <Function>                                                            */
    270   /*    ft_var_load_avar                                                   */
    271   /*                                                                       */
    272   /* <Description>                                                         */
    273   /*    Parse the `avar' table if present.  It need not be, so we return   */
    274   /*    nothing.                                                           */
    275   /*                                                                       */
    276   /* <InOut>                                                               */
    277   /*    face :: The font face.                                             */
    278   /*                                                                       */
    279   static void
    280   ft_var_load_avar( TT_Face  face )
    281   {
    282     FT_Stream       stream = FT_FACE_STREAM(face);
    283     FT_Memory       memory = stream->memory;
    284     GX_Blend        blend  = face->blend;
    285     GX_AVarSegment  segment;
    286     FT_Error        error = TT_Err_Ok;
    287     FT_ULong        version;
    288     FT_Long         axisCount;
    289     FT_Int          i, j;
    290     FT_ULong        table_len;
    291 
    292     FT_UNUSED( error );
    293 
    294 
    295     blend->avar_checked = TRUE;
    296     if ( (error = face->goto_table( face, TTAG_avar, stream, &table_len )) != 0 )
    297       return;
    298 
    299     if ( FT_FRAME_ENTER( table_len ) )
    300       return;
    301 
    302     version   = FT_GET_LONG();
    303     axisCount = FT_GET_LONG();
    304 
    305     if ( version != 0x00010000L                       ||
    306          axisCount != (FT_Long)blend->mmvar->num_axis )
    307       goto Exit;
    308 
    309     if ( FT_NEW_ARRAY( blend->avar_segment, axisCount ) )
    310       goto Exit;
    311 
    312     segment = &blend->avar_segment[0];
    313     for ( i = 0; i < axisCount; ++i, ++segment )
    314     {
    315       segment->pairCount = FT_GET_USHORT();
    316       if ( FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) )
    317       {
    318         /* Failure.  Free everything we have done so far.  We must do */
    319         /* it right now since loading the `avar' table is optional.   */
    320 
    321         for ( j = i - 1; j >= 0; --j )
    322           FT_FREE( blend->avar_segment[j].correspondence );
    323 
    324         FT_FREE( blend->avar_segment );
    325         blend->avar_segment = NULL;
    326         goto Exit;
    327       }
    328 
    329       for ( j = 0; j < segment->pairCount; ++j )
    330       {
    331         segment->correspondence[j].fromCoord =
    332           FT_GET_SHORT() << 2;    /* convert to Fixed */
    333         segment->correspondence[j].toCoord =
    334           FT_GET_SHORT()<<2;    /* convert to Fixed */
    335       }
    336     }
    337 
    338   Exit:
    339     FT_FRAME_EXIT();
    340   }
    341 
    342 
    343   typedef struct  GX_GVar_Head_
    344   {
    345     FT_Long    version;
    346     FT_UShort  axisCount;
    347     FT_UShort  globalCoordCount;
    348     FT_ULong   offsetToCoord;
    349     FT_UShort  glyphCount;
    350     FT_UShort  flags;
    351     FT_ULong   offsetToData;
    352 
    353   } GX_GVar_Head;
    354 
    355 
    356   /*************************************************************************/
    357   /*                                                                       */
    358   /* <Function>                                                            */
    359   /*    ft_var_load_gvar                                                   */
    360   /*                                                                       */
    361   /* <Description>                                                         */
    362   /*    Parses the `gvar' table if present.  If `fvar' is there, `gvar'    */
    363   /*    had better be there too.                                           */
    364   /*                                                                       */
    365   /* <InOut>                                                               */
    366   /*    face :: The font face.                                             */
    367   /*                                                                       */
    368   /* <Return>                                                              */
    369   /*    FreeType error code.  0 means success.                             */
    370   /*                                                                       */
    371   static FT_Error
    372   ft_var_load_gvar( TT_Face  face )
    373   {
    374     FT_Stream     stream = FT_FACE_STREAM(face);
    375     FT_Memory     memory = stream->memory;
    376     GX_Blend      blend  = face->blend;
    377     FT_Error      error;
    378     FT_UInt       i, j;
    379     FT_ULong      table_len;
    380     FT_ULong      gvar_start;
    381     FT_ULong      offsetToData;
    382     GX_GVar_Head  gvar_head;
    383 
    384     static const FT_Frame_Field  gvar_fields[] =
    385     {
    386 
    387 #undef  FT_STRUCTURE
    388 #define FT_STRUCTURE  GX_GVar_Head
    389 
    390       FT_FRAME_START( 20 ),
    391         FT_FRAME_LONG  ( version ),
    392         FT_FRAME_USHORT( axisCount ),
    393         FT_FRAME_USHORT( globalCoordCount ),
    394         FT_FRAME_ULONG ( offsetToCoord ),
    395         FT_FRAME_USHORT( glyphCount ),
    396         FT_FRAME_USHORT( flags ),
    397         FT_FRAME_ULONG ( offsetToData ),
    398       FT_FRAME_END
    399     };
    400 
    401     if ( (error = face->goto_table( face, TTAG_gvar, stream, &table_len )) != 0 )
    402       goto Exit;
    403 
    404     gvar_start = FT_STREAM_POS( );
    405     if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) )
    406       goto Exit;
    407 
    408     blend->tuplecount  = gvar_head.globalCoordCount;
    409     blend->gv_glyphcnt = gvar_head.glyphCount;
    410     offsetToData       = gvar_start + gvar_head.offsetToData;
    411 
    412     if ( gvar_head.version   != (FT_Long)0x00010000L              ||
    413          gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
    414     {
    415       error = TT_Err_Invalid_Table;
    416       goto Exit;
    417     }
    418 
    419     if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) )
    420       goto Exit;
    421 
    422     if ( gvar_head.flags & 1 )
    423     {
    424       /* long offsets (one more offset than glyphs, to mark size of last) */
    425       if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) )
    426         goto Exit;
    427 
    428       for ( i = 0; i <= blend->gv_glyphcnt; ++i )
    429         blend->glyphoffsets[i] = offsetToData + FT_GET_LONG();
    430 
    431       FT_FRAME_EXIT();
    432     }
    433     else
    434     {
    435       /* short offsets (one more offset than glyphs, to mark size of last) */
    436       if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) )
    437         goto Exit;
    438 
    439       for ( i = 0; i <= blend->gv_glyphcnt; ++i )
    440         blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
    441                                               /* XXX: Undocumented: `*2'! */
    442 
    443       FT_FRAME_EXIT();
    444     }
    445 
    446     if ( blend->tuplecount != 0 )
    447     {
    448       if ( FT_NEW_ARRAY( blend->tuplecoords,
    449                          gvar_head.axisCount * blend->tuplecount ) )
    450         goto Exit;
    451 
    452       if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord )       ||
    453            FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L )                   )
    454         goto Exit;
    455 
    456       for ( i = 0; i < blend->tuplecount; ++i )
    457         for ( j = 0 ; j < (FT_UInt)gvar_head.axisCount; ++j )
    458           blend->tuplecoords[i * gvar_head.axisCount + j] =
    459             FT_GET_SHORT() << 2;                /* convert to FT_Fixed */
    460 
    461       FT_FRAME_EXIT();
    462     }
    463 
    464   Exit:
    465     return error;
    466   }
    467 
    468 
    469   /*************************************************************************/
    470   /*                                                                       */
    471   /* <Function>                                                            */
    472   /*    ft_var_apply_tuple                                                 */
    473   /*                                                                       */
    474   /* <Description>                                                         */
    475   /*    Figure out whether a given tuple (design) applies to the current   */
    476   /*    blend, and if so, what is the scaling factor.                      */
    477   /*                                                                       */
    478   /* <Input>                                                               */
    479   /*    blend           :: The current blend of the font.                  */
    480   /*                                                                       */
    481   /*    tupleIndex      :: A flag saying whether this is an intermediate   */
    482   /*                       tuple or not.                                   */
    483   /*                                                                       */
    484   /*    tuple_coords    :: The coordinates of the tuple in normalized axis */
    485   /*                       units.                                          */
    486   /*                                                                       */
    487   /*    im_start_coords :: The initial coordinates where this tuple starts */
    488   /*                       to apply (for intermediate coordinates).        */
    489   /*                                                                       */
    490   /*    im_end_coords   :: The final coordinates after which this tuple no */
    491   /*                       longer applies (for intermediate coordinates).  */
    492   /*                                                                       */
    493   /* <Return>                                                              */
    494   /*    An FT_Fixed value containing the scaling factor.                   */
    495   /*                                                                       */
    496   static FT_Fixed
    497   ft_var_apply_tuple( GX_Blend   blend,
    498                       FT_UShort  tupleIndex,
    499                       FT_Fixed*  tuple_coords,
    500                       FT_Fixed*  im_start_coords,
    501                       FT_Fixed*  im_end_coords )
    502   {
    503     FT_UInt   i;
    504     FT_Fixed  apply;
    505     FT_Fixed  temp;
    506 
    507 
    508     apply = 0x10000L;
    509     for ( i = 0; i < blend->num_axis; ++i )
    510     {
    511       if ( tuple_coords[i] == 0 )
    512         /* It's not clear why (for intermediate tuples) we don't need     */
    513         /* to check against start/end -- the documentation says we don't. */
    514         /* Similarly, it's unclear why we don't need to scale along the   */
    515         /* axis.                                                          */
    516         continue;
    517 
    518       else if ( blend->normalizedcoords[i] == 0                           ||
    519                 ( blend->normalizedcoords[i] < 0 && tuple_coords[i] > 0 ) ||
    520                 ( blend->normalizedcoords[i] > 0 && tuple_coords[i] < 0 ) )
    521       {
    522         apply = 0;
    523         break;
    524       }
    525 
    526       else if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
    527         /* not an intermediate tuple */
    528         apply = FT_MulDiv( apply,
    529                            blend->normalizedcoords[i] > 0
    530                              ? blend->normalizedcoords[i]
    531                              : -blend->normalizedcoords[i],
    532                            0x10000L );
    533 
    534       else if ( blend->normalizedcoords[i] <= im_start_coords[i] ||
    535                 blend->normalizedcoords[i] >= im_end_coords[i]   )
    536       {
    537         apply = 0;
    538         break;
    539       }
    540 
    541       else if ( blend->normalizedcoords[i] < tuple_coords[i] )
    542       {
    543         temp = FT_MulDiv( blend->normalizedcoords[i] - im_start_coords[i],
    544                           0x10000L,
    545                           tuple_coords[i] - im_start_coords[i]);
    546         apply = FT_MulDiv( apply, temp, 0x10000L );
    547       }
    548 
    549       else
    550       {
    551         temp = FT_MulDiv( im_end_coords[i] - blend->normalizedcoords[i],
    552                           0x10000L,
    553                           im_end_coords[i] - tuple_coords[i] );
    554         apply = FT_MulDiv( apply, temp, 0x10000L );
    555       }
    556     }
    557 
    558     return apply;
    559   }
    560 
    561 
    562   /*************************************************************************/
    563   /*************************************************************************/
    564   /*****                                                               *****/
    565   /*****               MULTIPLE MASTERS SERVICE FUNCTIONS              *****/
    566   /*****                                                               *****/
    567   /*************************************************************************/
    568   /*************************************************************************/
    569 
    570 
    571   typedef struct  GX_FVar_Head_
    572   {
    573     FT_Long    version;
    574     FT_UShort  offsetToData;
    575     FT_UShort  countSizePairs;
    576     FT_UShort  axisCount;
    577     FT_UShort  axisSize;
    578     FT_UShort  instanceCount;
    579     FT_UShort  instanceSize;
    580 
    581   } GX_FVar_Head;
    582 
    583 
    584   typedef struct  fvar_axis_
    585   {
    586     FT_ULong   axisTag;
    587     FT_ULong   minValue;
    588     FT_ULong   defaultValue;
    589     FT_ULong   maxValue;
    590     FT_UShort  flags;
    591     FT_UShort  nameID;
    592 
    593   } GX_FVar_Axis;
    594 
    595 
    596   /*************************************************************************/
    597   /*                                                                       */
    598   /* <Function>                                                            */
    599   /*    TT_Get_MM_Var                                                      */
    600   /*                                                                       */
    601   /* <Description>                                                         */
    602   /*    Check that the font's `fvar' table is valid, parse it, and return  */
    603   /*    those data.                                                        */
    604   /*                                                                       */
    605   /* <InOut>                                                               */
    606   /*    face   :: The font face.                                           */
    607   /*              TT_Get_MM_Var initializes the blend structure.           */
    608   /*                                                                       */
    609   /* <Output>                                                              */
    610   /*    master :: The `fvar' data (must be freed by caller).               */
    611   /*                                                                       */
    612   /* <Return>                                                              */
    613   /*    FreeType error code.  0 means success.                             */
    614   /*                                                                       */
    615   FT_LOCAL_DEF( FT_Error )
    616   TT_Get_MM_Var( TT_Face      face,
    617                  FT_MM_Var*  *master )
    618   {
    619     FT_Stream            stream = face->root.stream;
    620     FT_Memory            memory = face->root.memory;
    621     FT_ULong             table_len;
    622     FT_Error             error  = TT_Err_Ok;
    623     FT_ULong             fvar_start;
    624     FT_Int               i, j;
    625     FT_MM_Var*           mmvar = NULL;
    626     FT_Fixed*            next_coords;
    627     FT_String*           next_name;
    628     FT_Var_Axis*         a;
    629     FT_Var_Named_Style*  ns;
    630     GX_FVar_Head         fvar_head;
    631 
    632     static const FT_Frame_Field  fvar_fields[] =
    633     {
    634 
    635 #undef  FT_STRUCTURE
    636 #define FT_STRUCTURE  GX_FVar_Head
    637 
    638       FT_FRAME_START( 16 ),
    639         FT_FRAME_LONG  ( version ),
    640         FT_FRAME_USHORT( offsetToData ),
    641         FT_FRAME_USHORT( countSizePairs ),
    642         FT_FRAME_USHORT( axisCount ),
    643         FT_FRAME_USHORT( axisSize ),
    644         FT_FRAME_USHORT( instanceCount ),
    645         FT_FRAME_USHORT( instanceSize ),
    646       FT_FRAME_END
    647     };
    648 
    649     static const FT_Frame_Field  fvaraxis_fields[] =
    650     {
    651 
    652 #undef  FT_STRUCTURE
    653 #define FT_STRUCTURE  GX_FVar_Axis
    654 
    655       FT_FRAME_START( 20 ),
    656         FT_FRAME_ULONG ( axisTag ),
    657         FT_FRAME_ULONG ( minValue ),
    658         FT_FRAME_ULONG ( defaultValue ),
    659         FT_FRAME_ULONG ( maxValue ),
    660         FT_FRAME_USHORT( flags ),
    661         FT_FRAME_USHORT( nameID ),
    662       FT_FRAME_END
    663     };
    664 
    665 
    666     if ( face->blend == NULL )
    667     {
    668       /* both `fvar' and `gvar' must be present */
    669       if ( (error = face->goto_table( face, TTAG_gvar,
    670                                       stream, &table_len )) != 0 )
    671         goto Exit;
    672 
    673       if ( (error = face->goto_table( face, TTAG_fvar,
    674                                       stream, &table_len )) != 0 )
    675         goto Exit;
    676 
    677       fvar_start = FT_STREAM_POS( );
    678 
    679       if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
    680         goto Exit;
    681 
    682       if ( fvar_head.version != (FT_Long)0x00010000L                      ||
    683            fvar_head.countSizePairs != 2                                  ||
    684            fvar_head.axisSize != 20                                       ||
    685            /* axisCount limit implied by 16-bit instanceSize */
    686            fvar_head.axisCount > 0x3FFE                                   ||
    687            fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount          ||
    688            /* instanceCount limit implied by limited range of name IDs */
    689            fvar_head.instanceCount > 0x7EFF                               ||
    690            fvar_head.offsetToData + fvar_head.axisCount * 20U +
    691              fvar_head.instanceCount * fvar_head.instanceSize > table_len )
    692       {
    693         error = TT_Err_Invalid_Table;
    694         goto Exit;
    695       }
    696 
    697       if ( FT_NEW( face->blend ) )
    698         goto Exit;
    699 
    700       /* cannot overflow 32-bit arithmetic because of limits above */
    701       face->blend->mmvar_len =
    702         sizeof ( FT_MM_Var ) +
    703         fvar_head.axisCount * sizeof ( FT_Var_Axis ) +
    704         fvar_head.instanceCount * sizeof ( FT_Var_Named_Style ) +
    705         fvar_head.instanceCount * fvar_head.axisCount * sizeof ( FT_Fixed ) +
    706         5 * fvar_head.axisCount;
    707 
    708       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
    709         goto Exit;
    710       face->blend->mmvar = mmvar;
    711 
    712       mmvar->num_axis =
    713         fvar_head.axisCount;
    714       mmvar->num_designs =
    715         (FT_UInt)-1;           /* meaningless in this context; each glyph */
    716                                /* may have a different number of designs  */
    717                                /* (or tuples, as called by Apple)         */
    718       mmvar->num_namedstyles =
    719         fvar_head.instanceCount;
    720       mmvar->axis =
    721         (FT_Var_Axis*)&(mmvar[1]);
    722       mmvar->namedstyle =
    723         (FT_Var_Named_Style*)&(mmvar->axis[fvar_head.axisCount]);
    724 
    725       next_coords =
    726         (FT_Fixed*)&(mmvar->namedstyle[fvar_head.instanceCount]);
    727       for ( i = 0; i < fvar_head.instanceCount; ++i )
    728       {
    729         mmvar->namedstyle[i].coords  = next_coords;
    730         next_coords                 += fvar_head.axisCount;
    731       }
    732 
    733       next_name = (FT_String*)next_coords;
    734       for ( i = 0; i < fvar_head.axisCount; ++i )
    735       {
    736         mmvar->axis[i].name  = next_name;
    737         next_name           += 5;
    738       }
    739 
    740       if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
    741         goto Exit;
    742 
    743       a = mmvar->axis;
    744       for ( i = 0; i < fvar_head.axisCount; ++i )
    745       {
    746         GX_FVar_Axis  axis_rec;
    747 
    748 
    749         if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
    750           goto Exit;
    751         a->tag     = axis_rec.axisTag;
    752         a->minimum = axis_rec.minValue;     /* A Fixed */
    753         a->def     = axis_rec.defaultValue; /* A Fixed */
    754         a->maximum = axis_rec.maxValue;     /* A Fixed */
    755         a->strid   = axis_rec.nameID;
    756 
    757         a->name[0] = (FT_String)(   a->tag >> 24 );
    758         a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
    759         a->name[2] = (FT_String)( ( a->tag >>  8 ) & 0xFF );
    760         a->name[3] = (FT_String)( ( a->tag       ) & 0xFF );
    761         a->name[4] = 0;
    762 
    763         ++a;
    764       }
    765 
    766       ns = mmvar->namedstyle;
    767       for ( i = 0; i < fvar_head.instanceCount; ++i, ++ns )
    768       {
    769         if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) )
    770           goto Exit;
    771 
    772         ns->strid       =    FT_GET_USHORT();
    773         (void) /* flags = */ FT_GET_USHORT();
    774 
    775         for ( j = 0; j < fvar_head.axisCount; ++j )
    776           ns->coords[j] = FT_GET_ULONG();     /* A Fixed */
    777 
    778         FT_FRAME_EXIT();
    779       }
    780     }
    781 
    782     if ( master != NULL )
    783     {
    784       FT_UInt  n;
    785 
    786 
    787       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
    788         goto Exit;
    789       FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
    790 
    791       mmvar->axis =
    792         (FT_Var_Axis*)&(mmvar[1]);
    793       mmvar->namedstyle =
    794         (FT_Var_Named_Style*)&(mmvar->axis[mmvar->num_axis]);
    795       next_coords =
    796         (FT_Fixed*)&(mmvar->namedstyle[mmvar->num_namedstyles]);
    797 
    798       for ( n = 0; n < mmvar->num_namedstyles; ++n )
    799       {
    800         mmvar->namedstyle[n].coords  = next_coords;
    801         next_coords                 += mmvar->num_axis;
    802       }
    803 
    804       a = mmvar->axis;
    805       next_name = (FT_String*)next_coords;
    806       for ( n = 0; n < mmvar->num_axis; ++n )
    807       {
    808         a->name = next_name;
    809 
    810         /* standard PostScript names for some standard apple tags */
    811         if ( a->tag == TTAG_wght )
    812           a->name = (char *)"Weight";
    813         else if ( a->tag == TTAG_wdth )
    814           a->name = (char *)"Width";
    815         else if ( a->tag == TTAG_opsz )
    816           a->name = (char *)"OpticalSize";
    817         else if ( a->tag == TTAG_slnt )
    818           a->name = (char *)"Slant";
    819 
    820         next_name += 5;
    821         ++a;
    822       }
    823 
    824       *master = mmvar;
    825     }
    826 
    827   Exit:
    828     return error;
    829   }
    830 
    831 
    832   /*************************************************************************/
    833   /*                                                                       */
    834   /* <Function>                                                            */
    835   /*    TT_Set_MM_Blend                                                    */
    836   /*                                                                       */
    837   /* <Description>                                                         */
    838   /*    Set the blend (normalized) coordinates for this instance of the    */
    839   /*    font.  Check that the `gvar' table is reasonable and does some     */
    840   /*    initial preparation.                                               */
    841   /*                                                                       */
    842   /* <InOut>                                                               */
    843   /*    face       :: The font.                                            */
    844   /*                  Initialize the blend structure with `gvar' data.     */
    845   /*                                                                       */
    846   /* <Input>                                                               */
    847   /*    num_coords :: Must be the axis count of the font.                  */
    848   /*                                                                       */
    849   /*    coords     :: An array of num_coords, each between [-1,1].         */
    850   /*                                                                       */
    851   /* <Return>                                                              */
    852   /*    FreeType error code.  0 means success.                             */
    853   /*                                                                       */
    854   FT_LOCAL_DEF( FT_Error )
    855   TT_Set_MM_Blend( TT_Face    face,
    856                    FT_UInt    num_coords,
    857                    FT_Fixed*  coords )
    858   {
    859     FT_Error    error = TT_Err_Ok;
    860     GX_Blend    blend;
    861     FT_MM_Var*  mmvar;
    862     FT_UInt     i;
    863     FT_Memory   memory = face->root.memory;
    864 
    865     enum
    866     {
    867       mcvt_retain,
    868       mcvt_modify,
    869       mcvt_load
    870 
    871     } manageCvt;
    872 
    873 
    874     face->doblend = FALSE;
    875 
    876     if ( face->blend == NULL )
    877     {
    878       if ( (error = TT_Get_MM_Var( face, NULL)) != 0 )
    879         goto Exit;
    880     }
    881 
    882     blend = face->blend;
    883     mmvar = blend->mmvar;
    884 
    885     if ( num_coords != mmvar->num_axis )
    886     {
    887       error = TT_Err_Invalid_Argument;
    888       goto Exit;
    889     }
    890 
    891     for ( i = 0; i < num_coords; ++i )
    892       if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
    893       {
    894         error = TT_Err_Invalid_Argument;
    895         goto Exit;
    896       }
    897 
    898     if ( blend->glyphoffsets == NULL )
    899       if ( (error = ft_var_load_gvar( face )) != 0 )
    900         goto Exit;
    901 
    902     if ( blend->normalizedcoords == NULL )
    903     {
    904       if ( FT_NEW_ARRAY( blend->normalizedcoords, num_coords ) )
    905         goto Exit;
    906 
    907       manageCvt = mcvt_modify;
    908 
    909       /* If we have not set the blend coordinates before this, then the  */
    910       /* cvt table will still be what we read from the `cvt ' table and  */
    911       /* we don't need to reload it.  We may need to change it though... */
    912     }
    913     else
    914     {
    915       manageCvt = mcvt_retain;
    916       for ( i = 0; i < num_coords; ++i )
    917       {
    918         if ( blend->normalizedcoords[i] != coords[i] )
    919         {
    920           manageCvt = mcvt_load;
    921           break;
    922         }
    923       }
    924 
    925       /* If we don't change the blend coords then we don't need to do  */
    926       /* anything to the cvt table.  It will be correct.  Otherwise we */
    927       /* no longer have the original cvt (it was modified when we set  */
    928       /* the blend last time), so we must reload and then modify it.   */
    929     }
    930 
    931     blend->num_axis = num_coords;
    932     FT_MEM_COPY( blend->normalizedcoords,
    933                  coords,
    934                  num_coords * sizeof ( FT_Fixed ) );
    935 
    936     face->doblend = TRUE;
    937 
    938     if ( face->cvt != NULL )
    939     {
    940       switch ( manageCvt )
    941       {
    942       case mcvt_load:
    943         /* The cvt table has been loaded already; every time we change the */
    944         /* blend we may need to reload and remodify the cvt table.         */
    945         FT_FREE( face->cvt );
    946         face->cvt = NULL;
    947 
    948         tt_face_load_cvt( face, face->root.stream );
    949         break;
    950 
    951       case mcvt_modify:
    952         /* The original cvt table is in memory.  All we need to do is */
    953         /* apply the `cvar' table (if any).                           */
    954         tt_face_vary_cvt( face, face->root.stream );
    955         break;
    956 
    957       case mcvt_retain:
    958         /* The cvt table is correct for this set of coordinates. */
    959         break;
    960       }
    961     }
    962 
    963   Exit:
    964     return error;
    965   }
    966 
    967 
    968   /*************************************************************************/
    969   /*                                                                       */
    970   /* <Function>                                                            */
    971   /*    TT_Set_Var_Design                                                  */
    972   /*                                                                       */
    973   /* <Description>                                                         */
    974   /*    Set the coordinates for the instance, measured in the user         */
    975   /*    coordinate system.  Parse the `avar' table (if present) to convert */
    976   /*    from user to normalized coordinates.                               */
    977   /*                                                                       */
    978   /* <InOut>                                                               */
    979   /*    face       :: The font face.                                       */
    980   /*                  Initialize the blend struct with `gvar' data.        */
    981   /*                                                                       */
    982   /* <Input>                                                               */
    983   /*    num_coords :: This must be the axis count of the font.             */
    984   /*                                                                       */
    985   /*    coords     :: A coordinate array with `num_coords' elements.       */
    986   /*                                                                       */
    987   /* <Return>                                                              */
    988   /*    FreeType error code.  0 means success.                             */
    989   /*                                                                       */
    990   FT_LOCAL_DEF( FT_Error )
    991   TT_Set_Var_Design( TT_Face    face,
    992                      FT_UInt    num_coords,
    993                      FT_Fixed*  coords )
    994   {
    995     FT_Error        error      = TT_Err_Ok;
    996     FT_Fixed*       normalized = NULL;
    997     GX_Blend        blend;
    998     FT_MM_Var*      mmvar;
    999     FT_UInt         i, j;
   1000     FT_Var_Axis*    a;
   1001     GX_AVarSegment  av;
   1002     FT_Memory       memory = face->root.memory;
   1003 
   1004 
   1005     if ( face->blend == NULL )
   1006     {
   1007       if ( (error = TT_Get_MM_Var( face, NULL )) != 0 )
   1008         goto Exit;
   1009     }
   1010 
   1011     blend = face->blend;
   1012     mmvar = blend->mmvar;
   1013 
   1014     if ( num_coords != mmvar->num_axis )
   1015     {
   1016       error = TT_Err_Invalid_Argument;
   1017       goto Exit;
   1018     }
   1019 
   1020     /* Axis normalization is a two stage process.  First we normalize */
   1021     /* based on the [min,def,max] values for the axis to be [-1,0,1]. */
   1022     /* Then, if there's an `avar' table, we renormalize this range.   */
   1023 
   1024     if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
   1025       goto Exit;
   1026 
   1027     a = mmvar->axis;
   1028     for ( i = 0; i < mmvar->num_axis; ++i, ++a )
   1029     {
   1030       if ( coords[i] > a->maximum || coords[i] < a->minimum )
   1031       {
   1032         error = TT_Err_Invalid_Argument;
   1033         goto Exit;
   1034       }
   1035 
   1036       if ( coords[i] < a->def )
   1037       {
   1038         normalized[i] = -FT_MulDiv( coords[i] - a->def,
   1039                                     0x10000L,
   1040                                     a->minimum - a->def );
   1041       }
   1042       else if ( a->maximum == a->def )
   1043         normalized[i] = 0;
   1044       else
   1045       {
   1046         normalized[i] = FT_MulDiv( coords[i] - a->def,
   1047                                    0x10000L,
   1048                                    a->maximum - a->def );
   1049       }
   1050     }
   1051 
   1052     if ( !blend->avar_checked )
   1053       ft_var_load_avar( face );
   1054 
   1055     if ( blend->avar_segment != NULL )
   1056     {
   1057       av = blend->avar_segment;
   1058       for ( i = 0; i < mmvar->num_axis; ++i, ++av )
   1059       {
   1060         for ( j = 1; j < (FT_UInt)av->pairCount; ++j )
   1061           if ( normalized[i] < av->correspondence[j].fromCoord )
   1062           {
   1063             normalized[i] =
   1064               FT_MulDiv(
   1065                 FT_MulDiv(
   1066                   normalized[i] - av->correspondence[j - 1].fromCoord,
   1067                   0x10000L,
   1068                   av->correspondence[j].fromCoord -
   1069                     av->correspondence[j - 1].fromCoord ),
   1070                 av->correspondence[j].toCoord -
   1071                   av->correspondence[j - 1].toCoord,
   1072                 0x10000L ) +
   1073               av->correspondence[j - 1].toCoord;
   1074             break;
   1075           }
   1076       }
   1077     }
   1078 
   1079     error = TT_Set_MM_Blend( face, num_coords, normalized );
   1080 
   1081   Exit:
   1082     FT_FREE( normalized );
   1083     return error;
   1084   }
   1085 
   1086 
   1087   /*************************************************************************/
   1088   /*************************************************************************/
   1089   /*****                                                               *****/
   1090   /*****                     GX VAR PARSING ROUTINES                   *****/
   1091   /*****                                                               *****/
   1092   /*************************************************************************/
   1093   /*************************************************************************/
   1094 
   1095 
   1096   /*************************************************************************/
   1097   /*                                                                       */
   1098   /* <Function>                                                            */
   1099   /*    tt_face_vary_cvt                                                   */
   1100   /*                                                                       */
   1101   /* <Description>                                                         */
   1102   /*    Modify the loaded cvt table according to the `cvar' table and the  */
   1103   /*    font's blend.                                                      */
   1104   /*                                                                       */
   1105   /* <InOut>                                                               */
   1106   /*    face   :: A handle to the target face object.                      */
   1107   /*                                                                       */
   1108   /* <Input>                                                               */
   1109   /*    stream :: A handle to the input stream.                            */
   1110   /*                                                                       */
   1111   /* <Return>                                                              */
   1112   /*    FreeType error code.  0 means success.                             */
   1113   /*                                                                       */
   1114   /*    Most errors are ignored.  It is perfectly valid not to have a      */
   1115   /*    `cvar' table even if there is a `gvar' and `fvar' table.           */
   1116   /*                                                                       */
   1117   FT_LOCAL_DEF( FT_Error )
   1118   tt_face_vary_cvt( TT_Face    face,
   1119                     FT_Stream  stream )
   1120   {
   1121     FT_Error    error;
   1122     FT_Memory   memory = stream->memory;
   1123     FT_ULong    table_start;
   1124     FT_ULong    table_len;
   1125     FT_UInt     tupleCount;
   1126     FT_ULong    offsetToData;
   1127     FT_ULong    here;
   1128     FT_UInt     i, j;
   1129     FT_Fixed*   tuple_coords    = NULL;
   1130     FT_Fixed*   im_start_coords = NULL;
   1131     FT_Fixed*   im_end_coords   = NULL;
   1132     GX_Blend    blend           = face->blend;
   1133     FT_UInt     point_count;
   1134     FT_UShort*  localpoints;
   1135     FT_Short*   deltas;
   1136 
   1137 
   1138     FT_TRACE2(( "CVAR " ));
   1139 
   1140     if ( blend == NULL )
   1141     {
   1142       FT_TRACE2(( "tt_face_vary_cvt: no blend specified\n" ));
   1143 
   1144       error = TT_Err_Ok;
   1145       goto Exit;
   1146     }
   1147 
   1148     if ( face->cvt == NULL )
   1149     {
   1150       FT_TRACE2(( "tt_face_vary_cvt: no `cvt ' table\n" ));
   1151 
   1152       error = TT_Err_Ok;
   1153       goto Exit;
   1154     }
   1155 
   1156     error = face->goto_table( face, TTAG_cvar, stream, &table_len );
   1157     if ( error )
   1158     {
   1159       FT_TRACE2(( "is missing\n" ));
   1160 
   1161       error = TT_Err_Ok;
   1162       goto Exit;
   1163     }
   1164 
   1165     if ( FT_FRAME_ENTER( table_len ) )
   1166     {
   1167       error = TT_Err_Ok;
   1168       goto Exit;
   1169     }
   1170 
   1171     table_start = FT_Stream_FTell( stream );
   1172     if ( FT_GET_LONG() != 0x00010000L )
   1173     {
   1174       FT_TRACE2(( "bad table version\n" ));
   1175 
   1176       error = TT_Err_Ok;
   1177       goto FExit;
   1178     }
   1179 
   1180     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
   1181          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
   1182          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
   1183       goto FExit;
   1184 
   1185     tupleCount   = FT_GET_USHORT();
   1186     offsetToData = table_start + FT_GET_USHORT();
   1187 
   1188     /* The documentation implies there are flags packed into the        */
   1189     /* tuplecount, but John Jenkins says that shared points don't apply */
   1190     /* to `cvar', and no other flags are defined.                       */
   1191 
   1192     for ( i = 0; i < ( tupleCount & 0xFFF ); ++i )
   1193     {
   1194       FT_UInt   tupleDataSize;
   1195       FT_UInt   tupleIndex;
   1196       FT_Fixed  apply;
   1197 
   1198 
   1199       tupleDataSize = FT_GET_USHORT();
   1200       tupleIndex    = FT_GET_USHORT();
   1201 
   1202       /* There is no provision here for a global tuple coordinate section, */
   1203       /* so John says.  There are no tuple indices, just embedded tuples.  */
   1204 
   1205       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
   1206       {
   1207         for ( j = 0; j < blend->num_axis; ++j )
   1208           tuple_coords[j] = FT_GET_SHORT() << 2; /* convert from        */
   1209                                                  /* short frac to fixed */
   1210       }
   1211       else
   1212       {
   1213         /* skip this tuple; it makes no sense */
   1214 
   1215         if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
   1216           for ( j = 0; j < 2 * blend->num_axis; ++j )
   1217             (void)FT_GET_SHORT();
   1218 
   1219         offsetToData += tupleDataSize;
   1220         continue;
   1221       }
   1222 
   1223       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
   1224       {
   1225         for ( j = 0; j < blend->num_axis; ++j )
   1226           im_start_coords[j] = FT_GET_SHORT() << 2;
   1227         for ( j = 0; j < blend->num_axis; ++j )
   1228           im_end_coords[j] = FT_GET_SHORT() << 2;
   1229       }
   1230 
   1231       apply = ft_var_apply_tuple( blend,
   1232                                   (FT_UShort)tupleIndex,
   1233                                   tuple_coords,
   1234                                   im_start_coords,
   1235                                   im_end_coords );
   1236       if ( /* tuple isn't active for our blend */
   1237            apply == 0                                    ||
   1238            /* global points not allowed,           */
   1239            /* if they aren't local, makes no sense */
   1240            !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) )
   1241       {
   1242         offsetToData += tupleDataSize;
   1243         continue;
   1244       }
   1245 
   1246       here = FT_Stream_FTell( stream );
   1247 
   1248       FT_Stream_SeekSet( stream, offsetToData );
   1249 
   1250       localpoints = ft_var_readpackedpoints( stream, &point_count );
   1251       deltas      = ft_var_readpackeddeltas( stream,
   1252                                              point_count == 0 ? face->cvt_size
   1253                                                               : point_count );
   1254       if ( localpoints == NULL || deltas == NULL )
   1255         /* failure, ignore it */;
   1256 
   1257       else if ( localpoints == ALL_POINTS )
   1258       {
   1259         /* this means that there are deltas for every entry in cvt */
   1260         for ( j = 0; j < face->cvt_size; ++j )
   1261           face->cvt[j] = (FT_Short)( face->cvt[j] +
   1262                                      FT_MulFix( deltas[j], apply ) );
   1263       }
   1264 
   1265       else
   1266       {
   1267         for ( j = 0; j < point_count; ++j )
   1268         {
   1269           int  pindex = localpoints[j];
   1270 
   1271           face->cvt[pindex] = (FT_Short)( face->cvt[pindex] +
   1272                                           FT_MulFix( deltas[j], apply ) );
   1273         }
   1274       }
   1275 
   1276       if ( localpoints != ALL_POINTS )
   1277         FT_FREE( localpoints );
   1278       FT_FREE( deltas );
   1279 
   1280       offsetToData += tupleDataSize;
   1281 
   1282       FT_Stream_SeekSet( stream, here );
   1283     }
   1284 
   1285   FExit:
   1286     FT_FRAME_EXIT();
   1287 
   1288   Exit:
   1289     FT_FREE( tuple_coords );
   1290     FT_FREE( im_start_coords );
   1291     FT_FREE( im_end_coords );
   1292 
   1293     return error;
   1294   }
   1295 
   1296 
   1297   /*************************************************************************/
   1298   /*                                                                       */
   1299   /* <Function>                                                            */
   1300   /*    TT_Vary_Get_Glyph_Deltas                                           */
   1301   /*                                                                       */
   1302   /* <Description>                                                         */
   1303   /*    Load the appropriate deltas for the current glyph.                 */
   1304   /*                                                                       */
   1305   /* <Input>                                                               */
   1306   /*    face        :: A handle to the target face object.                 */
   1307   /*                                                                       */
   1308   /*    glyph_index :: The index of the glyph being modified.              */
   1309   /*                                                                       */
   1310   /*    n_points    :: The number of the points in the glyph, including    */
   1311   /*                   phantom points.                                     */
   1312   /*                                                                       */
   1313   /* <Output>                                                              */
   1314   /*    deltas      :: The array of points to change.                      */
   1315   /*                                                                       */
   1316   /* <Return>                                                              */
   1317   /*    FreeType error code.  0 means success.                             */
   1318   /*                                                                       */
   1319   FT_LOCAL_DEF( FT_Error )
   1320   TT_Vary_Get_Glyph_Deltas( TT_Face      face,
   1321                             FT_UInt      glyph_index,
   1322                             FT_Vector*  *deltas,
   1323                             FT_UInt      n_points )
   1324   {
   1325     FT_Stream   stream = face->root.stream;
   1326     FT_Memory   memory = stream->memory;
   1327     GX_Blend    blend  = face->blend;
   1328     FT_Vector*  delta_xy = NULL;
   1329 
   1330     FT_Error    error;
   1331     FT_ULong    glyph_start;
   1332     FT_UInt     tupleCount;
   1333     FT_ULong    offsetToData;
   1334     FT_ULong    here;
   1335     FT_UInt     i, j;
   1336     FT_Fixed*   tuple_coords    = NULL;
   1337     FT_Fixed*   im_start_coords = NULL;
   1338     FT_Fixed*   im_end_coords   = NULL;
   1339     FT_UInt     point_count, spoint_count = 0;
   1340     FT_UShort*  sharedpoints = NULL;
   1341     FT_UShort*  localpoints  = NULL;
   1342     FT_UShort*  points;
   1343     FT_Short    *deltas_x, *deltas_y;
   1344 
   1345 
   1346     if ( !face->doblend || blend == NULL )
   1347       return TT_Err_Invalid_Argument;
   1348 
   1349     /* to be freed by the caller */
   1350     if ( FT_NEW_ARRAY( delta_xy, n_points ) )
   1351       goto Exit;
   1352     *deltas = delta_xy;
   1353 
   1354     if ( glyph_index >= blend->gv_glyphcnt      ||
   1355          blend->glyphoffsets[glyph_index] ==
   1356            blend->glyphoffsets[glyph_index + 1] )
   1357       return TT_Err_Ok;               /* no variation data for this glyph */
   1358 
   1359     if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] )   ||
   1360          FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] -
   1361                            blend->glyphoffsets[glyph_index] ) )
   1362       goto Fail1;
   1363 
   1364     glyph_start = FT_Stream_FTell( stream );
   1365 
   1366     /* each set of glyph variation data is formatted similarly to `cvar' */
   1367     /* (except we get shared points and global tuples)                   */
   1368 
   1369     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
   1370          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
   1371          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
   1372       goto Fail2;
   1373 
   1374     tupleCount   = FT_GET_USHORT();
   1375     offsetToData = glyph_start + FT_GET_USHORT();
   1376 
   1377     if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
   1378     {
   1379       here = FT_Stream_FTell( stream );
   1380 
   1381       FT_Stream_SeekSet( stream, offsetToData );
   1382 
   1383       sharedpoints = ft_var_readpackedpoints( stream, &spoint_count );
   1384       offsetToData = FT_Stream_FTell( stream );
   1385 
   1386       FT_Stream_SeekSet( stream, here );
   1387     }
   1388 
   1389     for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); ++i )
   1390     {
   1391       FT_UInt   tupleDataSize;
   1392       FT_UInt   tupleIndex;
   1393       FT_Fixed  apply;
   1394 
   1395 
   1396       tupleDataSize = FT_GET_USHORT();
   1397       tupleIndex    = FT_GET_USHORT();
   1398 
   1399       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
   1400       {
   1401         for ( j = 0; j < blend->num_axis; ++j )
   1402           tuple_coords[j] = FT_GET_SHORT() << 2;  /* convert from        */
   1403                                                   /* short frac to fixed */
   1404       }
   1405       else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
   1406       {
   1407         error = TT_Err_Invalid_Table;
   1408         goto Fail3;
   1409       }
   1410       else
   1411       {
   1412         FT_MEM_COPY(
   1413           tuple_coords,
   1414           &blend->tuplecoords[(tupleIndex & 0xFFF) * blend->num_axis],
   1415           blend->num_axis * sizeof ( FT_Fixed ) );
   1416       }
   1417 
   1418       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
   1419       {
   1420         for ( j = 0; j < blend->num_axis; ++j )
   1421           im_start_coords[j] = FT_GET_SHORT() << 2;
   1422         for ( j = 0; j < blend->num_axis; ++j )
   1423           im_end_coords[j] = FT_GET_SHORT() << 2;
   1424       }
   1425 
   1426       apply = ft_var_apply_tuple( blend,
   1427                                   (FT_UShort)tupleIndex,
   1428                                   tuple_coords,
   1429                                   im_start_coords,
   1430                                   im_end_coords );
   1431 
   1432       if ( apply == 0 )              /* tuple isn't active for our blend */
   1433       {
   1434         offsetToData += tupleDataSize;
   1435         continue;
   1436       }
   1437 
   1438       here = FT_Stream_FTell( stream );
   1439 
   1440       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
   1441       {
   1442         FT_Stream_SeekSet( stream, offsetToData );
   1443 
   1444         localpoints = ft_var_readpackedpoints( stream, &point_count );
   1445         points      = localpoints;
   1446       }
   1447       else
   1448       {
   1449         points      = sharedpoints;
   1450         point_count = spoint_count;
   1451       }
   1452 
   1453       deltas_x = ft_var_readpackeddeltas( stream,
   1454                                           point_count == 0 ? n_points
   1455                                                            : point_count );
   1456       deltas_y = ft_var_readpackeddeltas( stream,
   1457                                           point_count == 0 ? n_points
   1458                                                            : point_count );
   1459 
   1460       if ( points == NULL || deltas_y == NULL || deltas_x == NULL )
   1461         ; /* failure, ignore it */
   1462 
   1463       else if ( points == ALL_POINTS )
   1464       {
   1465         /* this means that there are deltas for every point in the glyph */
   1466         for ( j = 0; j < n_points; ++j )
   1467         {
   1468           delta_xy[j].x += FT_MulFix( deltas_x[j], apply );
   1469           delta_xy[j].y += FT_MulFix( deltas_y[j], apply );
   1470         }
   1471       }
   1472 
   1473       else
   1474       {
   1475         for ( j = 0; j < point_count; ++j )
   1476         {
   1477           if ( localpoints[j] >= n_points )
   1478             continue;
   1479 
   1480           delta_xy[localpoints[j]].x += FT_MulFix( deltas_x[j], apply );
   1481           delta_xy[localpoints[j]].y += FT_MulFix( deltas_y[j], apply );
   1482         }
   1483       }
   1484 
   1485       if ( localpoints != ALL_POINTS )
   1486         FT_FREE( localpoints );
   1487       FT_FREE( deltas_x );
   1488       FT_FREE( deltas_y );
   1489 
   1490       offsetToData += tupleDataSize;
   1491 
   1492       FT_Stream_SeekSet( stream, here );
   1493     }
   1494 
   1495   Fail3:
   1496     FT_FREE( tuple_coords );
   1497     FT_FREE( im_start_coords );
   1498     FT_FREE( im_end_coords );
   1499 
   1500   Fail2:
   1501     FT_FRAME_EXIT();
   1502 
   1503   Fail1:
   1504     if ( error )
   1505     {
   1506       FT_FREE( delta_xy );
   1507       *deltas = NULL;
   1508     }
   1509 
   1510   Exit:
   1511     return error;
   1512   }
   1513 
   1514 
   1515   /*************************************************************************/
   1516   /*                                                                       */
   1517   /* <Function>                                                            */
   1518   /*    tt_done_blend                                                      */
   1519   /*                                                                       */
   1520   /* <Description>                                                         */
   1521   /*    Frees the blend internal data structure.                           */
   1522   /*                                                                       */
   1523   FT_LOCAL_DEF( void )
   1524   tt_done_blend( FT_Memory  memory,
   1525                  GX_Blend   blend )
   1526   {
   1527     if ( blend != NULL )
   1528     {
   1529       FT_UInt  i;
   1530 
   1531 
   1532       FT_FREE( blend->normalizedcoords );
   1533       FT_FREE( blend->mmvar );
   1534 
   1535       if ( blend->avar_segment != NULL )
   1536       {
   1537         for ( i = 0; i < blend->num_axis; ++i )
   1538           FT_FREE( blend->avar_segment[i].correspondence );
   1539         FT_FREE( blend->avar_segment );
   1540       }
   1541 
   1542       FT_FREE( blend->tuplecoords );
   1543       FT_FREE( blend->glyphoffsets );
   1544       FT_FREE( blend );
   1545     }
   1546   }
   1547 
   1548 #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
   1549 
   1550 
   1551 /* END */
   1552