• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /***************************************************************************/
2 /*                                                                         */
3 /*  ttgxvar.c                                                              */
4 /*                                                                         */
5 /*    TrueType GX Font Variation loader                                    */
6 /*                                                                         */
7 /*  Copyright 2004, 2005, 2006, 2007, 2008 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 be 2. */
27 /*                                                                         */
28 /* The documentation for `gvar' is not intelligible; `cvar' refers you to  */
29 /* `gvar' and is thus also incomprehensible.                               */
30 /*                                                                         */
31 /* The documentation for `avar' appears correct, but Apple has no fonts    */
32 /* with an `avar' table, so it is hard to test.                            */
33 /*                                                                         */
34 /* Many thanks to John Jenkins (at Apple) in figuring this out.            */
35 /*                                                                         */
36 /*                                                                         */
37 /* Apple's `kern' table has some references to tuple indices, but as there */
38 /* is no indication where these indices are defined, nor how to            */
39 /* interpolate the kerning values (different tuples have different         */
40 /* classes) this issue is ignored.                                         */
41 /*                                                                         */
42 /***************************************************************************/
43 
44 
45 #include <ft2build.h>
46 #include FT_INTERNAL_DEBUG_H
47 #include FT_CONFIG_CONFIG_H
48 #include FT_INTERNAL_STREAM_H
49 #include FT_INTERNAL_SFNT_H
50 #include FT_TRUETYPE_IDS_H
51 #include FT_TRUETYPE_TAGS_H
52 #include FT_MULTIPLE_MASTERS_H
53 
54 #include "ttdriver.h"
55 #include "ttpload.h"
56 #include "ttgxvar.h"
57 
58 #include "tterrors.h"
59 
60 
61 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
62 
63 
64 #define FT_Stream_FTell( stream )  \
65           ( (stream)->cursor - (stream)->base )
66 #define FT_Stream_SeekSet( stream, off ) \
67               ( (stream)->cursor = (stream)->base+(off) )
68 
69 
70   /*************************************************************************/
71   /*                                                                       */
72   /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
73   /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
74   /* messages during execution.                                            */
75   /*                                                                       */
76 #undef  FT_COMPONENT
77 #define FT_COMPONENT  trace_ttgxvar
78 
79 
80   /*************************************************************************/
81   /*************************************************************************/
82   /*****                                                               *****/
83   /*****                       Internal Routines                       *****/
84   /*****                                                               *****/
85   /*************************************************************************/
86   /*************************************************************************/
87 
88 
89   /*************************************************************************/
90   /*                                                                       */
91   /* The macro ALL_POINTS is used in `ft_var_readpackedpoints'.  It        */
92   /* indicates that there is a delta for every point without needing to    */
93   /* enumerate all of them.                                                */
94   /*                                                                       */
95 #define ALL_POINTS  (FT_UShort*)( -1 )
96 
97 
98   enum
99   {
100     GX_PT_POINTS_ARE_WORDS     = 0x80,
101     GX_PT_POINT_RUN_COUNT_MASK = 0x7F
102   };
103 
104 
105   /*************************************************************************/
106   /*                                                                       */
107   /* <Function>                                                            */
108   /*    ft_var_readpackedpoints                                            */
109   /*                                                                       */
110   /* <Description>                                                         */
111   /*    Read a set of points to which the following deltas will apply.     */
112   /*    Points are packed with a run length encoding.                      */
113   /*                                                                       */
114   /* <Input>                                                               */
115   /*    stream    :: The data stream.                                      */
116   /*                                                                       */
117   /* <Output>                                                              */
118   /*    point_cnt :: The number of points read.  A zero value means that   */
119   /*                 all points in the glyph will be affected, without     */
120   /*                 enumerating them individually.                        */
121   /*                                                                       */
122   /* <Return>                                                              */
123   /*    An array of FT_UShort containing the affected points or the        */
124   /*    special value ALL_POINTS.                                          */
125   /*                                                                       */
126   static FT_UShort*
ft_var_readpackedpoints(FT_Stream stream,FT_UInt * point_cnt)127   ft_var_readpackedpoints( FT_Stream  stream,
128                            FT_UInt   *point_cnt )
129   {
130     FT_UShort *points;
131     FT_Int     n;
132     FT_Int     runcnt;
133     FT_Int     i;
134     FT_Int     j;
135     FT_Int     first;
136     FT_Memory  memory = stream->memory;
137     FT_Error   error = TT_Err_Ok;
138 
139     FT_UNUSED( error );
140 
141 
142     *point_cnt = n = FT_GET_BYTE();
143     if ( n == 0 )
144       return ALL_POINTS;
145 
146     if ( n & GX_PT_POINTS_ARE_WORDS )
147       n = FT_GET_BYTE() | ( ( n & GX_PT_POINT_RUN_COUNT_MASK ) << 8 );
148 
149     if ( FT_NEW_ARRAY( points, n ) )
150       return NULL;
151 
152     i = 0;
153     while ( i < n )
154     {
155       runcnt = FT_GET_BYTE();
156       if ( runcnt & GX_PT_POINTS_ARE_WORDS )
157       {
158         runcnt = runcnt & GX_PT_POINT_RUN_COUNT_MASK;
159         first  = points[i++] = FT_GET_USHORT();
160 
161         /* first point not included in runcount */
162         for ( j = 0; j < runcnt; ++j )
163           points[i++] = (FT_UShort)( first += FT_GET_USHORT() );
164       }
165       else
166       {
167         first = points[i++] = FT_GET_BYTE();
168 
169         for ( j = 0; j < runcnt; ++j )
170           points[i++] = (FT_UShort)( first += FT_GET_BYTE() );
171       }
172     }
173 
174     return points;
175   }
176 
177 
178   enum
179   {
180     GX_DT_DELTAS_ARE_ZERO      = 0x80,
181     GX_DT_DELTAS_ARE_WORDS     = 0x40,
182     GX_DT_DELTA_RUN_COUNT_MASK = 0x3F
183   };
184 
185 
186   /*************************************************************************/
187   /*                                                                       */
188   /* <Function>                                                            */
189   /*    ft_var_readpackeddeltas                                            */
190   /*                                                                       */
191   /* <Description>                                                         */
192   /*    Read a set of deltas.  These are packed slightly differently than  */
193   /*    points.  In particular there is no overall count.                  */
194   /*                                                                       */
195   /* <Input>                                                               */
196   /*    stream    :: The data stream.                                      */
197   /*                                                                       */
198   /*    delta_cnt :: The number of to be read.                             */
199   /*                                                                       */
200   /* <Return>                                                              */
201   /*    An array of FT_Short containing the deltas for the affected        */
202   /*    points.  (This only gets the deltas for one dimension.  It will    */
203   /*    generally be called twice, once for x, once for y.  When used in   */
204   /*    cvt table, it will only be called once.)                           */
205   /*                                                                       */
206   static FT_Short*
ft_var_readpackeddeltas(FT_Stream stream,FT_Int delta_cnt)207   ft_var_readpackeddeltas( FT_Stream  stream,
208                            FT_Int     delta_cnt )
209   {
210     FT_Short  *deltas;
211     FT_Int     runcnt;
212     FT_Int     i;
213     FT_Int     j;
214     FT_Memory  memory = stream->memory;
215     FT_Error   error = TT_Err_Ok;
216 
217     FT_UNUSED( error );
218 
219 
220     if ( FT_NEW_ARRAY( deltas, delta_cnt ) )
221       return NULL;
222 
223     i = 0;
224     while ( i < delta_cnt )
225     {
226       runcnt = FT_GET_BYTE();
227       if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
228       {
229         /* runcnt zeroes get added */
230         for ( j = 0;
231               j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
232               ++j )
233           deltas[i++] = 0;
234       }
235       else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
236       {
237         /* runcnt shorts from the stack */
238         for ( j = 0;
239               j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
240               ++j )
241           deltas[i++] = FT_GET_SHORT();
242       }
243       else
244       {
245         /* runcnt signed bytes from the stack */
246         for ( j = 0;
247               j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
248               ++j )
249           deltas[i++] = FT_GET_CHAR();
250       }
251 
252       if ( j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) )
253       {
254         /* Bad format */
255         FT_FREE( deltas );
256         return NULL;
257       }
258     }
259 
260     return deltas;
261   }
262 
263 
264   /*************************************************************************/
265   /*                                                                       */
266   /* <Function>                                                            */
267   /*    ft_var_load_avar                                                   */
268   /*                                                                       */
269   /* <Description>                                                         */
270   /*    Parse the `avar' table if present.  It need not be, so we return   */
271   /*    nothing.                                                           */
272   /*                                                                       */
273   /* <InOut>                                                               */
274   /*    face :: The font face.                                             */
275   /*                                                                       */
276   static void
ft_var_load_avar(TT_Face face)277   ft_var_load_avar( TT_Face  face )
278   {
279     FT_Stream       stream = FT_FACE_STREAM(face);
280     FT_Memory       memory = stream->memory;
281     GX_Blend        blend  = face->blend;
282     GX_AVarSegment  segment;
283     FT_Error        error = TT_Err_Ok;
284     FT_ULong        version;
285     FT_Long         axisCount;
286     FT_Int          i, j;
287     FT_ULong        table_len;
288 
289     FT_UNUSED( error );
290 
291 
292     blend->avar_checked = TRUE;
293     if ( (error = face->goto_table( face, TTAG_avar, stream, &table_len )) != 0 )
294       return;
295 
296     if ( FT_FRAME_ENTER( table_len ) )
297       return;
298 
299     version   = FT_GET_LONG();
300     axisCount = FT_GET_LONG();
301 
302     if ( version != 0x00010000L                       ||
303          axisCount != (FT_Long)blend->mmvar->num_axis )
304       goto Exit;
305 
306     if ( FT_NEW_ARRAY( blend->avar_segment, axisCount ) )
307       goto Exit;
308 
309     segment = &blend->avar_segment[0];
310     for ( i = 0; i < axisCount; ++i, ++segment )
311     {
312       segment->pairCount = FT_GET_USHORT();
313       if ( FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) )
314       {
315         /* Failure.  Free everything we have done so far.  We must do */
316         /* it right now since loading the `avar' table is optional.   */
317 
318         for ( j = i - 1; j >= 0; --j )
319           FT_FREE( blend->avar_segment[j].correspondence );
320 
321         FT_FREE( blend->avar_segment );
322         blend->avar_segment = NULL;
323         goto Exit;
324       }
325 
326       for ( j = 0; j < segment->pairCount; ++j )
327       {
328         segment->correspondence[j].fromCoord =
329           FT_GET_SHORT() << 2;    /* convert to Fixed */
330         segment->correspondence[j].toCoord =
331           FT_GET_SHORT()<<2;    /* convert to Fixed */
332       }
333     }
334 
335   Exit:
336     FT_FRAME_EXIT();
337   }
338 
339 
340   typedef struct  GX_GVar_Head_
341   {
342     FT_Long    version;
343     FT_UShort  axisCount;
344     FT_UShort  globalCoordCount;
345     FT_ULong   offsetToCoord;
346     FT_UShort  glyphCount;
347     FT_UShort  flags;
348     FT_ULong   offsetToData;
349 
350   } GX_GVar_Head;
351 
352 
353   /*************************************************************************/
354   /*                                                                       */
355   /* <Function>                                                            */
356   /*    ft_var_load_gvar                                                   */
357   /*                                                                       */
358   /* <Description>                                                         */
359   /*    Parses the `gvar' table if present.  If `fvar' is there, `gvar'    */
360   /*    had better be there too.                                           */
361   /*                                                                       */
362   /* <InOut>                                                               */
363   /*    face :: The font face.                                             */
364   /*                                                                       */
365   /* <Return>                                                              */
366   /*    FreeType error code.  0 means success.                             */
367   /*                                                                       */
368   static FT_Error
ft_var_load_gvar(TT_Face face)369   ft_var_load_gvar( TT_Face  face )
370   {
371     FT_Stream     stream = FT_FACE_STREAM(face);
372     FT_Memory     memory = stream->memory;
373     GX_Blend      blend  = face->blend;
374     FT_Error      error;
375     FT_UInt       i, j;
376     FT_ULong      table_len;
377     FT_ULong      gvar_start;
378     FT_ULong      offsetToData;
379     GX_GVar_Head  gvar_head;
380 
381     static const FT_Frame_Field  gvar_fields[] =
382     {
383 
384 #undef  FT_STRUCTURE
385 #define FT_STRUCTURE  GX_GVar_Head
386 
387       FT_FRAME_START( 20 ),
388         FT_FRAME_LONG  ( version ),
389         FT_FRAME_USHORT( axisCount ),
390         FT_FRAME_USHORT( globalCoordCount ),
391         FT_FRAME_ULONG ( offsetToCoord ),
392         FT_FRAME_USHORT( glyphCount ),
393         FT_FRAME_USHORT( flags ),
394         FT_FRAME_ULONG ( offsetToData ),
395       FT_FRAME_END
396     };
397 
398     if ( (error = face->goto_table( face, TTAG_gvar, stream, &table_len )) != 0 )
399       goto Exit;
400 
401     gvar_start = FT_STREAM_POS( );
402     if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) )
403       goto Exit;
404 
405     blend->tuplecount  = gvar_head.globalCoordCount;
406     blend->gv_glyphcnt = gvar_head.glyphCount;
407     offsetToData       = gvar_start + gvar_head.offsetToData;
408 
409     if ( gvar_head.version   != (FT_Long)0x00010000L              ||
410          gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
411     {
412       error = TT_Err_Invalid_Table;
413       goto Exit;
414     }
415 
416     if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) )
417       goto Exit;
418 
419     if ( gvar_head.flags & 1 )
420     {
421       /* long offsets (one more offset than glyphs, to mark size of last) */
422       if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) )
423         goto Exit;
424 
425       for ( i = 0; i <= blend->gv_glyphcnt; ++i )
426         blend->glyphoffsets[i] = offsetToData + FT_GET_LONG();
427 
428       FT_FRAME_EXIT();
429     }
430     else
431     {
432       /* short offsets (one more offset than glyphs, to mark size of last) */
433       if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) )
434         goto Exit;
435 
436       for ( i = 0; i <= blend->gv_glyphcnt; ++i )
437         blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
438                                               /* XXX: Undocumented: `*2'! */
439 
440       FT_FRAME_EXIT();
441     }
442 
443     if ( blend->tuplecount != 0 )
444     {
445       if ( FT_NEW_ARRAY( blend->tuplecoords,
446                          gvar_head.axisCount * blend->tuplecount ) )
447         goto Exit;
448 
449       if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord )       ||
450            FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L )                   )
451         goto Exit;
452 
453       for ( i = 0; i < blend->tuplecount; ++i )
454         for ( j = 0 ; j < (FT_UInt)gvar_head.axisCount; ++j )
455           blend->tuplecoords[i * gvar_head.axisCount + j] =
456             FT_GET_SHORT() << 2;                /* convert to FT_Fixed */
457 
458       FT_FRAME_EXIT();
459     }
460 
461   Exit:
462     return error;
463   }
464 
465 
466   /*************************************************************************/
467   /*                                                                       */
468   /* <Function>                                                            */
469   /*    ft_var_apply_tuple                                                 */
470   /*                                                                       */
471   /* <Description>                                                         */
472   /*    Figure out whether a given tuple (design) applies to the current   */
473   /*    blend, and if so, what is the scaling factor.                      */
474   /*                                                                       */
475   /* <Input>                                                               */
476   /*    blend           :: The current blend of the font.                  */
477   /*                                                                       */
478   /*    tupleIndex      :: A flag saying whether this is an intermediate   */
479   /*                       tuple or not.                                   */
480   /*                                                                       */
481   /*    tuple_coords    :: The coordinates of the tuple in normalized axis */
482   /*                       units.                                          */
483   /*                                                                       */
484   /*    im_start_coords :: The initial coordinates where this tuple starts */
485   /*                       to apply (for intermediate coordinates).        */
486   /*                                                                       */
487   /*    im_end_coords   :: The final coordinates after which this tuple no */
488   /*                       longer applies (for intermediate coordinates).  */
489   /*                                                                       */
490   /* <Return>                                                              */
491   /*    An FT_Fixed value containing the scaling factor.                   */
492   /*                                                                       */
493   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)494   ft_var_apply_tuple( GX_Blend   blend,
495                       FT_UShort  tupleIndex,
496                       FT_Fixed*  tuple_coords,
497                       FT_Fixed*  im_start_coords,
498                       FT_Fixed*  im_end_coords )
499   {
500     FT_UInt   i;
501     FT_Fixed  apply;
502     FT_Fixed  temp;
503 
504 
505     apply = 0x10000L;
506     for ( i = 0; i < blend->num_axis; ++i )
507     {
508       if ( tuple_coords[i] == 0 )
509         /* It's not clear why (for intermediate tuples) we don't need     */
510         /* to check against start/end -- the documentation says we don't. */
511         /* Similarly, it's unclear why we don't need to scale along the   */
512         /* axis.                                                          */
513         continue;
514 
515       else if ( blend->normalizedcoords[i] == 0                           ||
516                 ( blend->normalizedcoords[i] < 0 && tuple_coords[i] > 0 ) ||
517                 ( blend->normalizedcoords[i] > 0 && tuple_coords[i] < 0 ) )
518       {
519         apply = 0;
520         break;
521       }
522 
523       else if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
524         /* not an intermediate tuple */
525         apply = FT_MulDiv( apply,
526                            blend->normalizedcoords[i] > 0
527                              ? blend->normalizedcoords[i]
528                              : -blend->normalizedcoords[i],
529                            0x10000L );
530 
531       else if ( blend->normalizedcoords[i] <= im_start_coords[i] ||
532                 blend->normalizedcoords[i] >= im_end_coords[i]   )
533       {
534         apply = 0;
535         break;
536       }
537 
538       else if ( blend->normalizedcoords[i] < tuple_coords[i] )
539       {
540         temp = FT_MulDiv( blend->normalizedcoords[i] - im_start_coords[i],
541                           0x10000L,
542                           tuple_coords[i] - im_start_coords[i]);
543         apply = FT_MulDiv( apply, temp, 0x10000L );
544       }
545 
546       else
547       {
548         temp = FT_MulDiv( im_end_coords[i] - blend->normalizedcoords[i],
549                           0x10000L,
550                           im_end_coords[i] - tuple_coords[i] );
551         apply = FT_MulDiv( apply, temp, 0x10000L );
552       }
553     }
554 
555     return apply;
556   }
557 
558 
559   /*************************************************************************/
560   /*************************************************************************/
561   /*****                                                               *****/
562   /*****               MULTIPLE MASTERS SERVICE FUNCTIONS              *****/
563   /*****                                                               *****/
564   /*************************************************************************/
565   /*************************************************************************/
566 
567 
568   typedef struct  GX_FVar_Head_
569   {
570     FT_Long    version;
571     FT_UShort  offsetToData;
572     FT_UShort  countSizePairs;
573     FT_UShort  axisCount;
574     FT_UShort  axisSize;
575     FT_UShort  instanceCount;
576     FT_UShort  instanceSize;
577 
578   } GX_FVar_Head;
579 
580 
581   typedef struct  fvar_axis_
582   {
583     FT_ULong   axisTag;
584     FT_ULong   minValue;
585     FT_ULong   defaultValue;
586     FT_ULong   maxValue;
587     FT_UShort  flags;
588     FT_UShort  nameID;
589 
590   } GX_FVar_Axis;
591 
592 
593   /*************************************************************************/
594   /*                                                                       */
595   /* <Function>                                                            */
596   /*    TT_Get_MM_Var                                                      */
597   /*                                                                       */
598   /* <Description>                                                         */
599   /*    Check that the font's `fvar' table is valid, parse it, and return  */
600   /*    those data.                                                        */
601   /*                                                                       */
602   /* <InOut>                                                               */
603   /*    face   :: The font face.                                           */
604   /*              TT_Get_MM_Var initializes the blend structure.           */
605   /*                                                                       */
606   /* <Output>                                                              */
607   /*    master :: The `fvar' data (must be freed by caller).               */
608   /*                                                                       */
609   /* <Return>                                                              */
610   /*    FreeType error code.  0 means success.                             */
611   /*                                                                       */
612   FT_LOCAL_DEF( FT_Error )
TT_Get_MM_Var(TT_Face face,FT_MM_Var ** master)613   TT_Get_MM_Var( TT_Face      face,
614                  FT_MM_Var*  *master )
615   {
616     FT_Stream            stream = face->root.stream;
617     FT_Memory            memory = face->root.memory;
618     FT_ULong             table_len;
619     FT_Error             error  = TT_Err_Ok;
620     FT_ULong             fvar_start;
621     FT_Int               i, j;
622     FT_MM_Var*           mmvar;
623     FT_Fixed*            next_coords;
624     FT_String*           next_name;
625     FT_Var_Axis*         a;
626     FT_Var_Named_Style*  ns;
627     GX_FVar_Head         fvar_head;
628 
629     static const FT_Frame_Field  fvar_fields[] =
630     {
631 
632 #undef  FT_STRUCTURE
633 #define FT_STRUCTURE  GX_FVar_Head
634 
635       FT_FRAME_START( 16 ),
636         FT_FRAME_LONG  ( version ),
637         FT_FRAME_USHORT( offsetToData ),
638         FT_FRAME_USHORT( countSizePairs ),
639         FT_FRAME_USHORT( axisCount ),
640         FT_FRAME_USHORT( axisSize ),
641         FT_FRAME_USHORT( instanceCount ),
642         FT_FRAME_USHORT( instanceSize ),
643       FT_FRAME_END
644     };
645 
646     static const FT_Frame_Field  fvaraxis_fields[] =
647     {
648 
649 #undef  FT_STRUCTURE
650 #define FT_STRUCTURE  GX_FVar_Axis
651 
652       FT_FRAME_START( 20 ),
653         FT_FRAME_ULONG ( axisTag ),
654         FT_FRAME_ULONG ( minValue ),
655         FT_FRAME_ULONG ( defaultValue ),
656         FT_FRAME_ULONG ( maxValue ),
657         FT_FRAME_USHORT( flags ),
658         FT_FRAME_USHORT( nameID ),
659       FT_FRAME_END
660     };
661 
662 
663     if ( face->blend == NULL )
664     {
665       /* both `fvar' and `gvar' must be present */
666       if ( (error = face->goto_table( face, TTAG_gvar,
667                                       stream, &table_len )) != 0 )
668         goto Exit;
669 
670       if ( (error = face->goto_table( face, TTAG_fvar,
671                                       stream, &table_len )) != 0 )
672         goto Exit;
673 
674       fvar_start = FT_STREAM_POS( );
675 
676       if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
677         goto Exit;
678 
679       if ( fvar_head.version != (FT_Long)0x00010000L                      ||
680            fvar_head.countSizePairs != 2                                  ||
681            fvar_head.axisSize != 20                                       ||
682            fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount          ||
683            fvar_head.offsetToData + fvar_head.axisCount * 20U +
684              fvar_head.instanceCount * fvar_head.instanceSize > table_len )
685       {
686         error = TT_Err_Invalid_Table;
687         goto Exit;
688       }
689 
690       if ( FT_NEW( face->blend ) )
691         goto Exit;
692 
693       /* XXX: TODO - check for overflows */
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         (FT_UInt)-1;           /* 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 )
TT_Set_MM_Blend(TT_Face face,FT_UInt num_coords,FT_Fixed * coords)848   TT_Set_MM_Blend( TT_Face    face,
849                    FT_UInt    num_coords,
850                    FT_Fixed*  coords )
851   {
852     FT_Error    error = TT_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 = TT_Err_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 = TT_Err_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 )
TT_Set_Var_Design(TT_Face face,FT_UInt num_coords,FT_Fixed * coords)984   TT_Set_Var_Design( TT_Face    face,
985                      FT_UInt    num_coords,
986                      FT_Fixed*  coords )
987   {
988     FT_Error        error      = TT_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 = TT_Err_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 = TT_Err_Invalid_Argument;
1026         goto Exit;
1027       }
1028 
1029       if ( coords[i] < a->def )
1030       {
1031         normalized[i] = -FT_MulDiv( coords[i] - a->def,
1032                                     0x10000L,
1033                                     a->minimum - a->def );
1034       }
1035       else if ( a->maximum == a->def )
1036         normalized[i] = 0;
1037       else
1038       {
1039         normalized[i] = FT_MulDiv( coords[i] - a->def,
1040                                    0x10000L,
1041                                    a->maximum - a->def );
1042       }
1043     }
1044 
1045     if ( !blend->avar_checked )
1046       ft_var_load_avar( face );
1047 
1048     if ( blend->avar_segment != NULL )
1049     {
1050       av = blend->avar_segment;
1051       for ( i = 0; i < mmvar->num_axis; ++i, ++av )
1052       {
1053         for ( j = 1; j < (FT_UInt)av->pairCount; ++j )
1054           if ( normalized[i] < av->correspondence[j].fromCoord )
1055           {
1056             normalized[i] =
1057               FT_MulDiv(
1058                 FT_MulDiv(
1059                   normalized[i] - av->correspondence[j - 1].fromCoord,
1060                   0x10000L,
1061                   av->correspondence[j].fromCoord -
1062                     av->correspondence[j - 1].fromCoord ),
1063                 av->correspondence[j].toCoord -
1064                   av->correspondence[j - 1].toCoord,
1065                 0x10000L ) +
1066               av->correspondence[j - 1].toCoord;
1067             break;
1068           }
1069       }
1070     }
1071 
1072     error = TT_Set_MM_Blend( face, num_coords, normalized );
1073 
1074   Exit:
1075     FT_FREE( normalized );
1076     return error;
1077   }
1078 
1079 
1080   /*************************************************************************/
1081   /*************************************************************************/
1082   /*****                                                               *****/
1083   /*****                     GX VAR PARSING ROUTINES                   *****/
1084   /*****                                                               *****/
1085   /*************************************************************************/
1086   /*************************************************************************/
1087 
1088 
1089   /*************************************************************************/
1090   /*                                                                       */
1091   /* <Function>                                                            */
1092   /*    tt_face_vary_cvt                                                   */
1093   /*                                                                       */
1094   /* <Description>                                                         */
1095   /*    Modify the loaded cvt table according to the `cvar' table and the  */
1096   /*    font's blend.                                                      */
1097   /*                                                                       */
1098   /* <InOut>                                                               */
1099   /*    face   :: A handle to the target face object.                      */
1100   /*                                                                       */
1101   /* <Input>                                                               */
1102   /*    stream :: A handle to the input stream.                            */
1103   /*                                                                       */
1104   /* <Return>                                                              */
1105   /*    FreeType error code.  0 means success.                             */
1106   /*                                                                       */
1107   /*    Most errors are ignored.  It is perfectly valid not to have a      */
1108   /*    `cvar' table even if there is a `gvar' and `fvar' table.           */
1109   /*                                                                       */
1110   FT_LOCAL_DEF( FT_Error )
tt_face_vary_cvt(TT_Face face,FT_Stream stream)1111   tt_face_vary_cvt( TT_Face    face,
1112                     FT_Stream  stream )
1113   {
1114     FT_Error    error;
1115     FT_Memory   memory = stream->memory;
1116     FT_ULong    table_start;
1117     FT_ULong    table_len;
1118     FT_UInt     tupleCount;
1119     FT_ULong    offsetToData;
1120     FT_ULong    here;
1121     FT_UInt     i, j;
1122     FT_Fixed*   tuple_coords    = NULL;
1123     FT_Fixed*   im_start_coords = NULL;
1124     FT_Fixed*   im_end_coords   = NULL;
1125     GX_Blend    blend           = face->blend;
1126     FT_UInt     point_count;
1127     FT_UShort*  localpoints;
1128     FT_Short*   deltas;
1129 
1130 
1131     FT_TRACE2(( "CVAR " ));
1132 
1133     if ( blend == NULL )
1134     {
1135       FT_TRACE2(( "no blend specified!\n" ));
1136 
1137       error = TT_Err_Ok;
1138       goto Exit;
1139     }
1140 
1141     if ( face->cvt == NULL )
1142     {
1143       FT_TRACE2(( "no `cvt ' table!\n" ));
1144 
1145       error = TT_Err_Ok;
1146       goto Exit;
1147     }
1148 
1149     error = face->goto_table( face, TTAG_cvar, stream, &table_len );
1150     if ( error )
1151     {
1152       FT_TRACE2(( "is missing!\n" ));
1153 
1154       error = TT_Err_Ok;
1155       goto Exit;
1156     }
1157 
1158     if ( FT_FRAME_ENTER( table_len ) )
1159     {
1160       error = TT_Err_Ok;
1161       goto Exit;
1162     }
1163 
1164     table_start = FT_Stream_FTell( stream );
1165     if ( FT_GET_LONG() != 0x00010000L )
1166     {
1167       FT_TRACE2(( "bad table version!\n" ));
1168 
1169       error = TT_Err_Ok;
1170       goto FExit;
1171     }
1172 
1173     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
1174          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
1175          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
1176       goto FExit;
1177 
1178     tupleCount   = FT_GET_USHORT();
1179     offsetToData = table_start + FT_GET_USHORT();
1180 
1181     /* The documentation implies there are flags packed into the        */
1182     /* tuplecount, but John Jenkins says that shared points don't apply */
1183     /* to `cvar', and no other flags are defined.                       */
1184 
1185     for ( i = 0; i < ( tupleCount & 0xFFF ); ++i )
1186     {
1187       FT_UInt   tupleDataSize;
1188       FT_UInt   tupleIndex;
1189       FT_Fixed  apply;
1190 
1191 
1192       tupleDataSize = FT_GET_USHORT();
1193       tupleIndex    = FT_GET_USHORT();
1194 
1195       /* There is no provision here for a global tuple coordinate section, */
1196       /* so John says.  There are no tuple indices, just embedded tuples.  */
1197 
1198       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
1199       {
1200         for ( j = 0; j < blend->num_axis; ++j )
1201           tuple_coords[j] = FT_GET_SHORT() << 2; /* convert from        */
1202                                                  /* short frac to fixed */
1203       }
1204       else
1205       {
1206         /* skip this tuple; it makes no sense */
1207 
1208         if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
1209           for ( j = 0; j < 2 * blend->num_axis; ++j )
1210             (void)FT_GET_SHORT();
1211 
1212         offsetToData += tupleDataSize;
1213         continue;
1214       }
1215 
1216       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
1217       {
1218         for ( j = 0; j < blend->num_axis; ++j )
1219           im_start_coords[j] = FT_GET_SHORT() << 2;
1220         for ( j = 0; j < blend->num_axis; ++j )
1221           im_end_coords[j] = FT_GET_SHORT() << 2;
1222       }
1223 
1224       apply = ft_var_apply_tuple( blend,
1225                                   (FT_UShort)tupleIndex,
1226                                   tuple_coords,
1227                                   im_start_coords,
1228                                   im_end_coords );
1229       if ( /* tuple isn't active for our blend */
1230            apply == 0                                    ||
1231            /* global points not allowed,           */
1232            /* if they aren't local, makes no sense */
1233            !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) )
1234       {
1235         offsetToData += tupleDataSize;
1236         continue;
1237       }
1238 
1239       here = FT_Stream_FTell( stream );
1240 
1241       FT_Stream_SeekSet( stream, offsetToData );
1242 
1243       localpoints = ft_var_readpackedpoints( stream, &point_count );
1244       deltas      = ft_var_readpackeddeltas( stream,
1245                                              point_count == 0 ? face->cvt_size
1246                                                               : point_count );
1247       if ( localpoints == NULL || deltas == NULL )
1248         /* failure, ignore it */;
1249 
1250       else if ( localpoints == ALL_POINTS )
1251       {
1252         /* this means that there are deltas for every entry in cvt */
1253         for ( j = 0; j < face->cvt_size; ++j )
1254           face->cvt[j] = (FT_Short)( face->cvt[j] +
1255                                      FT_MulFix( deltas[j], apply ) );
1256       }
1257 
1258       else
1259       {
1260         for ( j = 0; j < point_count; ++j )
1261         {
1262           int  pindex = localpoints[j];
1263 
1264           face->cvt[pindex] = (FT_Short)( face->cvt[pindex] +
1265                                           FT_MulFix( deltas[j], apply ) );
1266         }
1267       }
1268 
1269       if ( localpoints != ALL_POINTS )
1270         FT_FREE( localpoints );
1271       FT_FREE( deltas );
1272 
1273       offsetToData += tupleDataSize;
1274 
1275       FT_Stream_SeekSet( stream, here );
1276     }
1277 
1278   FExit:
1279     FT_FRAME_EXIT();
1280 
1281   Exit:
1282     FT_FREE( tuple_coords );
1283     FT_FREE( im_start_coords );
1284     FT_FREE( im_end_coords );
1285 
1286     return error;
1287   }
1288 
1289 
1290   /*************************************************************************/
1291   /*                                                                       */
1292   /* <Function>                                                            */
1293   /*    TT_Vary_Get_Glyph_Deltas                                           */
1294   /*                                                                       */
1295   /* <Description>                                                         */
1296   /*    Load the appropriate deltas for the current glyph.                 */
1297   /*                                                                       */
1298   /* <Input>                                                               */
1299   /*    face        :: A handle to the target face object.                 */
1300   /*                                                                       */
1301   /*    glyph_index :: The index of the glyph being modified.              */
1302   /*                                                                       */
1303   /*    n_points    :: The number of the points in the glyph, including    */
1304   /*                   phantom points.                                     */
1305   /*                                                                       */
1306   /* <Output>                                                              */
1307   /*    deltas      :: The array of points to change.                      */
1308   /*                                                                       */
1309   /* <Return>                                                              */
1310   /*    FreeType error code.  0 means success.                             */
1311   /*                                                                       */
1312   FT_LOCAL_DEF( FT_Error )
TT_Vary_Get_Glyph_Deltas(TT_Face face,FT_UInt glyph_index,FT_Vector ** deltas,FT_UInt n_points)1313   TT_Vary_Get_Glyph_Deltas( TT_Face      face,
1314                             FT_UInt      glyph_index,
1315                             FT_Vector*  *deltas,
1316                             FT_UInt      n_points )
1317   {
1318     FT_Stream   stream = face->root.stream;
1319     FT_Memory   memory = stream->memory;
1320     GX_Blend    blend  = face->blend;
1321     FT_Vector*  delta_xy;
1322 
1323     FT_Error    error;
1324     FT_ULong    glyph_start;
1325     FT_UInt     tupleCount;
1326     FT_ULong    offsetToData;
1327     FT_ULong    here;
1328     FT_UInt     i, j;
1329     FT_Fixed*   tuple_coords    = NULL;
1330     FT_Fixed*   im_start_coords = NULL;
1331     FT_Fixed*   im_end_coords   = NULL;
1332     FT_UInt     point_count, spoint_count = 0;
1333     FT_UShort*  sharedpoints = NULL;
1334     FT_UShort*  localpoints  = NULL;
1335     FT_UShort*  points;
1336     FT_Short    *deltas_x, *deltas_y;
1337 
1338 
1339     if ( !face->doblend || blend == NULL )
1340       return TT_Err_Invalid_Argument;
1341 
1342     /* to be freed by the caller */
1343     if ( FT_NEW_ARRAY( delta_xy, n_points ) )
1344       goto Exit;
1345     *deltas = delta_xy;
1346 
1347     if ( glyph_index >= blend->gv_glyphcnt      ||
1348          blend->glyphoffsets[glyph_index] ==
1349            blend->glyphoffsets[glyph_index + 1] )
1350       return TT_Err_Ok;               /* no variation data for this glyph */
1351 
1352     if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] )   ||
1353          FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] -
1354                            blend->glyphoffsets[glyph_index] ) )
1355       goto Fail1;
1356 
1357     glyph_start = FT_Stream_FTell( stream );
1358 
1359     /* each set of glyph variation data is formatted similarly to `cvar' */
1360     /* (except we get shared points and global tuples)                   */
1361 
1362     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
1363          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
1364          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
1365       goto Fail2;
1366 
1367     tupleCount   = FT_GET_USHORT();
1368     offsetToData = glyph_start + FT_GET_USHORT();
1369 
1370     if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
1371     {
1372       here = FT_Stream_FTell( stream );
1373 
1374       FT_Stream_SeekSet( stream, offsetToData );
1375 
1376       sharedpoints = ft_var_readpackedpoints( stream, &spoint_count );
1377       offsetToData = FT_Stream_FTell( stream );
1378 
1379       FT_Stream_SeekSet( stream, here );
1380     }
1381 
1382     for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); ++i )
1383     {
1384       FT_UInt   tupleDataSize;
1385       FT_UInt   tupleIndex;
1386       FT_Fixed  apply;
1387 
1388 
1389       tupleDataSize = FT_GET_USHORT();
1390       tupleIndex    = FT_GET_USHORT();
1391 
1392       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
1393       {
1394         for ( j = 0; j < blend->num_axis; ++j )
1395           tuple_coords[j] = FT_GET_SHORT() << 2;  /* convert from        */
1396                                                   /* short frac to fixed */
1397       }
1398       else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
1399       {
1400         error = TT_Err_Invalid_Table;
1401         goto Fail3;
1402       }
1403       else
1404       {
1405         FT_MEM_COPY(
1406           tuple_coords,
1407           &blend->tuplecoords[(tupleIndex & 0xFFF) * blend->num_axis],
1408           blend->num_axis * sizeof ( FT_Fixed ) );
1409       }
1410 
1411       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
1412       {
1413         for ( j = 0; j < blend->num_axis; ++j )
1414           im_start_coords[j] = FT_GET_SHORT() << 2;
1415         for ( j = 0; j < blend->num_axis; ++j )
1416           im_end_coords[j] = FT_GET_SHORT() << 2;
1417       }
1418 
1419       apply = ft_var_apply_tuple( blend,
1420                                   (FT_UShort)tupleIndex,
1421                                   tuple_coords,
1422                                   im_start_coords,
1423                                   im_end_coords );
1424 
1425       if ( apply == 0 )              /* tuple isn't active for our blend */
1426       {
1427         offsetToData += tupleDataSize;
1428         continue;
1429       }
1430 
1431       here = FT_Stream_FTell( stream );
1432 
1433       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
1434       {
1435         FT_Stream_SeekSet( stream, offsetToData );
1436 
1437         localpoints = ft_var_readpackedpoints( stream, &point_count );
1438         points      = localpoints;
1439       }
1440       else
1441       {
1442         points      = sharedpoints;
1443         point_count = spoint_count;
1444       }
1445 
1446       deltas_x = ft_var_readpackeddeltas( stream,
1447                                           point_count == 0 ? n_points
1448                                                            : point_count );
1449       deltas_y = ft_var_readpackeddeltas( stream,
1450                                           point_count == 0 ? n_points
1451                                                            : point_count );
1452 
1453       if ( points == NULL || deltas_y == NULL || deltas_x == NULL )
1454         ; /* failure, ignore it */
1455 
1456       else if ( points == ALL_POINTS )
1457       {
1458         /* this means that there are deltas for every point in the glyph */
1459         for ( j = 0; j < n_points; ++j )
1460         {
1461           delta_xy[j].x += FT_MulFix( deltas_x[j], apply );
1462           delta_xy[j].y += FT_MulFix( deltas_y[j], apply );
1463         }
1464       }
1465 
1466       else
1467       {
1468         for ( j = 0; j < point_count; ++j )
1469         {
1470           delta_xy[localpoints[j]].x += FT_MulFix( deltas_x[j], apply );
1471           delta_xy[localpoints[j]].y += FT_MulFix( deltas_y[j], apply );
1472         }
1473       }
1474 
1475       if ( localpoints != ALL_POINTS )
1476         FT_FREE( localpoints );
1477       FT_FREE( deltas_x );
1478       FT_FREE( deltas_y );
1479 
1480       offsetToData += tupleDataSize;
1481 
1482       FT_Stream_SeekSet( stream, here );
1483     }
1484 
1485   Fail3:
1486     FT_FREE( tuple_coords );
1487     FT_FREE( im_start_coords );
1488     FT_FREE( im_end_coords );
1489 
1490   Fail2:
1491     FT_FRAME_EXIT();
1492 
1493   Fail1:
1494     if ( error )
1495     {
1496       FT_FREE( delta_xy );
1497       *deltas = NULL;
1498     }
1499 
1500   Exit:
1501     return error;
1502   }
1503 
1504 
1505   /*************************************************************************/
1506   /*                                                                       */
1507   /* <Function>                                                            */
1508   /*    tt_done_blend                                                      */
1509   /*                                                                       */
1510   /* <Description>                                                         */
1511   /*    Frees the blend internal data structure.                           */
1512   /*                                                                       */
1513   FT_LOCAL_DEF( void )
tt_done_blend(FT_Memory memory,GX_Blend blend)1514   tt_done_blend( FT_Memory  memory,
1515                  GX_Blend   blend )
1516   {
1517     if ( blend != NULL )
1518     {
1519       FT_UInt  i;
1520 
1521 
1522       FT_FREE( blend->normalizedcoords );
1523       FT_FREE( blend->mmvar );
1524 
1525       if ( blend->avar_segment != NULL )
1526       {
1527         for ( i = 0; i < blend->num_axis; ++i )
1528           FT_FREE( blend->avar_segment[i].correspondence );
1529         FT_FREE( blend->avar_segment );
1530       }
1531 
1532       FT_FREE( blend->tuplecoords );
1533       FT_FREE( blend->glyphoffsets );
1534       FT_FREE( blend );
1535     }
1536   }
1537 
1538 #endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
1539 
1540 
1541 /* END */
1542