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