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