Home | History | Annotate | Download | only in truetype
      1 /***************************************************************************/
      2 /*                                                                         */
      3 /*  ttgxvar.c                                                              */
      4 /*                                                                         */
      5 /*    TrueType GX Font Variation loader                                    */
      6 /*                                                                         */
      7 /*  Copyright 2004, 2005, 2006, 2007, 2008, 2009 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;
    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 )
    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 )
    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;
    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;
    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            fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount          ||
    686            fvar_head.offsetToData + fvar_head.axisCount * 20U +
    687              fvar_head.instanceCount * fvar_head.instanceSize > table_len )
    688       {
    689         error = TT_Err_Invalid_Table;
    690         goto Exit;
    691       }
    692 
    693       if ( FT_NEW( face->blend ) )
    694         goto Exit;
    695 
    696       /* XXX: TODO - check for overflows */
    697       face->blend->mmvar_len =
    698         sizeof ( FT_MM_Var ) +
    699         fvar_head.axisCount * sizeof ( FT_Var_Axis ) +
    700         fvar_head.instanceCount * sizeof ( FT_Var_Named_Style ) +
    701         fvar_head.instanceCount * fvar_head.axisCount * sizeof ( FT_Fixed ) +
    702         5 * fvar_head.axisCount;
    703 
    704       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
    705         goto Exit;
    706       face->blend->mmvar = mmvar;
    707 
    708       mmvar->num_axis =
    709         fvar_head.axisCount;
    710       mmvar->num_designs =
    711         (FT_UInt)-1;           /* meaningless in this context; each glyph */
    712                                /* may have a different number of designs  */
    713                                /* (or tuples, as called by Apple)         */
    714       mmvar->num_namedstyles =
    715         fvar_head.instanceCount;
    716       mmvar->axis =
    717         (FT_Var_Axis*)&(mmvar[1]);
    718       mmvar->namedstyle =
    719         (FT_Var_Named_Style*)&(mmvar->axis[fvar_head.axisCount]);
    720 
    721       next_coords =
    722         (FT_Fixed*)&(mmvar->namedstyle[fvar_head.instanceCount]);
    723       for ( i = 0; i < fvar_head.instanceCount; ++i )
    724       {
    725         mmvar->namedstyle[i].coords  = next_coords;
    726         next_coords                 += fvar_head.axisCount;
    727       }
    728 
    729       next_name = (FT_String*)next_coords;
    730       for ( i = 0; i < fvar_head.axisCount; ++i )
    731       {
    732         mmvar->axis[i].name  = next_name;
    733         next_name           += 5;
    734       }
    735 
    736       if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
    737         goto Exit;
    738 
    739       a = mmvar->axis;
    740       for ( i = 0; i < fvar_head.axisCount; ++i )
    741       {
    742         GX_FVar_Axis  axis_rec;
    743 
    744 
    745         if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
    746           goto Exit;
    747         a->tag     = axis_rec.axisTag;
    748         a->minimum = axis_rec.minValue;     /* A Fixed */
    749         a->def     = axis_rec.defaultValue; /* A Fixed */
    750         a->maximum = axis_rec.maxValue;     /* A Fixed */
    751         a->strid   = axis_rec.nameID;
    752 
    753         a->name[0] = (FT_String)(   a->tag >> 24 );
    754         a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
    755         a->name[2] = (FT_String)( ( a->tag >>  8 ) & 0xFF );
    756         a->name[3] = (FT_String)( ( a->tag       ) & 0xFF );
    757         a->name[4] = 0;
    758 
    759         ++a;
    760       }
    761 
    762       ns = mmvar->namedstyle;
    763       for ( i = 0; i < fvar_head.instanceCount; ++i, ++ns )
    764       {
    765         if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) )
    766           goto Exit;
    767 
    768         ns->strid       =    FT_GET_USHORT();
    769         (void) /* flags = */ FT_GET_USHORT();
    770 
    771         for ( j = 0; j < fvar_head.axisCount; ++j )
    772           ns->coords[j] = FT_GET_ULONG();     /* A Fixed */
    773 
    774         FT_FRAME_EXIT();
    775       }
    776     }
    777 
    778     if ( master != NULL )
    779     {
    780       FT_UInt  n;
    781 
    782 
    783       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
    784         goto Exit;
    785       FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
    786 
    787       mmvar->axis =
    788         (FT_Var_Axis*)&(mmvar[1]);
    789       mmvar->namedstyle =
    790         (FT_Var_Named_Style*)&(mmvar->axis[mmvar->num_axis]);
    791       next_coords =
    792         (FT_Fixed*)&(mmvar->namedstyle[mmvar->num_namedstyles]);
    793 
    794       for ( n = 0; n < mmvar->num_namedstyles; ++n )
    795       {
    796         mmvar->namedstyle[n].coords  = next_coords;
    797         next_coords                 += mmvar->num_axis;
    798       }
    799 
    800       a = mmvar->axis;
    801       next_name = (FT_String*)next_coords;
    802       for ( n = 0; n < mmvar->num_axis; ++n )
    803       {
    804         a->name = next_name;
    805 
    806         /* standard PostScript names for some standard apple tags */
    807         if ( a->tag == TTAG_wght )
    808           a->name = (char *)"Weight";
    809         else if ( a->tag == TTAG_wdth )
    810           a->name = (char *)"Width";
    811         else if ( a->tag == TTAG_opsz )
    812           a->name = (char *)"OpticalSize";
    813         else if ( a->tag == TTAG_slnt )
    814           a->name = (char *)"Slant";
    815 
    816         next_name += 5;
    817         ++a;
    818       }
    819 
    820       *master = mmvar;
    821     }
    822 
    823   Exit:
    824     return error;
    825   }
    826 
    827 
    828   /*************************************************************************/
    829   /*                                                                       */
    830   /* <Function>                                                            */
    831   /*    TT_Set_MM_Blend                                                    */
    832   /*                                                                       */
    833   /* <Description>                                                         */
    834   /*    Set the blend (normalized) coordinates for this instance of the    */
    835   /*    font.  Check that the `gvar' table is reasonable and does some     */
    836   /*    initial preparation.                                               */
    837   /*                                                                       */
    838   /* <InOut>                                                               */
    839   /*    face       :: The font.                                            */
    840   /*                  Initialize the blend structure with `gvar' data.     */
    841   /*                                                                       */
    842   /* <Input>                                                               */
    843   /*    num_coords :: Must be the axis count of the font.                  */
    844   /*                                                                       */
    845   /*    coords     :: An array of num_coords, each between [-1,1].         */
    846   /*                                                                       */
    847   /* <Return>                                                              */
    848   /*    FreeType error code.  0 means success.                             */
    849   /*                                                                       */
    850   FT_LOCAL_DEF( FT_Error )
    851   TT_Set_MM_Blend( TT_Face    face,
    852                    FT_UInt    num_coords,
    853                    FT_Fixed*  coords )
    854   {
    855     FT_Error    error = TT_Err_Ok;
    856     GX_Blend    blend;
    857     FT_MM_Var*  mmvar;
    858     FT_UInt     i;
    859     FT_Memory   memory = face->root.memory;
    860 
    861     enum
    862     {
    863       mcvt_retain,
    864       mcvt_modify,
    865       mcvt_load
    866 
    867     } manageCvt;
    868 
    869 
    870     face->doblend = FALSE;
    871 
    872     if ( face->blend == NULL )
    873     {
    874       if ( (error = TT_Get_MM_Var( face, NULL)) != 0 )
    875         goto Exit;
    876     }
    877 
    878     blend = face->blend;
    879     mmvar = blend->mmvar;
    880 
    881     if ( num_coords != mmvar->num_axis )
    882     {
    883       error = TT_Err_Invalid_Argument;
    884       goto Exit;
    885     }
    886 
    887     for ( i = 0; i < num_coords; ++i )
    888       if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
    889       {
    890         error = TT_Err_Invalid_Argument;
    891         goto Exit;
    892       }
    893 
    894     if ( blend->glyphoffsets == NULL )
    895       if ( (error = ft_var_load_gvar( face )) != 0 )
    896         goto Exit;
    897 
    898     if ( blend->normalizedcoords == NULL )
    899     {
    900       if ( FT_NEW_ARRAY( blend->normalizedcoords, num_coords ) )
    901         goto Exit;
    902 
    903       manageCvt = mcvt_modify;
    904 
    905       /* If we have not set the blend coordinates before this, then the  */
    906       /* cvt table will still be what we read from the `cvt ' table and  */
    907       /* we don't need to reload it.  We may need to change it though... */
    908     }
    909     else
    910     {
    911       manageCvt = mcvt_retain;
    912       for ( i = 0; i < num_coords; ++i )
    913       {
    914         if ( blend->normalizedcoords[i] != coords[i] )
    915         {
    916           manageCvt = mcvt_load;
    917           break;
    918         }
    919       }
    920 
    921       /* If we don't change the blend coords then we don't need to do  */
    922       /* anything to the cvt table.  It will be correct.  Otherwise we */
    923       /* no longer have the original cvt (it was modified when we set  */
    924       /* the blend last time), so we must reload and then modify it.   */
    925     }
    926 
    927     blend->num_axis = num_coords;
    928     FT_MEM_COPY( blend->normalizedcoords,
    929                  coords,
    930                  num_coords * sizeof ( FT_Fixed ) );
    931 
    932     face->doblend = TRUE;
    933 
    934     if ( face->cvt != NULL )
    935     {
    936       switch ( manageCvt )
    937       {
    938       case mcvt_load:
    939         /* The cvt table has been loaded already; every time we change the */
    940         /* blend we may need to reload and remodify the cvt table.         */
    941         FT_FREE( face->cvt );
    942         face->cvt = NULL;
    943 
    944         tt_face_load_cvt( face, face->root.stream );
    945         break;
    946 
    947       case mcvt_modify:
    948         /* The original cvt table is in memory.  All we need to do is */
    949         /* apply the `cvar' table (if any).                           */
    950         tt_face_vary_cvt( face, face->root.stream );
    951         break;
    952 
    953       case mcvt_retain:
    954         /* The cvt table is correct for this set of coordinates. */
    955         break;
    956       }
    957     }
    958 
    959   Exit:
    960     return error;
    961   }
    962 
    963 
    964   /*************************************************************************/
    965   /*                                                                       */
    966   /* <Function>                                                            */
    967   /*    TT_Set_Var_Design                                                  */
    968   /*                                                                       */
    969   /* <Description>                                                         */
    970   /*    Set the coordinates for the instance, measured in the user         */
    971   /*    coordinate system.  Parse the `avar' table (if present) to convert */
    972   /*    from user to normalized coordinates.                               */
    973   /*                                                                       */
    974   /* <InOut>                                                               */
    975   /*    face       :: The font face.                                       */
    976   /*                  Initialize the blend struct with `gvar' data.        */
    977   /*                                                                       */
    978   /* <Input>                                                               */
    979   /*    num_coords :: This must be the axis count of the font.             */
    980   /*                                                                       */
    981   /*    coords     :: A coordinate array with `num_coords' elements.       */
    982   /*                                                                       */
    983   /* <Return>                                                              */
    984   /*    FreeType error code.  0 means success.                             */
    985   /*                                                                       */
    986   FT_LOCAL_DEF( FT_Error )
    987   TT_Set_Var_Design( TT_Face    face,
    988                      FT_UInt    num_coords,
    989                      FT_Fixed*  coords )
    990   {
    991     FT_Error        error      = TT_Err_Ok;
    992     FT_Fixed*       normalized = NULL;
    993     GX_Blend        blend;
    994     FT_MM_Var*      mmvar;
    995     FT_UInt         i, j;
    996     FT_Var_Axis*    a;
    997     GX_AVarSegment  av;
    998     FT_Memory       memory = face->root.memory;
    999 
   1000 
   1001     if ( face->blend == NULL )
   1002     {
   1003       if ( (error = TT_Get_MM_Var( face, NULL )) != 0 )
   1004         goto Exit;
   1005     }
   1006 
   1007     blend = face->blend;
   1008     mmvar = blend->mmvar;
   1009 
   1010     if ( num_coords != mmvar->num_axis )
   1011     {
   1012       error = TT_Err_Invalid_Argument;
   1013       goto Exit;
   1014     }
   1015 
   1016     /* Axis normalization is a two stage process.  First we normalize */
   1017     /* based on the [min,def,max] values for the axis to be [-1,0,1]. */
   1018     /* Then, if there's an `avar' table, we renormalize this range.   */
   1019 
   1020     if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
   1021       goto Exit;
   1022 
   1023     a = mmvar->axis;
   1024     for ( i = 0; i < mmvar->num_axis; ++i, ++a )
   1025     {
   1026       if ( coords[i] > a->maximum || coords[i] < a->minimum )
   1027       {
   1028         error = TT_Err_Invalid_Argument;
   1029         goto Exit;
   1030       }
   1031 
   1032       if ( coords[i] < a->def )
   1033       {
   1034         normalized[i] = -FT_MulDiv( coords[i] - a->def,
   1035                                     0x10000L,
   1036                                     a->minimum - a->def );
   1037       }
   1038       else if ( a->maximum == a->def )
   1039         normalized[i] = 0;
   1040       else
   1041       {
   1042         normalized[i] = FT_MulDiv( coords[i] - a->def,
   1043                                    0x10000L,
   1044                                    a->maximum - a->def );
   1045       }
   1046     }
   1047 
   1048     if ( !blend->avar_checked )
   1049       ft_var_load_avar( face );
   1050 
   1051     if ( blend->avar_segment != NULL )
   1052     {
   1053       av = blend->avar_segment;
   1054       for ( i = 0; i < mmvar->num_axis; ++i, ++av )
   1055       {
   1056         for ( j = 1; j < (FT_UInt)av->pairCount; ++j )
   1057           if ( normalized[i] < av->correspondence[j].fromCoord )
   1058           {
   1059             normalized[i] =
   1060               FT_MulDiv(
   1061                 FT_MulDiv(
   1062                   normalized[i] - av->correspondence[j - 1].fromCoord,
   1063                   0x10000L,
   1064                   av->correspondence[j].fromCoord -
   1065                     av->correspondence[j - 1].fromCoord ),
   1066                 av->correspondence[j].toCoord -
   1067                   av->correspondence[j - 1].toCoord,
   1068                 0x10000L ) +
   1069               av->correspondence[j - 1].toCoord;
   1070             break;
   1071           }
   1072       }
   1073     }
   1074 
   1075     error = TT_Set_MM_Blend( face, num_coords, normalized );
   1076 
   1077   Exit:
   1078     FT_FREE( normalized );
   1079     return error;
   1080   }
   1081 
   1082 
   1083   /*************************************************************************/
   1084   /*************************************************************************/
   1085   /*****                                                               *****/
   1086   /*****                     GX VAR PARSING ROUTINES                   *****/
   1087   /*****                                                               *****/
   1088   /*************************************************************************/
   1089   /*************************************************************************/
   1090 
   1091 
   1092   /*************************************************************************/
   1093   /*                                                                       */
   1094   /* <Function>                                                            */
   1095   /*    tt_face_vary_cvt                                                   */
   1096   /*                                                                       */
   1097   /* <Description>                                                         */
   1098   /*    Modify the loaded cvt table according to the `cvar' table and the  */
   1099   /*    font's blend.                                                      */
   1100   /*                                                                       */
   1101   /* <InOut>                                                               */
   1102   /*    face   :: A handle to the target face object.                      */
   1103   /*                                                                       */
   1104   /* <Input>                                                               */
   1105   /*    stream :: A handle to the input stream.                            */
   1106   /*                                                                       */
   1107   /* <Return>                                                              */
   1108   /*    FreeType error code.  0 means success.                             */
   1109   /*                                                                       */
   1110   /*    Most errors are ignored.  It is perfectly valid not to have a      */
   1111   /*    `cvar' table even if there is a `gvar' and `fvar' table.           */
   1112   /*                                                                       */
   1113   FT_LOCAL_DEF( FT_Error )
   1114   tt_face_vary_cvt( TT_Face    face,
   1115                     FT_Stream  stream )
   1116   {
   1117     FT_Error    error;
   1118     FT_Memory   memory = stream->memory;
   1119     FT_ULong    table_start;
   1120     FT_ULong    table_len;
   1121     FT_UInt     tupleCount;
   1122     FT_ULong    offsetToData;
   1123     FT_ULong    here;
   1124     FT_UInt     i, j;
   1125     FT_Fixed*   tuple_coords    = NULL;
   1126     FT_Fixed*   im_start_coords = NULL;
   1127     FT_Fixed*   im_end_coords   = NULL;
   1128     GX_Blend    blend           = face->blend;
   1129     FT_UInt     point_count;
   1130     FT_UShort*  localpoints;
   1131     FT_Short*   deltas;
   1132 
   1133 
   1134     FT_TRACE2(( "CVAR " ));
   1135 
   1136     if ( blend == NULL )
   1137     {
   1138       FT_TRACE2(( "tt_face_vary_cvt: no blend specified\n" ));
   1139 
   1140       error = TT_Err_Ok;
   1141       goto Exit;
   1142     }
   1143 
   1144     if ( face->cvt == NULL )
   1145     {
   1146       FT_TRACE2(( "tt_face_vary_cvt: no `cvt ' table\n" ));
   1147 
   1148       error = TT_Err_Ok;
   1149       goto Exit;
   1150     }
   1151 
   1152     error = face->goto_table( face, TTAG_cvar, stream, &table_len );
   1153     if ( error )
   1154     {
   1155       FT_TRACE2(( "is missing\n" ));
   1156 
   1157       error = TT_Err_Ok;
   1158       goto Exit;
   1159     }
   1160 
   1161     if ( FT_FRAME_ENTER( table_len ) )
   1162     {
   1163       error = TT_Err_Ok;
   1164       goto Exit;
   1165     }
   1166 
   1167     table_start = FT_Stream_FTell( stream );
   1168     if ( FT_GET_LONG() != 0x00010000L )
   1169     {
   1170       FT_TRACE2(( "bad table version\n" ));
   1171 
   1172       error = TT_Err_Ok;
   1173       goto FExit;
   1174     }
   1175 
   1176     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
   1177          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
   1178          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
   1179       goto FExit;
   1180 
   1181     tupleCount   = FT_GET_USHORT();
   1182     offsetToData = table_start + FT_GET_USHORT();
   1183 
   1184     /* The documentation implies there are flags packed into the        */
   1185     /* tuplecount, but John Jenkins says that shared points don't apply */
   1186     /* to `cvar', and no other flags are defined.                       */
   1187 
   1188     for ( i = 0; i < ( tupleCount & 0xFFF ); ++i )
   1189     {
   1190       FT_UInt   tupleDataSize;
   1191       FT_UInt   tupleIndex;
   1192       FT_Fixed  apply;
   1193 
   1194 
   1195       tupleDataSize = FT_GET_USHORT();
   1196       tupleIndex    = FT_GET_USHORT();
   1197 
   1198       /* There is no provision here for a global tuple coordinate section, */
   1199       /* so John says.  There are no tuple indices, just embedded tuples.  */
   1200 
   1201       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
   1202       {
   1203         for ( j = 0; j < blend->num_axis; ++j )
   1204           tuple_coords[j] = FT_GET_SHORT() << 2; /* convert from        */
   1205                                                  /* short frac to fixed */
   1206       }
   1207       else
   1208       {
   1209         /* skip this tuple; it makes no sense */
   1210 
   1211         if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
   1212           for ( j = 0; j < 2 * blend->num_axis; ++j )
   1213             (void)FT_GET_SHORT();
   1214 
   1215         offsetToData += tupleDataSize;
   1216         continue;
   1217       }
   1218 
   1219       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
   1220       {
   1221         for ( j = 0; j < blend->num_axis; ++j )
   1222           im_start_coords[j] = FT_GET_SHORT() << 2;
   1223         for ( j = 0; j < blend->num_axis; ++j )
   1224           im_end_coords[j] = FT_GET_SHORT() << 2;
   1225       }
   1226 
   1227       apply = ft_var_apply_tuple( blend,
   1228                                   (FT_UShort)tupleIndex,
   1229                                   tuple_coords,
   1230                                   im_start_coords,
   1231                                   im_end_coords );
   1232       if ( /* tuple isn't active for our blend */
   1233            apply == 0                                    ||
   1234            /* global points not allowed,           */
   1235            /* if they aren't local, makes no sense */
   1236            !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) )
   1237       {
   1238         offsetToData += tupleDataSize;
   1239         continue;
   1240       }
   1241 
   1242       here = FT_Stream_FTell( stream );
   1243 
   1244       FT_Stream_SeekSet( stream, offsetToData );
   1245 
   1246       localpoints = ft_var_readpackedpoints( stream, &point_count );
   1247       deltas      = ft_var_readpackeddeltas( stream,
   1248                                              point_count == 0 ? face->cvt_size
   1249                                                               : point_count );
   1250       if ( localpoints == NULL || deltas == NULL )
   1251         /* failure, ignore it */;
   1252 
   1253       else if ( localpoints == ALL_POINTS )
   1254       {
   1255         /* this means that there are deltas for every entry in cvt */
   1256         for ( j = 0; j < face->cvt_size; ++j )
   1257           face->cvt[j] = (FT_Short)( face->cvt[j] +
   1258                                      FT_MulFix( deltas[j], apply ) );
   1259       }
   1260 
   1261       else
   1262       {
   1263         for ( j = 0; j < point_count; ++j )
   1264         {
   1265           int  pindex = localpoints[j];
   1266 
   1267           face->cvt[pindex] = (FT_Short)( face->cvt[pindex] +
   1268                                           FT_MulFix( deltas[j], apply ) );
   1269         }
   1270       }
   1271 
   1272       if ( localpoints != ALL_POINTS )
   1273         FT_FREE( localpoints );
   1274       FT_FREE( deltas );
   1275 
   1276       offsetToData += tupleDataSize;
   1277 
   1278       FT_Stream_SeekSet( stream, here );
   1279     }
   1280 
   1281   FExit:
   1282     FT_FRAME_EXIT();
   1283 
   1284   Exit:
   1285     FT_FREE( tuple_coords );
   1286     FT_FREE( im_start_coords );
   1287     FT_FREE( im_end_coords );
   1288 
   1289     return error;
   1290   }
   1291 
   1292 
   1293   /*************************************************************************/
   1294   /*                                                                       */
   1295   /* <Function>                                                            */
   1296   /*    TT_Vary_Get_Glyph_Deltas                                           */
   1297   /*                                                                       */
   1298   /* <Description>                                                         */
   1299   /*    Load the appropriate deltas for the current glyph.                 */
   1300   /*                                                                       */
   1301   /* <Input>                                                               */
   1302   /*    face        :: A handle to the target face object.                 */
   1303   /*                                                                       */
   1304   /*    glyph_index :: The index of the glyph being modified.              */
   1305   /*                                                                       */
   1306   /*    n_points    :: The number of the points in the glyph, including    */
   1307   /*                   phantom points.                                     */
   1308   /*                                                                       */
   1309   /* <Output>                                                              */
   1310   /*    deltas      :: The array of points to change.                      */
   1311   /*                                                                       */
   1312   /* <Return>                                                              */
   1313   /*    FreeType error code.  0 means success.                             */
   1314   /*                                                                       */
   1315   FT_LOCAL_DEF( FT_Error )
   1316   TT_Vary_Get_Glyph_Deltas( TT_Face      face,
   1317                             FT_UInt      glyph_index,
   1318                             FT_Vector*  *deltas,
   1319                             FT_UInt      n_points )
   1320   {
   1321     FT_Stream   stream = face->root.stream;
   1322     FT_Memory   memory = stream->memory;
   1323     GX_Blend    blend  = face->blend;
   1324     FT_Vector*  delta_xy;
   1325 
   1326     FT_Error    error;
   1327     FT_ULong    glyph_start;
   1328     FT_UInt     tupleCount;
   1329     FT_ULong    offsetToData;
   1330     FT_ULong    here;
   1331     FT_UInt     i, j;
   1332     FT_Fixed*   tuple_coords    = NULL;
   1333     FT_Fixed*   im_start_coords = NULL;
   1334     FT_Fixed*   im_end_coords   = NULL;
   1335     FT_UInt     point_count, spoint_count = 0;
   1336     FT_UShort*  sharedpoints = NULL;
   1337     FT_UShort*  localpoints  = NULL;
   1338     FT_UShort*  points;
   1339     FT_Short    *deltas_x, *deltas_y;
   1340 
   1341 
   1342     if ( !face->doblend || blend == NULL )
   1343       return TT_Err_Invalid_Argument;
   1344 
   1345     /* to be freed by the caller */
   1346     if ( FT_NEW_ARRAY( delta_xy, n_points ) )
   1347       goto Exit;
   1348     *deltas = delta_xy;
   1349 
   1350     if ( glyph_index >= blend->gv_glyphcnt      ||
   1351          blend->glyphoffsets[glyph_index] ==
   1352            blend->glyphoffsets[glyph_index + 1] )
   1353       return TT_Err_Ok;               /* no variation data for this glyph */
   1354 
   1355     if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] )   ||
   1356          FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] -
   1357                            blend->glyphoffsets[glyph_index] ) )
   1358       goto Fail1;
   1359 
   1360     glyph_start = FT_Stream_FTell( stream );
   1361 
   1362     /* each set of glyph variation data is formatted similarly to `cvar' */
   1363     /* (except we get shared points and global tuples)                   */
   1364 
   1365     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
   1366          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
   1367          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
   1368       goto Fail2;
   1369 
   1370     tupleCount   = FT_GET_USHORT();
   1371     offsetToData = glyph_start + FT_GET_USHORT();
   1372 
   1373     if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
   1374     {
   1375       here = FT_Stream_FTell( stream );
   1376 
   1377       FT_Stream_SeekSet( stream, offsetToData );
   1378 
   1379       sharedpoints = ft_var_readpackedpoints( stream, &spoint_count );
   1380       offsetToData = FT_Stream_FTell( stream );
   1381 
   1382       FT_Stream_SeekSet( stream, here );
   1383     }
   1384 
   1385     for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); ++i )
   1386     {
   1387       FT_UInt   tupleDataSize;
   1388       FT_UInt   tupleIndex;
   1389       FT_Fixed  apply;
   1390 
   1391 
   1392       tupleDataSize = FT_GET_USHORT();
   1393       tupleIndex    = FT_GET_USHORT();
   1394 
   1395       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
   1396       {
   1397         for ( j = 0; j < blend->num_axis; ++j )
   1398           tuple_coords[j] = FT_GET_SHORT() << 2;  /* convert from        */
   1399                                                   /* short frac to fixed */
   1400       }
   1401       else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
   1402       {
   1403         error = TT_Err_Invalid_Table;
   1404         goto Fail3;
   1405       }
   1406       else
   1407       {
   1408         FT_MEM_COPY(
   1409           tuple_coords,
   1410           &blend->tuplecoords[(tupleIndex & 0xFFF) * blend->num_axis],
   1411           blend->num_axis * sizeof ( FT_Fixed ) );
   1412       }
   1413 
   1414       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
   1415       {
   1416         for ( j = 0; j < blend->num_axis; ++j )
   1417           im_start_coords[j] = FT_GET_SHORT() << 2;
   1418         for ( j = 0; j < blend->num_axis; ++j )
   1419           im_end_coords[j] = FT_GET_SHORT() << 2;
   1420       }
   1421 
   1422       apply = ft_var_apply_tuple( blend,
   1423                                   (FT_UShort)tupleIndex,
   1424                                   tuple_coords,
   1425                                   im_start_coords,
   1426                                   im_end_coords );
   1427 
   1428       if ( apply == 0 )              /* tuple isn't active for our blend */
   1429       {
   1430         offsetToData += tupleDataSize;
   1431         continue;
   1432       }
   1433 
   1434       here = FT_Stream_FTell( stream );
   1435 
   1436       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
   1437       {
   1438         FT_Stream_SeekSet( stream, offsetToData );
   1439 
   1440         localpoints = ft_var_readpackedpoints( stream, &point_count );
   1441         points      = localpoints;
   1442       }
   1443       else
   1444       {
   1445         points      = sharedpoints;
   1446         point_count = spoint_count;
   1447       }
   1448 
   1449       deltas_x = ft_var_readpackeddeltas( stream,
   1450                                           point_count == 0 ? n_points
   1451                                                            : point_count );
   1452       deltas_y = ft_var_readpackeddeltas( stream,
   1453                                           point_count == 0 ? n_points
   1454                                                            : point_count );
   1455 
   1456       if ( points == NULL || deltas_y == NULL || deltas_x == NULL )
   1457         ; /* failure, ignore it */
   1458 
   1459       else if ( points == ALL_POINTS )
   1460       {
   1461         /* this means that there are deltas for every point in the glyph */
   1462         for ( j = 0; j < n_points; ++j )
   1463         {
   1464           delta_xy[j].x += FT_MulFix( deltas_x[j], apply );
   1465           delta_xy[j].y += FT_MulFix( deltas_y[j], apply );
   1466         }
   1467       }
   1468 
   1469       else
   1470       {
   1471         for ( j = 0; j < point_count; ++j )
   1472         {
   1473           delta_xy[localpoints[j]].x += FT_MulFix( deltas_x[j], apply );
   1474           delta_xy[localpoints[j]].y += FT_MulFix( deltas_y[j], apply );
   1475         }
   1476       }
   1477 
   1478       if ( localpoints != ALL_POINTS )
   1479         FT_FREE( localpoints );
   1480       FT_FREE( deltas_x );
   1481       FT_FREE( deltas_y );
   1482 
   1483       offsetToData += tupleDataSize;
   1484 
   1485       FT_Stream_SeekSet( stream, here );
   1486     }
   1487 
   1488   Fail3:
   1489     FT_FREE( tuple_coords );
   1490     FT_FREE( im_start_coords );
   1491     FT_FREE( im_end_coords );
   1492 
   1493   Fail2:
   1494     FT_FRAME_EXIT();
   1495 
   1496   Fail1:
   1497     if ( error )
   1498     {
   1499       FT_FREE( delta_xy );
   1500       *deltas = NULL;
   1501     }
   1502 
   1503   Exit:
   1504     return error;
   1505   }
   1506 
   1507 
   1508   /*************************************************************************/
   1509   /*                                                                       */
   1510   /* <Function>                                                            */
   1511   /*    tt_done_blend                                                      */
   1512   /*                                                                       */
   1513   /* <Description>                                                         */
   1514   /*    Frees the blend internal data structure.                           */
   1515   /*                                                                       */
   1516   FT_LOCAL_DEF( void )
   1517   tt_done_blend( FT_Memory  memory,
   1518                  GX_Blend   blend )
   1519   {
   1520     if ( blend != NULL )
   1521     {
   1522       FT_UInt  i;
   1523 
   1524 
   1525       FT_FREE( blend->normalizedcoords );
   1526       FT_FREE( blend->mmvar );
   1527 
   1528       if ( blend->avar_segment != NULL )
   1529       {
   1530         for ( i = 0; i < blend->num_axis; ++i )
   1531           FT_FREE( blend->avar_segment[i].correspondence );
   1532         FT_FREE( blend->avar_segment );
   1533       }
   1534 
   1535       FT_FREE( blend->tuplecoords );
   1536       FT_FREE( blend->glyphoffsets );
   1537       FT_FREE( blend );
   1538     }
   1539   }
   1540 
   1541 #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
   1542 
   1543 
   1544 /* END */
   1545