source: trunk/poppler/freetype2/src/truetype/ttgxvar.c @ 262

Last change on this file since 262 was 262, checked in by Eugene Romanenko, 12 years ago

PDF plugin: freetype library updated to version 2.3.8

File size: 56.3 KB
Line 
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*
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*
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
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
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
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 )
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 )
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 )
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 )
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 )
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 )
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 */
Note: See TracBrowser for help on using the repository browser.