PageRenderTime 81ms CodeModel.GetById 19ms app.highlight 52ms RepoModel.GetById 1ms app.codeStats 1ms

/src/freetype/src/truetype/ttgxvar.c

https://bitbucket.org/cabalistic/ogredeps/
C | 1551 lines | 957 code | 258 blank | 336 comment | 219 complexity | 2f115255783908edda59cff6048fa5bd MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/***************************************************************************/
   2/*                                                                         */
   3/*  ttgxvar.c                                                              */
   4/*                                                                         */
   5/*    TrueType GX Font Variation loader                                    */
   6/*                                                                         */
   7/*  Copyright 2004-2011 by                                                 */
   8/*  David Turner, Robert Wilhelm, Werner Lemberg, and George Williams.     */
   9/*                                                                         */
  10/*  This file is part of the FreeType project, and may only be used,       */
  11/*  modified, and distributed under the terms of the FreeType project      */
  12/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
  13/*  this file you indicate that you have read the license and              */
  14/*  understand and accept it fully.                                        */
  15/*                                                                         */
  16/***************************************************************************/
  17
  18
  19  /*************************************************************************/
  20  /*                                                                       */
  21  /* Apple documents the `fvar', `gvar', `cvar', and `avar' tables at      */
  22  /*                                                                       */
  23  /*   http://developer.apple.com/fonts/TTRefMan/RM06/Chap6[fgca]var.html  */
  24  /*                                                                       */
  25  /* The documentation for `fvar' is inconsistent.  At one point it says   */
  26  /* that `countSizePairs' should be 3, at another point 2.  It should     */
  27  /* be 2.                                                                 */
  28  /*                                                                       */
  29  /* The documentation for `gvar' is not intelligible; `cvar' refers you   */
  30  /* to `gvar' and is thus also incomprehensible.                          */
  31  /*                                                                       */
  32  /* The documentation for `avar' appears correct, but Apple has no fonts  */
  33  /* with an `avar' table, so it is hard to test.                          */
  34  /*                                                                       */
  35  /* Many thanks to John Jenkins (at Apple) in figuring this out.          */
  36  /*                                                                       */
  37  /*                                                                       */
  38  /* Apple's `kern' table has some references to tuple indices, but as     */
  39  /* there is no indication where these indices are defined, nor how to    */
  40  /* interpolate the kerning values (different tuples have different       */
  41  /* classes) this issue is ignored.                                       */
  42  /*                                                                       */
  43  /*************************************************************************/
  44
  45
  46#include <ft2build.h>
  47#include FT_INTERNAL_DEBUG_H
  48#include FT_CONFIG_CONFIG_H
  49#include FT_INTERNAL_STREAM_H
  50#include FT_INTERNAL_SFNT_H
  51#include FT_TRUETYPE_TAGS_H
  52#include FT_MULTIPLE_MASTERS_H
  53
  54#include "ttpload.h"
  55#include "ttgxvar.h"
  56
  57#include "tterrors.h"
  58
  59
  60#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
  61
  62
  63#define FT_Stream_FTell( stream )  \
  64          ( (stream)->cursor - (stream)->base )
  65#define FT_Stream_SeekSet( stream, off ) \
  66              ( (stream)->cursor = (stream)->base+(off) )
  67
  68
  69  /*************************************************************************/
  70  /*                                                                       */
  71  /* The macro FT_COMPONENT is used in trace mode.  It is an implicit      */
  72  /* parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log  */
  73  /* messages during execution.                                            */
  74  /*                                                                       */
  75#undef  FT_COMPONENT
  76#define FT_COMPONENT  trace_ttgxvar
  77
  78
  79  /*************************************************************************/
  80  /*************************************************************************/
  81  /*****                                                               *****/
  82  /*****                       Internal Routines                       *****/
  83  /*****                                                               *****/
  84  /*************************************************************************/
  85  /*************************************************************************/
  86
  87
  88  /*************************************************************************/
  89  /*                                                                       */
  90  /* The macro ALL_POINTS is used in `ft_var_readpackedpoints'.  It        */
  91  /* indicates that there is a delta for every point without needing to    */
  92  /* enumerate all of them.                                                */
  93  /*                                                                       */
  94#define ALL_POINTS  (FT_UShort*)( -1 )
  95
  96
  97#define GX_PT_POINTS_ARE_WORDS      0x80
  98#define GX_PT_POINT_RUN_COUNT_MASK  0x7F
  99
 100
 101  /*************************************************************************/
 102  /*                                                                       */
 103  /* <Function>                                                            */
 104  /*    ft_var_readpackedpoints                                            */
 105  /*                                                                       */
 106  /* <Description>                                                         */
 107  /*    Read a set of points to which the following deltas will apply.     */
 108  /*    Points are packed with a run length encoding.                      */
 109  /*                                                                       */
 110  /* <Input>                                                               */
 111  /*    stream    :: The data stream.                                      */
 112  /*                                                                       */
 113  /* <Output>                                                              */
 114  /*    point_cnt :: The number of points read.  A zero value means that   */
 115  /*                 all points in the glyph will be affected, without     */
 116  /*                 enumerating them individually.                        */
 117  /*                                                                       */
 118  /* <Return>                                                              */
 119  /*    An array of FT_UShort containing the affected points or the        */
 120  /*    special value ALL_POINTS.                                          */
 121  /*                                                                       */
 122  static FT_UShort*
 123  ft_var_readpackedpoints( FT_Stream  stream,
 124                           FT_UInt   *point_cnt )
 125  {
 126    FT_UShort *points = NULL;
 127    FT_Int     n;
 128    FT_Int     runcnt;
 129    FT_Int     i;
 130    FT_Int     j;
 131    FT_Int     first;
 132    FT_Memory  memory = stream->memory;
 133    FT_Error   error  = TT_Err_Ok;
 134
 135    FT_UNUSED( error );
 136
 137
 138    *point_cnt = n = FT_GET_BYTE();
 139    if ( n == 0 )
 140      return ALL_POINTS;
 141
 142    if ( n & GX_PT_POINTS_ARE_WORDS )
 143      n = FT_GET_BYTE() | ( ( n & GX_PT_POINT_RUN_COUNT_MASK ) << 8 );
 144
 145    if ( FT_NEW_ARRAY( points, n ) )
 146      return NULL;
 147
 148    i = 0;
 149    while ( i < n )
 150    {
 151      runcnt = FT_GET_BYTE();
 152      if ( runcnt & GX_PT_POINTS_ARE_WORDS )
 153      {
 154        runcnt = runcnt & GX_PT_POINT_RUN_COUNT_MASK;
 155        first  = points[i++] = FT_GET_USHORT();
 156
 157        if ( runcnt < 1 || i + runcnt >= n )
 158          goto Exit;
 159
 160        /* first point not included in runcount */
 161        for ( j = 0; j < runcnt; ++j )
 162          points[i++] = (FT_UShort)( first += FT_GET_USHORT() );
 163      }
 164      else
 165      {
 166        first = points[i++] = FT_GET_BYTE();
 167
 168        if ( runcnt < 1 || i + runcnt >= n )
 169          goto Exit;
 170
 171        for ( j = 0; j < runcnt; ++j )
 172          points[i++] = (FT_UShort)( first += FT_GET_BYTE() );
 173      }
 174    }
 175
 176  Exit:
 177    return points;
 178  }
 179
 180
 181  enum
 182  {
 183    GX_DT_DELTAS_ARE_ZERO      = 0x80,
 184    GX_DT_DELTAS_ARE_WORDS     = 0x40,
 185    GX_DT_DELTA_RUN_COUNT_MASK = 0x3F
 186  };
 187
 188
 189  /*************************************************************************/
 190  /*                                                                       */
 191  /* <Function>                                                            */
 192  /*    ft_var_readpackeddeltas                                            */
 193  /*                                                                       */
 194  /* <Description>                                                         */
 195  /*    Read a set of deltas.  These are packed slightly differently than  */
 196  /*    points.  In particular there is no overall count.                  */
 197  /*                                                                       */
 198  /* <Input>                                                               */
 199  /*    stream    :: The data stream.                                      */
 200  /*                                                                       */
 201  /*    delta_cnt :: The number of to be read.                             */
 202  /*                                                                       */
 203  /* <Return>                                                              */
 204  /*    An array of FT_Short containing the deltas for the affected        */
 205  /*    points.  (This only gets the deltas for one dimension.  It will    */
 206  /*    generally be called twice, once for x, once for y.  When used in   */
 207  /*    cvt table, it will only be called once.)                           */
 208  /*                                                                       */
 209  static FT_Short*
 210  ft_var_readpackeddeltas( FT_Stream  stream,
 211                           FT_Offset  delta_cnt )
 212  {
 213    FT_Short  *deltas = NULL;
 214    FT_UInt    runcnt;
 215    FT_Offset  i;
 216    FT_UInt    j;
 217    FT_Memory  memory = stream->memory;
 218    FT_Error   error  = TT_Err_Ok;
 219
 220    FT_UNUSED( error );
 221
 222
 223    if ( FT_NEW_ARRAY( deltas, delta_cnt ) )
 224      return NULL;
 225
 226    i = 0;
 227    while ( i < delta_cnt )
 228    {
 229      runcnt = FT_GET_BYTE();
 230      if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
 231      {
 232        /* runcnt zeroes get added */
 233        for ( j = 0;
 234              j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
 235              ++j )
 236          deltas[i++] = 0;
 237      }
 238      else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
 239      {
 240        /* runcnt shorts from the stack */
 241        for ( j = 0;
 242              j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
 243              ++j )
 244          deltas[i++] = FT_GET_SHORT();
 245      }
 246      else
 247      {
 248        /* runcnt signed bytes from the stack */
 249        for ( j = 0;
 250              j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) && i < delta_cnt;
 251              ++j )
 252          deltas[i++] = FT_GET_CHAR();
 253      }
 254
 255      if ( j <= ( runcnt & GX_DT_DELTA_RUN_COUNT_MASK ) )
 256      {
 257        /* Bad format */
 258        FT_FREE( deltas );
 259        return NULL;
 260      }
 261    }
 262
 263    return deltas;
 264  }
 265
 266
 267  /*************************************************************************/
 268  /*                                                                       */
 269  /* <Function>                                                            */
 270  /*    ft_var_load_avar                                                   */
 271  /*                                                                       */
 272  /* <Description>                                                         */
 273  /*    Parse the `avar' table if present.  It need not be, so we return   */
 274  /*    nothing.                                                           */
 275  /*                                                                       */
 276  /* <InOut>                                                               */
 277  /*    face :: The font face.                                             */
 278  /*                                                                       */
 279  static void
 280  ft_var_load_avar( TT_Face  face )
 281  {
 282    FT_Stream       stream = FT_FACE_STREAM(face);
 283    FT_Memory       memory = stream->memory;
 284    GX_Blend        blend  = face->blend;
 285    GX_AVarSegment  segment;
 286    FT_Error        error = TT_Err_Ok;
 287    FT_ULong        version;
 288    FT_Long         axisCount;
 289    FT_Int          i, j;
 290    FT_ULong        table_len;
 291
 292    FT_UNUSED( error );
 293
 294
 295    blend->avar_checked = TRUE;
 296    if ( (error = face->goto_table( face, TTAG_avar, stream, &table_len )) != 0 )
 297      return;
 298
 299    if ( FT_FRAME_ENTER( table_len ) )
 300      return;
 301
 302    version   = FT_GET_LONG();
 303    axisCount = FT_GET_LONG();
 304
 305    if ( version != 0x00010000L                       ||
 306         axisCount != (FT_Long)blend->mmvar->num_axis )
 307      goto Exit;
 308
 309    if ( FT_NEW_ARRAY( blend->avar_segment, axisCount ) )
 310      goto Exit;
 311
 312    segment = &blend->avar_segment[0];
 313    for ( i = 0; i < axisCount; ++i, ++segment )
 314    {
 315      segment->pairCount = FT_GET_USHORT();
 316      if ( FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) )
 317      {
 318        /* Failure.  Free everything we have done so far.  We must do */
 319        /* it right now since loading the `avar' table is optional.   */
 320
 321        for ( j = i - 1; j >= 0; --j )
 322          FT_FREE( blend->avar_segment[j].correspondence );
 323
 324        FT_FREE( blend->avar_segment );
 325        blend->avar_segment = NULL;
 326        goto Exit;
 327      }
 328
 329      for ( j = 0; j < segment->pairCount; ++j )
 330      {
 331        segment->correspondence[j].fromCoord =
 332          FT_GET_SHORT() << 2;    /* convert to Fixed */
 333        segment->correspondence[j].toCoord =
 334          FT_GET_SHORT()<<2;    /* convert to Fixed */
 335      }
 336    }
 337
 338  Exit:
 339    FT_FRAME_EXIT();
 340  }
 341
 342
 343  typedef struct  GX_GVar_Head_
 344  {
 345    FT_Long    version;
 346    FT_UShort  axisCount;
 347    FT_UShort  globalCoordCount;
 348    FT_ULong   offsetToCoord;
 349    FT_UShort  glyphCount;
 350    FT_UShort  flags;
 351    FT_ULong   offsetToData;
 352
 353  } GX_GVar_Head;
 354
 355
 356  /*************************************************************************/
 357  /*                                                                       */
 358  /* <Function>                                                            */
 359  /*    ft_var_load_gvar                                                   */
 360  /*                                                                       */
 361  /* <Description>                                                         */
 362  /*    Parses the `gvar' table if present.  If `fvar' is there, `gvar'    */
 363  /*    had better be there too.                                           */
 364  /*                                                                       */
 365  /* <InOut>                                                               */
 366  /*    face :: The font face.                                             */
 367  /*                                                                       */
 368  /* <Return>                                                              */
 369  /*    FreeType error code.  0 means success.                             */
 370  /*                                                                       */
 371  static FT_Error
 372  ft_var_load_gvar( TT_Face  face )
 373  {
 374    FT_Stream     stream = FT_FACE_STREAM(face);
 375    FT_Memory     memory = stream->memory;
 376    GX_Blend      blend  = face->blend;
 377    FT_Error      error;
 378    FT_UInt       i, j;
 379    FT_ULong      table_len;
 380    FT_ULong      gvar_start;
 381    FT_ULong      offsetToData;
 382    GX_GVar_Head  gvar_head;
 383
 384    static const FT_Frame_Field  gvar_fields[] =
 385    {
 386
 387#undef  FT_STRUCTURE
 388#define FT_STRUCTURE  GX_GVar_Head
 389
 390      FT_FRAME_START( 20 ),
 391        FT_FRAME_LONG  ( version ),
 392        FT_FRAME_USHORT( axisCount ),
 393        FT_FRAME_USHORT( globalCoordCount ),
 394        FT_FRAME_ULONG ( offsetToCoord ),
 395        FT_FRAME_USHORT( glyphCount ),
 396        FT_FRAME_USHORT( flags ),
 397        FT_FRAME_ULONG ( offsetToData ),
 398      FT_FRAME_END
 399    };
 400
 401    if ( (error = face->goto_table( face, TTAG_gvar, stream, &table_len )) != 0 )
 402      goto Exit;
 403
 404    gvar_start = FT_STREAM_POS( );
 405    if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) )
 406      goto Exit;
 407
 408    blend->tuplecount  = gvar_head.globalCoordCount;
 409    blend->gv_glyphcnt = gvar_head.glyphCount;
 410    offsetToData       = gvar_start + gvar_head.offsetToData;
 411
 412    if ( gvar_head.version   != (FT_Long)0x00010000L              ||
 413         gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
 414    {
 415      error = TT_Err_Invalid_Table;
 416      goto Exit;
 417    }
 418
 419    if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) )
 420      goto Exit;
 421
 422    if ( gvar_head.flags & 1 )
 423    {
 424      /* long offsets (one more offset than glyphs, to mark size of last) */
 425      if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) )
 426        goto Exit;
 427
 428      for ( i = 0; i <= blend->gv_glyphcnt; ++i )
 429        blend->glyphoffsets[i] = offsetToData + FT_GET_LONG();
 430
 431      FT_FRAME_EXIT();
 432    }
 433    else
 434    {
 435      /* short offsets (one more offset than glyphs, to mark size of last) */
 436      if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) )
 437        goto Exit;
 438
 439      for ( i = 0; i <= blend->gv_glyphcnt; ++i )
 440        blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
 441                                              /* XXX: Undocumented: `*2'! */
 442
 443      FT_FRAME_EXIT();
 444    }
 445
 446    if ( blend->tuplecount != 0 )
 447    {
 448      if ( FT_NEW_ARRAY( blend->tuplecoords,
 449                         gvar_head.axisCount * blend->tuplecount ) )
 450        goto Exit;
 451
 452      if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord )       ||
 453           FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L )                   )
 454        goto Exit;
 455
 456      for ( i = 0; i < blend->tuplecount; ++i )
 457        for ( j = 0 ; j < (FT_UInt)gvar_head.axisCount; ++j )
 458          blend->tuplecoords[i * gvar_head.axisCount + j] =
 459            FT_GET_SHORT() << 2;                /* convert to FT_Fixed */
 460
 461      FT_FRAME_EXIT();
 462    }
 463
 464  Exit:
 465    return error;
 466  }
 467
 468
 469  /*************************************************************************/
 470  /*                                                                       */
 471  /* <Function>                                                            */
 472  /*    ft_var_apply_tuple                                                 */
 473  /*                                                                       */
 474  /* <Description>                                                         */
 475  /*    Figure out whether a given tuple (design) applies to the current   */
 476  /*    blend, and if so, what is the scaling factor.                      */
 477  /*                                                                       */
 478  /* <Input>                                                               */
 479  /*    blend           :: The current blend of the font.                  */
 480  /*                                                                       */
 481  /*    tupleIndex      :: A flag saying whether this is an intermediate   */
 482  /*                       tuple or not.                                   */
 483  /*                                                                       */
 484  /*    tuple_coords    :: The coordinates of the tuple in normalized axis */
 485  /*                       units.                                          */
 486  /*                                                                       */
 487  /*    im_start_coords :: The initial coordinates where this tuple starts */
 488  /*                       to apply (for intermediate coordinates).        */
 489  /*                                                                       */
 490  /*    im_end_coords   :: The final coordinates after which this tuple no */
 491  /*                       longer applies (for intermediate coordinates).  */
 492  /*                                                                       */
 493  /* <Return>                                                              */
 494  /*    An FT_Fixed value containing the scaling factor.                   */
 495  /*                                                                       */
 496  static FT_Fixed
 497  ft_var_apply_tuple( GX_Blend   blend,
 498                      FT_UShort  tupleIndex,
 499                      FT_Fixed*  tuple_coords,
 500                      FT_Fixed*  im_start_coords,
 501                      FT_Fixed*  im_end_coords )
 502  {
 503    FT_UInt   i;
 504    FT_Fixed  apply;
 505    FT_Fixed  temp;
 506
 507
 508    apply = 0x10000L;
 509    for ( i = 0; i < blend->num_axis; ++i )
 510    {
 511      if ( tuple_coords[i] == 0 )
 512        /* It's not clear why (for intermediate tuples) we don't need     */
 513        /* to check against start/end -- the documentation says we don't. */
 514        /* Similarly, it's unclear why we don't need to scale along the   */
 515        /* axis.                                                          */
 516        continue;
 517
 518      else if ( blend->normalizedcoords[i] == 0                           ||
 519                ( blend->normalizedcoords[i] < 0 && tuple_coords[i] > 0 ) ||
 520                ( blend->normalizedcoords[i] > 0 && tuple_coords[i] < 0 ) )
 521      {
 522        apply = 0;
 523        break;
 524      }
 525
 526      else if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
 527        /* not an intermediate tuple */
 528        apply = FT_MulDiv( apply,
 529                           blend->normalizedcoords[i] > 0
 530                             ? blend->normalizedcoords[i]
 531                             : -blend->normalizedcoords[i],
 532                           0x10000L );
 533
 534      else if ( blend->normalizedcoords[i] <= im_start_coords[i] ||
 535                blend->normalizedcoords[i] >= im_end_coords[i]   )
 536      {
 537        apply = 0;
 538        break;
 539      }
 540
 541      else if ( blend->normalizedcoords[i] < tuple_coords[i] )
 542      {
 543        temp = FT_MulDiv( blend->normalizedcoords[i] - im_start_coords[i],
 544                          0x10000L,
 545                          tuple_coords[i] - im_start_coords[i]);
 546        apply = FT_MulDiv( apply, temp, 0x10000L );
 547      }
 548
 549      else
 550      {
 551        temp = FT_MulDiv( im_end_coords[i] - blend->normalizedcoords[i],
 552                          0x10000L,
 553                          im_end_coords[i] - tuple_coords[i] );
 554        apply = FT_MulDiv( apply, temp, 0x10000L );
 555      }
 556    }
 557
 558    return apply;
 559  }
 560
 561
 562  /*************************************************************************/
 563  /*************************************************************************/
 564  /*****                                                               *****/
 565  /*****               MULTIPLE MASTERS SERVICE FUNCTIONS              *****/
 566  /*****                                                               *****/
 567  /*************************************************************************/
 568  /*************************************************************************/
 569
 570
 571  typedef struct  GX_FVar_Head_
 572  {
 573    FT_Long    version;
 574    FT_UShort  offsetToData;
 575    FT_UShort  countSizePairs;
 576    FT_UShort  axisCount;
 577    FT_UShort  axisSize;
 578    FT_UShort  instanceCount;
 579    FT_UShort  instanceSize;
 580
 581  } GX_FVar_Head;
 582
 583
 584  typedef struct  fvar_axis_
 585  {
 586    FT_ULong   axisTag;
 587    FT_ULong   minValue;
 588    FT_ULong   defaultValue;
 589    FT_ULong   maxValue;
 590    FT_UShort  flags;
 591    FT_UShort  nameID;
 592
 593  } GX_FVar_Axis;
 594
 595
 596  /*************************************************************************/
 597  /*                                                                       */
 598  /* <Function>                                                            */
 599  /*    TT_Get_MM_Var                                                      */
 600  /*                                                                       */
 601  /* <Description>                                                         */
 602  /*    Check that the font's `fvar' table is valid, parse it, and return  */
 603  /*    those data.                                                        */
 604  /*                                                                       */
 605  /* <InOut>                                                               */
 606  /*    face   :: The font face.                                           */
 607  /*              TT_Get_MM_Var initializes the blend structure.           */
 608  /*                                                                       */
 609  /* <Output>                                                              */
 610  /*    master :: The `fvar' data (must be freed by caller).               */
 611  /*                                                                       */
 612  /* <Return>                                                              */
 613  /*    FreeType error code.  0 means success.                             */
 614  /*                                                                       */
 615  FT_LOCAL_DEF( FT_Error )
 616  TT_Get_MM_Var( TT_Face      face,
 617                 FT_MM_Var*  *master )
 618  {
 619    FT_Stream            stream = face->root.stream;
 620    FT_Memory            memory = face->root.memory;
 621    FT_ULong             table_len;
 622    FT_Error             error  = TT_Err_Ok;
 623    FT_ULong             fvar_start;
 624    FT_Int               i, j;
 625    FT_MM_Var*           mmvar = NULL;
 626    FT_Fixed*            next_coords;
 627    FT_String*           next_name;
 628    FT_Var_Axis*         a;
 629    FT_Var_Named_Style*  ns;
 630    GX_FVar_Head         fvar_head;
 631
 632    static const FT_Frame_Field  fvar_fields[] =
 633    {
 634
 635#undef  FT_STRUCTURE
 636#define FT_STRUCTURE  GX_FVar_Head
 637
 638      FT_FRAME_START( 16 ),
 639        FT_FRAME_LONG  ( version ),
 640        FT_FRAME_USHORT( offsetToData ),
 641        FT_FRAME_USHORT( countSizePairs ),
 642        FT_FRAME_USHORT( axisCount ),
 643        FT_FRAME_USHORT( axisSize ),
 644        FT_FRAME_USHORT( instanceCount ),
 645        FT_FRAME_USHORT( instanceSize ),
 646      FT_FRAME_END
 647    };
 648
 649    static const FT_Frame_Field  fvaraxis_fields[] =
 650    {
 651
 652#undef  FT_STRUCTURE
 653#define FT_STRUCTURE  GX_FVar_Axis
 654
 655      FT_FRAME_START( 20 ),
 656        FT_FRAME_ULONG ( axisTag ),
 657        FT_FRAME_ULONG ( minValue ),
 658        FT_FRAME_ULONG ( defaultValue ),
 659        FT_FRAME_ULONG ( maxValue ),
 660        FT_FRAME_USHORT( flags ),
 661        FT_FRAME_USHORT( nameID ),
 662      FT_FRAME_END
 663    };
 664
 665
 666    if ( face->blend == NULL )
 667    {
 668      /* both `fvar' and `gvar' must be present */
 669      if ( (error = face->goto_table( face, TTAG_gvar,
 670                                      stream, &table_len )) != 0 )
 671        goto Exit;
 672
 673      if ( (error = face->goto_table( face, TTAG_fvar,
 674                                      stream, &table_len )) != 0 )
 675        goto Exit;
 676
 677      fvar_start = FT_STREAM_POS( );
 678
 679      if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
 680        goto Exit;
 681
 682      if ( fvar_head.version != (FT_Long)0x00010000L                      ||
 683           fvar_head.countSizePairs != 2                                  ||
 684           fvar_head.axisSize != 20                                       ||
 685           /* axisCount limit implied by 16-bit instanceSize */
 686           fvar_head.axisCount > 0x3FFE                                   ||
 687           fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount          ||
 688           /* instanceCount limit implied by limited range of name IDs */
 689           fvar_head.instanceCount > 0x7EFF                               ||
 690           fvar_head.offsetToData + fvar_head.axisCount * 20U +
 691             fvar_head.instanceCount * fvar_head.instanceSize > table_len )
 692      {
 693        error = TT_Err_Invalid_Table;
 694        goto Exit;
 695      }
 696
 697      if ( FT_NEW( face->blend ) )
 698        goto Exit;
 699
 700      /* cannot overflow 32-bit arithmetic because of limits above */
 701      face->blend->mmvar_len =
 702        sizeof ( FT_MM_Var ) +
 703        fvar_head.axisCount * sizeof ( FT_Var_Axis ) +
 704        fvar_head.instanceCount * sizeof ( FT_Var_Named_Style ) +
 705        fvar_head.instanceCount * fvar_head.axisCount * sizeof ( FT_Fixed ) +
 706        5 * fvar_head.axisCount;
 707
 708      if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
 709        goto Exit;
 710      face->blend->mmvar = mmvar;
 711
 712      mmvar->num_axis =
 713        fvar_head.axisCount;
 714      mmvar->num_designs =
 715        (FT_UInt)-1;           /* meaningless in this context; each glyph */
 716                               /* may have a different number of designs  */
 717                               /* (or tuples, as called by Apple)         */
 718      mmvar->num_namedstyles =
 719        fvar_head.instanceCount;
 720      mmvar->axis =
 721        (FT_Var_Axis*)&(mmvar[1]);
 722      mmvar->namedstyle =
 723        (FT_Var_Named_Style*)&(mmvar->axis[fvar_head.axisCount]);
 724
 725      next_coords =
 726        (FT_Fixed*)&(mmvar->namedstyle[fvar_head.instanceCount]);
 727      for ( i = 0; i < fvar_head.instanceCount; ++i )
 728      {
 729        mmvar->namedstyle[i].coords  = next_coords;
 730        next_coords                 += fvar_head.axisCount;
 731      }
 732
 733      next_name = (FT_String*)next_coords;
 734      for ( i = 0; i < fvar_head.axisCount; ++i )
 735      {
 736        mmvar->axis[i].name  = next_name;
 737        next_name           += 5;
 738      }
 739
 740      if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
 741        goto Exit;
 742
 743      a = mmvar->axis;
 744      for ( i = 0; i < fvar_head.axisCount; ++i )
 745      {
 746        GX_FVar_Axis  axis_rec;
 747
 748
 749        if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
 750          goto Exit;
 751        a->tag     = axis_rec.axisTag;
 752        a->minimum = axis_rec.minValue;     /* A Fixed */
 753        a->def     = axis_rec.defaultValue; /* A Fixed */
 754        a->maximum = axis_rec.maxValue;     /* A Fixed */
 755        a->strid   = axis_rec.nameID;
 756
 757        a->name[0] = (FT_String)(   a->tag >> 24 );
 758        a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
 759        a->name[2] = (FT_String)( ( a->tag >>  8 ) & 0xFF );
 760        a->name[3] = (FT_String)( ( a->tag       ) & 0xFF );
 761        a->name[4] = 0;
 762
 763        ++a;
 764      }
 765
 766      ns = mmvar->namedstyle;
 767      for ( i = 0; i < fvar_head.instanceCount; ++i, ++ns )
 768      {
 769        if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) )
 770          goto Exit;
 771
 772        ns->strid       =    FT_GET_USHORT();
 773        (void) /* flags = */ FT_GET_USHORT();
 774
 775        for ( j = 0; j < fvar_head.axisCount; ++j )
 776          ns->coords[j] = FT_GET_ULONG();     /* A Fixed */
 777
 778        FT_FRAME_EXIT();
 779      }
 780    }
 781
 782    if ( master != NULL )
 783    {
 784      FT_UInt  n;
 785
 786
 787      if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
 788        goto Exit;
 789      FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
 790
 791      mmvar->axis =
 792        (FT_Var_Axis*)&(mmvar[1]);
 793      mmvar->namedstyle =
 794        (FT_Var_Named_Style*)&(mmvar->axis[mmvar->num_axis]);
 795      next_coords =
 796        (FT_Fixed*)&(mmvar->namedstyle[mmvar->num_namedstyles]);
 797
 798      for ( n = 0; n < mmvar->num_namedstyles; ++n )
 799      {
 800        mmvar->namedstyle[n].coords  = next_coords;
 801        next_coords                 += mmvar->num_axis;
 802      }
 803
 804      a = mmvar->axis;
 805      next_name = (FT_String*)next_coords;
 806      for ( n = 0; n < mmvar->num_axis; ++n )
 807      {
 808        a->name = next_name;
 809
 810        /* standard PostScript names for some standard apple tags */
 811        if ( a->tag == TTAG_wght )
 812          a->name = (char *)"Weight";
 813        else if ( a->tag == TTAG_wdth )
 814          a->name = (char *)"Width";
 815        else if ( a->tag == TTAG_opsz )
 816          a->name = (char *)"OpticalSize";
 817        else if ( a->tag == TTAG_slnt )
 818          a->name = (char *)"Slant";
 819
 820        next_name += 5;
 821        ++a;
 822      }
 823
 824      *master = mmvar;
 825    }
 826
 827  Exit:
 828    return error;
 829  }
 830
 831
 832  /*************************************************************************/
 833  /*                                                                       */
 834  /* <Function>                                                            */
 835  /*    TT_Set_MM_Blend                                                    */
 836  /*                                                                       */
 837  /* <Description>                                                         */
 838  /*    Set the blend (normalized) coordinates for this instance of the    */
 839  /*    font.  Check that the `gvar' table is reasonable and does some     */
 840  /*    initial preparation.                                               */
 841  /*                                                                       */
 842  /* <InOut>                                                               */
 843  /*    face       :: The font.                                            */
 844  /*                  Initialize the blend structure with `gvar' data.     */
 845  /*                                                                       */
 846  /* <Input>                                                               */
 847  /*    num_coords :: Must be the axis count of the font.                  */
 848  /*                                                                       */
 849  /*    coords     :: An array of num_coords, each between [-1,1].         */
 850  /*                                                                       */
 851  /* <Return>                                                              */
 852  /*    FreeType error code.  0 means success.                             */
 853  /*                                                                       */
 854  FT_LOCAL_DEF( FT_Error )
 855  TT_Set_MM_Blend( TT_Face    face,
 856                   FT_UInt    num_coords,
 857                   FT_Fixed*  coords )
 858  {
 859    FT_Error    error = TT_Err_Ok;
 860    GX_Blend    blend;
 861    FT_MM_Var*  mmvar;
 862    FT_UInt     i;
 863    FT_Memory   memory = face->root.memory;
 864
 865    enum
 866    {
 867      mcvt_retain,
 868      mcvt_modify,
 869      mcvt_load
 870
 871    } manageCvt;
 872
 873
 874    face->doblend = FALSE;
 875
 876    if ( face->blend == NULL )
 877    {
 878      if ( (error = TT_Get_MM_Var( face, NULL)) != 0 )
 879        goto Exit;
 880    }
 881
 882    blend = face->blend;
 883    mmvar = blend->mmvar;
 884
 885    if ( num_coords != mmvar->num_axis )
 886    {
 887      error = TT_Err_Invalid_Argument;
 888      goto Exit;
 889    }
 890
 891    for ( i = 0; i < num_coords; ++i )
 892      if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
 893      {
 894        error = TT_Err_Invalid_Argument;
 895        goto Exit;
 896      }
 897
 898    if ( blend->glyphoffsets == NULL )
 899      if ( (error = ft_var_load_gvar( face )) != 0 )
 900        goto Exit;
 901
 902    if ( blend->normalizedcoords == NULL )
 903    {
 904      if ( FT_NEW_ARRAY( blend->normalizedcoords, num_coords ) )
 905        goto Exit;
 906
 907      manageCvt = mcvt_modify;
 908
 909      /* If we have not set the blend coordinates before this, then the  */
 910      /* cvt table will still be what we read from the `cvt ' table and  */
 911      /* we don't need to reload it.  We may need to change it though... */
 912    }
 913    else
 914    {
 915      manageCvt = mcvt_retain;
 916      for ( i = 0; i < num_coords; ++i )
 917      {
 918        if ( blend->normalizedcoords[i] != coords[i] )
 919        {
 920          manageCvt = mcvt_load;
 921          break;
 922        }
 923      }
 924
 925      /* If we don't change the blend coords then we don't need to do  */
 926      /* anything to the cvt table.  It will be correct.  Otherwise we */
 927      /* no longer have the original cvt (it was modified when we set  */
 928      /* the blend last time), so we must reload and then modify it.   */
 929    }
 930
 931    blend->num_axis = num_coords;
 932    FT_MEM_COPY( blend->normalizedcoords,
 933                 coords,
 934                 num_coords * sizeof ( FT_Fixed ) );
 935
 936    face->doblend = TRUE;
 937
 938    if ( face->cvt != NULL )
 939    {
 940      switch ( manageCvt )
 941      {
 942      case mcvt_load:
 943        /* The cvt table has been loaded already; every time we change the */
 944        /* blend we may need to reload and remodify the cvt table.         */
 945        FT_FREE( face->cvt );
 946        face->cvt = NULL;
 947
 948        tt_face_load_cvt( face, face->root.stream );
 949        break;
 950
 951      case mcvt_modify:
 952        /* The original cvt table is in memory.  All we need to do is */
 953        /* apply the `cvar' table (if any).                           */
 954        tt_face_vary_cvt( face, face->root.stream );
 955        break;
 956
 957      case mcvt_retain:
 958        /* The cvt table is correct for this set of coordinates. */
 959        break;
 960      }
 961    }
 962
 963  Exit:
 964    return error;
 965  }
 966
 967
 968  /*************************************************************************/
 969  /*                                                                       */
 970  /* <Function>                                                            */
 971  /*    TT_Set_Var_Design                                                  */
 972  /*                                                                       */
 973  /* <Description>                                                         */
 974  /*    Set the coordinates for the instance, measured in the user         */
 975  /*    coordinate system.  Parse the `avar' table (if present) to convert */
 976  /*    from user to normalized coordinates.                               */
 977  /*                                                                       */
 978  /* <InOut>                                                               */
 979  /*    face       :: The font face.                                       */
 980  /*                  Initialize the blend struct with `gvar' data.        */
 981  /*                                                                       */
 982  /* <Input>                                                               */
 983  /*    num_coords :: This must be the axis count of the font.             */
 984  /*                                                                       */
 985  /*    coords     :: A coordinate array with `num_coords' elements.       */
 986  /*                                                                       */
 987  /* <Return>                                                              */
 988  /*    FreeType error code.  0 means success.                             */
 989  /*                                                                       */
 990  FT_LOCAL_DEF( FT_Error )
 991  TT_Set_Var_Design( TT_Face    face,
 992                     FT_UInt    num_coords,
 993                     FT_Fixed*  coords )
 994  {
 995    FT_Error        error      = TT_Err_Ok;
 996    FT_Fixed*       normalized = NULL;
 997    GX_Blend        blend;
 998    FT_MM_Var*      mmvar;
 999    FT_UInt         i, j;
1000    FT_Var_Axis*    a;
1001    GX_AVarSegment  av;
1002    FT_Memory       memory = face->root.memory;
1003
1004
1005    if ( face->blend == NULL )
1006    {
1007      if ( (error = TT_Get_MM_Var( face, NULL )) != 0 )
1008        goto Exit;
1009    }
1010
1011    blend = face->blend;
1012    mmvar = blend->mmvar;
1013
1014    if ( num_coords != mmvar->num_axis )
1015    {
1016      error = TT_Err_Invalid_Argument;
1017      goto Exit;
1018    }
1019
1020    /* Axis normalization is a two stage process.  First we normalize */
1021    /* based on the [min,def,max] values for the axis to be [-1,0,1]. */
1022    /* Then, if there's an `avar' table, we renormalize this range.   */
1023
1024    if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
1025      goto Exit;
1026
1027    a = mmvar->axis;
1028    for ( i = 0; i < mmvar->num_axis; ++i, ++a )
1029    {
1030      if ( coords[i] > a->maximum || coords[i] < a->minimum )
1031      {
1032        error = TT_Err_Invalid_Argument;
1033        goto Exit;
1034      }
1035
1036      if ( coords[i] < a->def )
1037      {
1038        normalized[i] = -FT_MulDiv( coords[i] - a->def,
1039                                    0x10000L,
1040                                    a->minimum - a->def );
1041      }
1042      else if ( a->maximum == a->def )
1043        normalized[i] = 0;
1044      else
1045      {
1046        normalized[i] = FT_MulDiv( coords[i] - a->def,
1047                                   0x10000L,
1048                                   a->maximum - a->def );
1049      }
1050    }
1051
1052    if ( !blend->avar_checked )
1053      ft_var_load_avar( face );
1054
1055    if ( blend->avar_segment != NULL )
1056    {
1057      av = blend->avar_segment;
1058      for ( i = 0; i < mmvar->num_axis; ++i, ++av )
1059      {
1060        for ( j = 1; j < (FT_UInt)av->pairCount; ++j )
1061          if ( normalized[i] < av->correspondence[j].fromCoord )
1062          {
1063            normalized[i] =
1064              FT_MulDiv(
1065                FT_MulDiv(
1066                  normalized[i] - av->correspondence[j - 1].fromCoord,
1067                  0x10000L,
1068                  av->correspondence[j].fromCoord -
1069                    av->correspondence[j - 1].fromCoord ),
1070                av->correspondence[j].toCoord -
1071                  av->correspondence[j - 1].toCoord,
1072                0x10000L ) +
1073              av->correspondence[j - 1].toCoord;
1074            break;
1075          }
1076      }
1077    }
1078
1079    error = TT_Set_MM_Blend( face, num_coords, normalized );
1080
1081  Exit:
1082    FT_FREE( normalized );
1083    return error;
1084  }
1085
1086
1087  /*************************************************************************/
1088  /*************************************************************************/
1089  /*****                                                               *****/
1090  /*****                     GX VAR PARSING ROUTINES                   *****/
1091  /*****                                                               *****/
1092  /*************************************************************************/
1093  /*************************************************************************/
1094
1095
1096  /*************************************************************************/
1097  /*                                                                       */
1098  /* <Function>                                                            */
1099  /*    tt_face_vary_cvt                                                   */
1100  /*                                                                       */
1101  /* <Description>                                                         */
1102  /*    Modify the loaded cvt table according to the `cvar' table and the  */
1103  /*    font's blend.                                                      */
1104  /*                                                                       */
1105  /* <InOut>                                                               */
1106  /*    face   :: A handle to the target face object.                      */
1107  /*                                                                       */
1108  /* <Input>                                                               */
1109  /*    stream :: A handle to the input stream.                            */
1110  /*                                                                       */
1111  /* <Return>                                                              */
1112  /*    FreeType error code.  0 means success.                             */
1113  /*                                                                       */
1114  /*    Most errors are ignored.  It is perfectly valid not to have a      */
1115  /*    `cvar' table even if there is a `gvar' and `fvar' table.           */
1116  /*                                                                       */
1117  FT_LOCAL_DEF( FT_Error )
1118  tt_face_vary_cvt( TT_Face    face,
1119                    FT_Stream  stream )
1120  {
1121    FT_Error    error;
1122    FT_Memory   memory = stream->memory;
1123    FT_ULong    table_start;
1124    FT_ULong    table_len;
1125    FT_UInt     tupleCount;
1126    FT_ULong    offsetToData;
1127    FT_ULong    here;
1128    FT_UInt     i, j;
1129    FT_Fixed*   tuple_coords    = NULL;
1130    FT_Fixed*   im_start_coords = NULL;
1131    FT_Fixed*   im_end_coords   = NULL;
1132    GX_Blend    blend           = face->blend;
1133    FT_UInt     point_count;
1134    FT_UShort*  localpoints;
1135    FT_Short*   deltas;
1136
1137
1138    FT_TRACE2(( "CVAR " ));
1139
1140    if ( blend == NULL )
1141    {
1142      FT_TRACE2(( "tt_face_vary_cvt: no blend specified\n" ));
1143
1144      error = TT_Err_Ok;
1145      goto Exit;
1146    }
1147
1148    if ( face->cvt == NULL )
1149    {
1150      FT_TRACE2(( "tt_face_vary_cvt: no `cvt ' table\n" ));
1151
1152      error = TT_Err_Ok;
1153      goto Exit;
1154    }
1155
1156    error = face->goto_table( face, TTAG_cvar, stream, &table_len );
1157    if ( error )
1158    {
1159      FT_TRACE2(( "is missing\n" ));
1160
1161      error = TT_Err_Ok;
1162      goto Exit;
1163    }
1164
1165    if ( FT_FRAME_ENTER( table_len ) )
1166    {
1167      error = TT_Err_Ok;
1168      goto Exit;
1169    }
1170
1171    table_start = FT_Stream_FTell( stream );
1172    if ( FT_GET_LONG() != 0x00010000L )
1173    {
1174      FT_TRACE2(( "bad table version\n" ));
1175
1176      error = TT_Err_Ok;
1177      goto FExit;
1178    }
1179
1180    if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
1181         FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
1182         FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
1183      goto FExit;
1184
1185    tupleCount   = FT_GET_USHORT();
1186    offsetToData = table_start + FT_GET_USHORT();
1187
1188    /* The documentation implies there are flags packed into the        */
1189    /* tuplecount, but John Jenkins says that shared points don't apply */
1190    /* to `cvar', and no other flags are defined.                       */
1191
1192    for ( i = 0; i < ( tupleCount & 0xFFF ); ++i )
1193    {
1194      FT_UInt   tupleDataSize;
1195      FT_UInt   tupleIndex;
1196      FT_Fixed  apply;
1197
1198
1199      tupleDataSize = FT_GET_USHORT();
1200      tupleIndex    = FT_GET_USHORT();
1201
1202      /* There is no provision here for a global tuple coordinate section, */
1203      /* so John says.  There are no tuple indices, just embedded tuples.  */
1204
1205      if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
1206      {
1207        for ( j = 0; j < blend->num_axis; ++j )
1208          tuple_coords[j] = FT_GET_SHORT() << 2; /* convert from        */
1209                                                 /* short frac to fixed */
1210      }
1211      else
1212      {
1213        /* skip this tuple; it makes no sense */
1214
1215        if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
1216          for ( j = 0; j < 2 * blend->num_axis; ++j )
1217            (void)FT_GET_SHORT();
1218
1219        offsetToData += tupleDataSize;
1220        continue;
1221      }
1222
1223      if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
1224      {
1225        for ( j = 0; j < blend->num_axis; ++j )
1226          im_start_coords[j] = FT_GET_SHORT() << 2;
1227        for ( j = 0; j < blend->num_axis; ++j )
1228          im_end_coords[j] = FT_GET_SHORT() << 2;
1229      }
1230
1231      apply = ft_var_apply_tuple( blend,
1232                                  (FT_UShort)tupleIndex,
1233                                  tuple_coords,
1234                                  im_start_coords,
1235                                  im_end_coords );
1236      if ( /* tuple isn't active for our blend */
1237           apply == 0                                    ||
1238           /* global points not allowed,           */
1239           /* if they aren't local, makes no sense */
1240           !( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS ) )
1241      {
1242        offsetToData += tupleDataSize;
1243        continue;
1244      }
1245
1246      here = FT_Stream_FTell( stream );
1247
1248      FT_Stream_SeekSet( stream, offsetToData );
1249
1250      localpoints = ft_var_readpackedpoints( stream, &point_count );
1251      deltas      = ft_var_readpackeddeltas( stream,
1252                                             point_count == 0 ? face->cvt_size
1253                                                              : point_count );
1254      if ( localpoints == NULL || deltas == NULL )
1255        /* failure, ignore it */;
1256
1257      else if ( localpoints == ALL_POINTS )
1258      {
1259        /* this means that there are deltas for every entry in cvt */
1260        for ( j = 0; j < face->cvt_size; ++j )
1261          face->cvt[j] = (FT_Short)( face->cvt[j] +
1262                                     FT_MulFix( deltas[j], apply ) );
1263      }
1264
1265      else
1266      {
1267        for ( j = 0; j < point_count; ++j )
1268        {
1269          int  pindex = localpoints[j];
1270
1271          face->cvt[pindex] = (FT_Short)( face->cvt[pindex] +
1272                                          FT_MulFix( deltas[j], apply ) );
1273        }
1274      }
1275
1276      if ( localpoints != ALL_POINTS )
1277        FT_FREE( localpoints );
1278      FT_FREE( deltas );
1279
1280      offsetToData += tupleDataSize;
1281
1282      FT_Stream_SeekSet( stream, here );
1283    }
1284
1285  FExit:
1286    FT_FRAME_EXIT();
1287
1288  Exit:
1289    FT_FREE( tuple_coords );
1290    FT_FREE( im_start_coords );
1291    FT_FREE( im_end_coords );
1292
1293    return error;
1294  }
1295
1296
1297  /*************************************************************************/
1298  /*                                                                       */
1299  /* <Function>                                                            */
1300  /*    TT_Vary_Get_Glyph_Deltas                                           */
1301  /*                                                                       */
1302  /* <Description>                                                         */
1303  /*    Load the appropriate deltas for the current glyph.                 */
1304  /*                                                                       */
1305  /* <Input>                                                               */
1306  /*    face        :: A handle to the target face object.                 */
1307  /*                                                                       */
1308  /*    glyph_index :: The index of the glyph being modified.              */
1309  /*                                                                       */
1310  /*    n_points    :: The number of the points in the glyph, including    */
1311  /*                   phantom points.                                     */
1312  /*                                                                       */
1313  /* <Output>                                                              */
1314  /*    deltas      :: The array of points to change.                      */
1315  /*                                                                       */
1316  /* <Return>                                                              */
1317  /*    FreeType error code.  0 means success.                             */
1318  /*                                                                       */
1319  FT_LOCAL_DEF( FT_Error )
1320  TT_Vary_Get_Glyph_Deltas( TT_Face      face,
1321                            FT_UInt      glyph_index,
1322                            FT_Vector*  *deltas,
1323                            FT_UInt      n_points )
1324  {
1325    FT_Stream   stream = face->root.stream;
1326    FT_Memory   memory =

Large files files are truncated, but you can click here to view the full file