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