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

Last change on this file since 251 was 251, checked in by Eugene Romanenko, 13 years ago

PDF plugin: freetype library updated to version 2.3.5

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      for ( i = 0;
909            i < num_coords && blend->normalizedcoords[i] == coords[i];
910            ++i );
911        if ( i == num_coords )
912          manageCvt = mcvt_retain;
913        else
914          manageCvt = mcvt_load;
915
916      /* If we don't change the blend coords then we don't need to do  */
917      /* anything to the cvt table.  It will be correct.  Otherwise we */
918      /* no longer have the original cvt (it was modified when we set  */
919      /* the blend last time), so we must reload and then modify it.   */
920    }
921
922    blend->num_axis = num_coords;
923    FT_MEM_COPY( blend->normalizedcoords,
924                 coords,
925                 num_coords * sizeof ( FT_Fixed ) );
926
927    face->doblend = TRUE;
928
929    if ( face->cvt != NULL )
930    {
931      switch ( manageCvt )
932      {
933      case mcvt_load:
934        /* The cvt table has been loaded already; every time we change the */
935        /* blend we may need to reload and remodify the cvt table.         */
936        FT_FREE( face->cvt );
937        face->cvt = NULL;
938
939        tt_face_load_cvt( face, face->root.stream );
940        break;
941
942      case mcvt_modify:
943        /* The original cvt table is in memory.  All we need to do is */
944        /* apply the `cvar' table (if any).                           */
945        tt_face_vary_cvt( face, face->root.stream );
946        break;
947
948      case mcvt_retain:
949        /* The cvt table is correct for this set of coordinates. */
950        break;
951      }
952    }
953
954  Exit:
955    return error;
956  }
957
958
959  /*************************************************************************/
960  /*                                                                       */
961  /* <Function>                                                            */
962  /*    TT_Set_Var_Design                                                  */
963  /*                                                                       */
964  /* <Description>                                                         */
965  /*    Set the coordinates for the instance, measured in the user         */
966  /*    coordinate system.  Parse the `avar' table (if present) to convert */
967  /*    from user to normalized coordinates.                               */
968  /*                                                                       */
969  /* <InOut>                                                               */
970  /*    face       :: The font face.                                       */
971  /*                  Initialize the blend struct with `gvar' data.        */
972  /*                                                                       */
973  /* <Input>                                                               */
974  /*    num_coords :: This must be the axis count of the font.             */
975  /*                                                                       */
976  /*    coords     :: A coordinate array with `num_coords' elements.       */
977  /*                                                                       */
978  /* <Return>                                                              */
979  /*    FreeType error code.  0 means success.                             */
980  /*                                                                       */
981  FT_LOCAL_DEF( FT_Error )
982  TT_Set_Var_Design( TT_Face    face,
983                     FT_UInt    num_coords,
984                     FT_Fixed*  coords )
985  {
986    FT_Error        error      = TT_Err_Ok;
987    FT_Fixed*       normalized = NULL;
988    GX_Blend        blend;
989    FT_MM_Var*      mmvar;
990    FT_UInt         i, j;
991    FT_Var_Axis*    a;
992    GX_AVarSegment  av;
993    FT_Memory       memory = face->root.memory;
994
995
996    if ( face->blend == NULL )
997    {
998      if ( (error = TT_Get_MM_Var( face, NULL )) != 0 )
999        goto Exit;
1000    }
1001
1002    blend = face->blend;
1003    mmvar = blend->mmvar;
1004
1005    if ( num_coords != mmvar->num_axis )
1006    {
1007      error = TT_Err_Invalid_Argument;
1008      goto Exit;
1009    }
1010
1011    /* Axis normalization is a two stage process.  First we normalize */
1012    /* based on the [min,def,max] values for the axis to be [-1,0,1]. */
1013    /* Then, if there's an `avar' table, we renormalize this range.   */
1014
1015    if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
1016      goto Exit;
1017
1018    a = mmvar->axis;
1019    for ( i = 0; i < mmvar->num_axis; ++i, ++a )
1020    {
1021      if ( coords[i] > a->maximum || coords[i] < a->minimum )
1022      {
1023        error = TT_Err_Invalid_Argument;
1024        goto Exit;
1025      }
1026
1027      if ( coords[i] < a->def )
1028      {
1029        normalized[i] = -FT_MulDiv( coords[i] - a->def,
1030                                    0x10000L,
1031                                    a->minimum - a->def );
1032      }
1033      else if ( a->maximum == a->def )
1034        normalized[i] = 0;
1035      else
1036      {
1037        normalized[i] = FT_MulDiv( coords[i] - a->def,
1038                                   0x10000L,
1039                                   a->maximum - a->def );
1040      }
1041    }
1042
1043    if ( !blend->avar_checked )
1044      ft_var_load_avar( face );
1045
1046    if ( blend->avar_segment != NULL )
1047    {
1048      av = blend->avar_segment;
1049      for ( i = 0; i < mmvar->num_axis; ++i, ++av )
1050      {
1051        for ( j = 1; j < (FT_UInt)av->pairCount; ++j )
1052          if ( normalized[i] < av->correspondence[j].fromCoord )
1053          {
1054            normalized[i] =
1055              FT_MulDiv(
1056                FT_MulDiv(
1057                  normalized[i] - av->correspondence[j - 1].fromCoord,
1058                  0x10000L,
1059                  av->correspondence[j].fromCoord -
1060                    av->correspondence[j - 1].fromCoord ),
1061                av->correspondence[j].toCoord -
1062                  av->correspondence[j - 1].toCoord,
1063                0x10000L ) +
1064              av->correspondence[j - 1].toCoord;
1065            break;
1066          }
1067      }
1068    }
1069
1070    error = TT_Set_MM_Blend( face, num_coords, normalized );
1071
1072  Exit:
1073    FT_FREE( normalized );
1074    return error;
1075  }
1076
1077
1078  /*************************************************************************/
1079  /*************************************************************************/
1080  /*****                                                               *****/
1081  /*****                     GX VAR PARSING ROUTINES                   *****/
1082  /*****                                                               *****/
1083  /*************************************************************************/
1084  /*************************************************************************/
1085
1086
1087  /*************************************************************************/
1088  /*                                                                       */
1089  /* <Function>                                                            */
1090  /*    tt_face_vary_cvt                                                   */
1091  /*                                                                       */
1092  /* <Description>                                                         */
1093  /*    Modify the loaded cvt table according to the `cvar' table and the  */
1094  /*    font's blend.                                                      */
1095  /*                                                                       */
1096  /* <InOut>                                                               */
1097  /*    face   :: A handle to the target face object.                      */
1098  /*                                                                       */
1099  /* <Input>                                                               */
1100  /*    stream :: A handle to the input stream.                            */
1101  /*                                                                       */
1102  /* <Return>                                                              */
1103  /*    FreeType error code.  0 means success.                             */
1104  /*                                                                       */
1105  /*    Most errors are ignored.  It is perfectly valid not to have a      */
1106  /*    `cvar' table even if there is a `gvar' and `fvar' table.           */
1107  /*                                                                       */
1108  FT_LOCAL_DEF( FT_Error )
1109  tt_face_vary_cvt( TT_Face    face,
1110                    FT_Stream  stream )
1111  {
1112    FT_Error    error;
1113    FT_Memory   memory = stream->memory;
1114    FT_ULong    table_start;
1115    FT_ULong    table_len;
1116    FT_UInt     tupleCount;
1117    FT_ULong    offsetToData;
1118    FT_ULong    here;
1119    FT_UInt     i, j;
1120    FT_Fixed*   tuple_coords    = NULL;
1121    FT_Fixed*   im_start_coords = NULL;
1122    FT_Fixed*   im_end_coords   = NULL;
1123    GX_Blend    blend           = face->blend;
1124    FT_UInt     point_count;
1125    FT_UShort*  localpoints;
1126    FT_Short*   deltas;
1127
1128
1129    FT_TRACE2(( "CVAR " ));
1130
1131    if ( blend == NULL )
1132    {
1133      FT_TRACE2(( "no blend specified!\n" ));
1134
1135      error = TT_Err_Ok;
1136      goto Exit;
1137    }
1138
1139    if ( face->cvt == NULL )
1140    {
1141      FT_TRACE2(( "no `cvt ' table!\n" ));
1142
1143      error = TT_Err_Ok;
1144      goto Exit;
1145    }
1146
1147    error = face->goto_table( face, TTAG_cvar, stream, &table_len );
1148    if ( error )
1149    {
1150      FT_TRACE2(( "is missing!\n" ));
1151
1152      error = TT_Err_Ok;
1153      goto Exit;
1154    }
1155
1156    if ( FT_FRAME_ENTER( table_len ) )
1157    {
1158      error = TT_Err_Ok;
1159      goto Exit;
1160    }
1161
1162    table_start = FT_Stream_FTell( stream );
1163    if ( FT_GET_LONG() != 0x00010000L )
1164    {
1165      FT_TRACE2(( "bad table version!\n" ));
1166
1167      error = TT_Err_Ok;
1168      goto FExit;
1169    }
1170
1171    if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
1172         FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
1173         FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
1174      goto FExit;
1175
1176    tupleCount   = FT_GET_USHORT();
1177    offsetToData = table_start + FT_GET_USHORT();
1178
1179    /* The documentation implies there are flags packed into the        */
1180    /* tuplecount, but John Jenkins says that shared points don't apply */
1181    /* to `cvar', and no other flags are defined.                       */
1182
1183    for ( i = 0; i < ( tupleCount & 0xFFF ); ++i )
1184    {
1185      FT_UInt   tupleDataSize;
1186      FT_UInt   tupleIndex;
1187      FT_Fixed  apply;
1188
1189
1190      tupleDataSize = FT_GET_USHORT();
1191      tupleIndex    = FT_GET_USHORT();
1192
1193      /* There is no provision here for a global tuple coordinate section, */
1194      /* so John says.  There are no tuple indices, just embedded tuples.  */
1195
1196      if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
1197      {
1198        for ( j = 0; j < blend->num_axis; ++j )
1199          tuple_coords[j] = FT_GET_SHORT() << 2; /* convert from        */
1200                                                 /* short frac to fixed */
1201      }
1202      else
1203      {
1204        /* skip this tuple; it makes no sense */
1205
1206        if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
1207          for ( j = 0; j < 2 * blend->num_axis; ++j )
1208            (void)FT_GET_SHORT();
1209
1210        offsetToData += tupleDataSize;
1211        continue;
1212      }
1213
1214      if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
1215      {
1216        for ( j = 0; j < blend->num_axis; ++j )
1217          im_start_coords[j] = FT_GET_SHORT() << 2;
1218        for ( j = 0; j < blend->num_axis; ++j )
1219          im_end_coords[j] = FT_GET_SHORT() << 2;
1220      }
1221
1222      apply = ft_var_apply_tuple( blend,
1223                                  (FT_UShort)tupleIndex,
1224                                  tuple_coords,
1225                                  im_start_coords,
1226                                  im_end_coords );
1227      if ( /* tuple isn't active for our blend */
1228           apply == 0                                    ||
1229           /* global points not allowed,           */
1230           /* if they aren't local, makes no sense */
1231           !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) )
1232      {
1233        offsetToData += tupleDataSize;
1234        continue;
1235      }
1236
1237      here = FT_Stream_FTell( stream );
1238
1239      FT_Stream_SeekSet( stream, offsetToData );
1240
1241      localpoints = ft_var_readpackedpoints( stream, &point_count );
1242      deltas      = ft_var_readpackeddeltas( stream,
1243                                             point_count == 0 ? face->cvt_size
1244                                                              : point_count );
1245      if ( localpoints == NULL || deltas == NULL )
1246        /* failure, ignore it */;
1247
1248      else if ( localpoints == ALL_POINTS )
1249      {
1250        /* this means that there are deltas for every entry in cvt */
1251        for ( j = 0; j < face->cvt_size; ++j )
1252          face->cvt[j] = (FT_Short)( face->cvt[j] +
1253                                     FT_MulFix( deltas[j], apply ) );
1254      }
1255
1256      else
1257      {
1258        for ( j = 0; j < point_count; ++j )
1259        {
1260          int  pindex = localpoints[j];
1261
1262          face->cvt[pindex] = (FT_Short)( face->cvt[pindex] +
1263                                          FT_MulFix( deltas[j], apply ) );
1264        }
1265      }
1266
1267      if ( localpoints != ALL_POINTS )
1268        FT_FREE( localpoints );
1269      FT_FREE( deltas );
1270
1271      offsetToData += tupleDataSize;
1272
1273      FT_Stream_SeekSet( stream, here );
1274    }
1275
1276  FExit:
1277    FT_FRAME_EXIT();
1278
1279  Exit:
1280    FT_FREE( tuple_coords );
1281    FT_FREE( im_start_coords );
1282    FT_FREE( im_end_coords );
1283
1284    return error;
1285  }
1286
1287
1288  /*************************************************************************/
1289  /*                                                                       */
1290  /* <Function>                                                            */
1291  /*    TT_Vary_Get_Glyph_Deltas                                           */
1292  /*                                                                       */
1293  /* <Description>                                                         */
1294  /*    Load the appropriate deltas for the current glyph.                 */
1295  /*                                                                       */
1296  /* <Input>                                                               */
1297  /*    face        :: A handle to the target face object.                 */
1298  /*                                                                       */
1299  /*    glyph_index :: The index of the glyph being modified.              */
1300  /*                                                                       */
1301  /*    n_points    :: The number of the points in the glyph, including    */
1302  /*                   phantom points.                                     */
1303  /*                                                                       */
1304  /* <Output>                                                              */
1305  /*    deltas      :: The array of points to change.                      */
1306  /*                                                                       */
1307  /* <Return>                                                              */
1308  /*    FreeType error code.  0 means success.                             */
1309  /*                                                                       */
1310  FT_LOCAL_DEF( FT_Error )
1311  TT_Vary_Get_Glyph_Deltas( TT_Face      face,
1312                            FT_UInt      glyph_index,
1313                            FT_Vector*  *deltas,
1314                            FT_UInt      n_points )
1315  {
1316    FT_Stream   stream = face->root.stream;
1317    FT_Memory   memory = stream->memory;
1318    GX_Blend    blend  = face->blend;
1319    FT_Vector*  delta_xy;
1320
1321    FT_Error    error;
1322    FT_ULong    glyph_start;
1323    FT_UInt     tupleCount;
1324    FT_ULong    offsetToData;
1325    FT_ULong    here;
1326    FT_UInt     i, j;
1327    FT_Fixed*   tuple_coords    = NULL;
1328    FT_Fixed*   im_start_coords = NULL;
1329    FT_Fixed*   im_end_coords   = NULL;
1330    FT_UInt     point_count, spoint_count = 0;
1331    FT_UShort*  sharedpoints = NULL;
1332    FT_UShort*  localpoints  = NULL;
1333    FT_UShort*  points;
1334    FT_Short    *deltas_x, *deltas_y;
1335
1336
1337    if ( !face->doblend || blend == NULL )
1338      return TT_Err_Invalid_Argument;
1339
1340    /* to be freed by the caller */
1341    if ( FT_NEW_ARRAY( delta_xy, n_points ) )
1342      goto Exit;
1343    *deltas = delta_xy;
1344
1345    if ( glyph_index >= blend->gv_glyphcnt      ||
1346         blend->glyphoffsets[glyph_index] ==
1347           blend->glyphoffsets[glyph_index + 1] )
1348      return TT_Err_Ok;               /* no variation data for this glyph */
1349
1350    if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] )   ||
1351         FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] -
1352                           blend->glyphoffsets[glyph_index] ) )
1353      goto Fail1;
1354
1355    glyph_start = FT_Stream_FTell( stream );
1356
1357    /* each set of glyph variation data is formatted similarly to `cvar' */
1358    /* (except we get shared points and global tuples)                   */
1359
1360    if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
1361         FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
1362         FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
1363      goto Fail2;
1364
1365    tupleCount   = FT_GET_USHORT();
1366    offsetToData = glyph_start + FT_GET_USHORT();
1367
1368    if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
1369    {
1370      here = FT_Stream_FTell( stream );
1371
1372      FT_Stream_SeekSet( stream, offsetToData );
1373
1374      sharedpoints = ft_var_readpackedpoints( stream, &spoint_count );
1375      offsetToData = FT_Stream_FTell( stream );
1376
1377      FT_Stream_SeekSet( stream, here );
1378    }
1379
1380    for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); ++i )
1381    {
1382      FT_UInt   tupleDataSize;
1383      FT_UInt   tupleIndex;
1384      FT_Fixed  apply;
1385
1386
1387      tupleDataSize = FT_GET_USHORT();
1388      tupleIndex    = FT_GET_USHORT();
1389
1390      if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
1391      {
1392        for ( j = 0; j < blend->num_axis; ++j )
1393          tuple_coords[j] = FT_GET_SHORT() << 2;  /* convert from        */
1394                                                  /* short frac to fixed */
1395      }
1396      else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
1397      {
1398        error = TT_Err_Invalid_Table;
1399        goto Fail3;
1400      }
1401      else
1402      {
1403        FT_MEM_COPY(
1404          tuple_coords,
1405          &blend->tuplecoords[(tupleIndex & 0xFFF) * blend->num_axis],
1406          blend->num_axis * sizeof ( FT_Fixed ) );
1407      }
1408
1409      if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
1410      {
1411        for ( j = 0; j < blend->num_axis; ++j )
1412          im_start_coords[j] = FT_GET_SHORT() << 2;
1413        for ( j = 0; j < blend->num_axis; ++j )
1414          im_end_coords[j] = FT_GET_SHORT() << 2;
1415      }
1416
1417      apply = ft_var_apply_tuple( blend,
1418                                  (FT_UShort)tupleIndex,
1419                                  tuple_coords,
1420                                  im_start_coords,
1421                                  im_end_coords );
1422
1423      if ( apply == 0 )              /* tuple isn't active for our blend */
1424      {
1425        offsetToData += tupleDataSize;
1426        continue;
1427      }
1428
1429      here = FT_Stream_FTell( stream );
1430
1431      if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
1432      {
1433        FT_Stream_SeekSet( stream, offsetToData );
1434
1435        localpoints = ft_var_readpackedpoints( stream, &point_count );
1436        points      = localpoints;
1437      }
1438      else
1439      {
1440        points      = sharedpoints;
1441        point_count = spoint_count;
1442      }
1443
1444      deltas_x = ft_var_readpackeddeltas( stream,
1445                                          point_count == 0 ? n_points
1446                                                           : point_count );
1447      deltas_y = ft_var_readpackeddeltas( stream,
1448                                          point_count == 0 ? n_points
1449                                                           : point_count );
1450
1451      if ( points == NULL || deltas_y == NULL || deltas_x == NULL )
1452        ; /* failure, ignore it */
1453
1454      else if ( points == ALL_POINTS )
1455      {
1456        /* this means that there are deltas for every point in the glyph */
1457        for ( j = 0; j < n_points; ++j )
1458        {
1459          delta_xy[j].x += FT_MulFix( deltas_x[j], apply );
1460          delta_xy[j].y += FT_MulFix( deltas_y[j], apply );
1461        }
1462      }
1463
1464      else
1465      {
1466        for ( j = 0; j < point_count; ++j )
1467        {
1468          delta_xy[localpoints[j]].x += FT_MulFix( deltas_x[j], apply );
1469          delta_xy[localpoints[j]].y += FT_MulFix( deltas_y[j], apply );
1470        }
1471      }
1472
1473      if ( localpoints != ALL_POINTS )
1474        FT_FREE( localpoints );
1475      FT_FREE( deltas_x );
1476      FT_FREE( deltas_y );
1477
1478      offsetToData += tupleDataSize;
1479
1480      FT_Stream_SeekSet( stream, here );
1481    }
1482
1483  Fail3:
1484    FT_FREE( tuple_coords );
1485    FT_FREE( im_start_coords );
1486    FT_FREE( im_end_coords );
1487
1488  Fail2:
1489    FT_FRAME_EXIT();
1490
1491  Fail1:
1492    if ( error )
1493    {
1494      FT_FREE( delta_xy );
1495      *deltas = NULL;
1496    }
1497
1498  Exit:
1499    return error;
1500  }
1501
1502
1503  /*************************************************************************/
1504  /*                                                                       */
1505  /* <Function>                                                            */
1506  /*    tt_done_blend                                                      */
1507  /*                                                                       */
1508  /* <Description>                                                         */
1509  /*    Frees the blend internal data structure.                           */
1510  /*                                                                       */
1511  FT_LOCAL_DEF( void )
1512  tt_done_blend( FT_Memory  memory,
1513                 GX_Blend   blend )
1514  {
1515    if ( blend != NULL )
1516    {
1517      FT_UInt  i;
1518
1519
1520      FT_FREE( blend->normalizedcoords );
1521      FT_FREE( blend->mmvar );
1522
1523      if ( blend->avar_segment != NULL )
1524      {
1525        for ( i = 0; i < blend->num_axis; ++i )
1526          FT_FREE( blend->avar_segment[i].correspondence );
1527        FT_FREE( blend->avar_segment );
1528      }
1529
1530      FT_FREE( blend->tuplecoords );
1531      FT_FREE( blend->glyphoffsets );
1532      FT_FREE( blend );
1533    }
1534  }
1535
1536#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
1537
1538
1539/* END */
Note: See TracBrowser for help on using the repository browser.