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