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

Last change on this file since 200 was 200, checked in by Eugene Romanenko, 15 years ago

PDF plugin: update freetype to 2.3.1 release

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