1 /***************************************************************************/
   2 /*                                                                         */
   3 /*  ttgxvar.c                                                              */
   4 /*                                                                         */
   5 /*    TrueType GX Font Variation loader                                    */
   6 /*                                                                         */
   7 /*  Copyright 2004-2018 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   /*   https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6[fgca]var.html */
  24   /*                                                                       */
  25   /* The documentation for `gvar' is not intelligible; `cvar' refers you   */
  26   /* to `gvar' and is thus also incomprehensible.                          */
  27   /*                                                                       */
  28   /* The documentation for `avar' appears correct, but Apple has no fonts  */
  29   /* with an `avar' table, so it is hard to test.                          */
  30   /*                                                                       */
  31   /* Many thanks to John Jenkins (at Apple) in figuring this out.          */
  32   /*                                                                       */
  33   /*                                                                       */
  34   /* Apple's `kern' table has some references to tuple indices, but as     */
  35   /* there is no indication where these indices are defined, nor how to    */
  36   /* interpolate the kerning values (different tuples have different       */
  37   /* classes) this issue is ignored.                                       */
  38   /*                                                                       */
  39   /*************************************************************************/
  40 
  41 
  42 #include <ft2build.h>
  43 #include FT_INTERNAL_DEBUG_H
  44 #include FT_CONFIG_CONFIG_H
  45 #include FT_INTERNAL_STREAM_H
  46 #include FT_INTERNAL_SFNT_H
  47 #include FT_TRUETYPE_TAGS_H
  48 #include FT_TRUETYPE_IDS_H
  49 #include FT_MULTIPLE_MASTERS_H
  50 #include FT_LIST_H
  51 
  52 #include "ttpload.h"
  53 #include "ttgxvar.h"
  54 
  55 #include "tterrors.h"
  56 
  57 
  58 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
  59 
  60 
  61 #define FT_Stream_FTell( stream )                         \
  62           (FT_ULong)( (stream)->cursor - (stream)->base )
  63 #define FT_Stream_SeekSet( stream, off )                               \
  64           (stream)->cursor =                                           \
  65             ( (off) < (FT_ULong)( (stream)->limit - (stream)->base ) ) \
  66                         ? (stream)->base + (off)                       \
  67                         : (stream)->limit
  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 
  96   /* ensure that value `0' has the same width as a pointer */
  97 #define ALL_POINTS  (FT_UShort*)~(FT_PtrDist)0
  98 
  99 
 100 #define GX_PT_POINTS_ARE_WORDS      0x80U
 101 #define GX_PT_POINT_RUN_COUNT_MASK  0x7FU
 102 
 103 
 104   /*************************************************************************/
 105   /*                                                                       */
 106   /* <Function>                                                            */
 107   /*    ft_var_readpackedpoints                                            */
 108   /*                                                                       */
 109   /* <Description>                                                         */
 110   /*    Read a set of points to which the following deltas will apply.     */
 111   /*    Points are packed with a run length encoding.                      */
 112   /*                                                                       */
 113   /* <Input>                                                               */
 114   /*    stream    :: The data stream.                                      */
 115   /*                                                                       */
 116   /*    size      :: The size of the table holding the data.               */
 117   /*                                                                       */
 118   /* <Output>                                                              */
 119   /*    point_cnt :: The number of points read.  A zero value means that   */
 120   /*                 all points in the glyph will be affected, without     */
 121   /*                 enumerating them individually.                        */
 122   /*                                                                       */
 123   /* <Return>                                                              */
 124   /*    An array of FT_UShort containing the affected points or the        */
 125   /*    special value ALL_POINTS.                                          */
 126   /*                                                                       */
 127   static FT_UShort*
 128   ft_var_readpackedpoints( FT_Stream  stream,
 129                            FT_ULong   size,
 130                            FT_UInt   *point_cnt )
 131   {
 132     FT_UShort *points = NULL;
 133     FT_UInt    n;
 134     FT_UInt    runcnt;
 135     FT_UInt    i, j;
 136     FT_UShort  first;
 137     FT_Memory  memory = stream->memory;
 138     FT_Error   error  = FT_Err_Ok;
 139 
 140     FT_UNUSED( error );
 141 
 142 
 143     *point_cnt = 0;
 144 
 145     n = FT_GET_BYTE();
 146     if ( n == 0 )
 147       return ALL_POINTS;
 148 
 149     if ( n & GX_PT_POINTS_ARE_WORDS )
 150     {
 151       n  &= GX_PT_POINT_RUN_COUNT_MASK;
 152       n <<= 8;
 153       n  |= FT_GET_BYTE();
 154     }
 155 
 156     if ( n > size )
 157     {
 158       FT_TRACE1(( "ft_var_readpackedpoints: number of points too large\n" ));
 159       return NULL;
 160     }
 161 
 162     /* in the nested loops below we increase `i' twice; */
 163     /* it is faster to simply allocate one more slot    */
 164     /* than to add another test within the loop         */
 165     if ( FT_NEW_ARRAY( points, n + 1 ) )
 166       return NULL;
 167 
 168     *point_cnt = n;
 169 
 170     first = 0;
 171     i     = 0;
 172     while ( i < n )
 173     {
 174       runcnt = FT_GET_BYTE();
 175       if ( runcnt & GX_PT_POINTS_ARE_WORDS )
 176       {
 177         runcnt     &= GX_PT_POINT_RUN_COUNT_MASK;
 178         first      += FT_GET_USHORT();
 179         points[i++] = first;
 180 
 181         /* first point not included in run count */
 182         for ( j = 0; j < runcnt; j++ )
 183         {
 184           first      += FT_GET_USHORT();
 185           points[i++] = first;
 186           if ( i >= n )
 187             break;
 188         }
 189       }
 190       else
 191       {
 192         first      += FT_GET_BYTE();
 193         points[i++] = first;
 194 
 195         for ( j = 0; j < runcnt; j++ )
 196         {
 197           first      += FT_GET_BYTE();
 198           points[i++] = first;
 199           if ( i >= n )
 200             break;
 201         }
 202       }
 203     }
 204 
 205     return points;
 206   }
 207 
 208 
 209 #define GX_DT_DELTAS_ARE_ZERO       0x80U
 210 #define GX_DT_DELTAS_ARE_WORDS      0x40U
 211 #define GX_DT_DELTA_RUN_COUNT_MASK  0x3FU
 212 
 213 
 214   /*************************************************************************/
 215   /*                                                                       */
 216   /* <Function>                                                            */
 217   /*    ft_var_readpackeddeltas                                            */
 218   /*                                                                       */
 219   /* <Description>                                                         */
 220   /*    Read a set of deltas.  These are packed slightly differently than  */
 221   /*    points.  In particular there is no overall count.                  */
 222   /*                                                                       */
 223   /* <Input>                                                               */
 224   /*    stream    :: The data stream.                                      */
 225   /*                                                                       */
 226   /*    size      :: The size of the table holding the data.               */
 227   /*                                                                       */
 228   /*    delta_cnt :: The number of deltas to be read.                      */
 229   /*                                                                       */
 230   /* <Return>                                                              */
 231   /*    An array of FT_Short containing the deltas for the affected        */
 232   /*    points.  (This only gets the deltas for one dimension.  It will    */
 233   /*    generally be called twice, once for x, once for y.  When used in   */
 234   /*    cvt table, it will only be called once.)                           */
 235   /*                                                                       */
 236   static FT_Short*
 237   ft_var_readpackeddeltas( FT_Stream  stream,
 238                            FT_ULong   size,
 239                            FT_UInt    delta_cnt )
 240   {
 241     FT_Short  *deltas = NULL;
 242     FT_UInt    runcnt, cnt;
 243     FT_UInt    i, j;
 244     FT_Memory  memory = stream->memory;
 245     FT_Error   error  = FT_Err_Ok;
 246 
 247     FT_UNUSED( error );
 248 
 249 
 250     if ( delta_cnt > size )
 251     {
 252       FT_TRACE1(( "ft_var_readpackeddeltas: number of points too large\n" ));
 253       return NULL;
 254     }
 255 
 256     if ( FT_NEW_ARRAY( deltas, delta_cnt ) )
 257       return NULL;
 258 
 259     i = 0;
 260     while ( i < delta_cnt )
 261     {
 262       runcnt = FT_GET_BYTE();
 263       cnt    = runcnt & GX_DT_DELTA_RUN_COUNT_MASK;
 264 
 265       if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
 266       {
 267         /* `runcnt' zeroes get added */
 268         for ( j = 0; j <= cnt && i < delta_cnt; j++ )
 269           deltas[i++] = 0;
 270       }
 271       else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
 272       {
 273         /* `runcnt' shorts from the stack */
 274         for ( j = 0; j <= cnt && i < delta_cnt; j++ )
 275           deltas[i++] = FT_GET_SHORT();
 276       }
 277       else
 278       {
 279         /* `runcnt' signed bytes from the stack */
 280         for ( j = 0; j <= cnt && i < delta_cnt; j++ )
 281           deltas[i++] = FT_GET_CHAR();
 282       }
 283 
 284       if ( j <= cnt )
 285       {
 286         /* bad format */
 287         FT_FREE( deltas );
 288         return NULL;
 289       }
 290     }
 291 
 292     return deltas;
 293   }
 294 
 295 
 296   /*************************************************************************/
 297   /*                                                                       */
 298   /* <Function>                                                            */
 299   /*    ft_var_load_avar                                                   */
 300   /*                                                                       */
 301   /* <Description>                                                         */
 302   /*    Parse the `avar' table if present.  It need not be, so we return   */
 303   /*    nothing.                                                           */
 304   /*                                                                       */
 305   /* <InOut>                                                               */
 306   /*    face :: The font face.                                             */
 307   /*                                                                       */
 308   static void
 309   ft_var_load_avar( TT_Face  face )
 310   {
 311     FT_Stream       stream = FT_FACE_STREAM( face );
 312     FT_Memory       memory = stream->memory;
 313     GX_Blend        blend  = face->blend;
 314     GX_AVarSegment  segment;
 315     FT_Error        error = FT_Err_Ok;
 316     FT_Long         version;
 317     FT_Long         axisCount;
 318     FT_Int          i, j;
 319     FT_ULong        table_len;
 320 
 321     FT_UNUSED( error );
 322 
 323 
 324     FT_TRACE2(( "AVAR " ));
 325 
 326     blend->avar_loaded = TRUE;
 327     error = face->goto_table( face, TTAG_avar, stream, &table_len );
 328     if ( error )
 329     {
 330       FT_TRACE2(( "is missing\n" ));
 331       return;
 332     }
 333 
 334     if ( FT_FRAME_ENTER( table_len ) )
 335       return;
 336 
 337     version   = FT_GET_LONG();
 338     axisCount = FT_GET_LONG();
 339 
 340     if ( version != 0x00010000L )
 341     {
 342       FT_TRACE2(( "bad table version\n" ));
 343       goto Exit;
 344     }
 345 
 346     FT_TRACE2(( "loaded\n" ));
 347 
 348     if ( axisCount != (FT_Long)blend->mmvar->num_axis )
 349     {
 350       FT_TRACE2(( "ft_var_load_avar: number of axes in `avar' and `fvar'\n"
 351                   "                  table are different\n" ));
 352       goto Exit;
 353     }
 354 
 355     if ( FT_NEW_ARRAY( blend->avar_segment, axisCount ) )
 356       goto Exit;
 357 
 358     segment = &blend->avar_segment[0];
 359     for ( i = 0; i < axisCount; i++, segment++ )
 360     {
 361       FT_TRACE5(( "  axis %d:\n", i ));
 362 
 363       segment->pairCount = FT_GET_USHORT();
 364       if ( (FT_ULong)segment->pairCount * 4 > table_len                ||
 365            FT_NEW_ARRAY( segment->correspondence, segment->pairCount ) )
 366       {
 367         /* Failure.  Free everything we have done so far.  We must do */
 368         /* it right now since loading the `avar' table is optional.   */
 369 
 370         for ( j = i - 1; j >= 0; j-- )
 371           FT_FREE( blend->avar_segment[j].correspondence );
 372 
 373         FT_FREE( blend->avar_segment );
 374         blend->avar_segment = NULL;
 375         goto Exit;
 376       }
 377 
 378       for ( j = 0; j < segment->pairCount; j++ )
 379       {
 380         /* convert to Fixed */
 381         segment->correspondence[j].fromCoord = FT_GET_SHORT() * 4;
 382         segment->correspondence[j].toCoord   = FT_GET_SHORT() * 4;
 383 
 384         FT_TRACE5(( "    mapping %.5f to %.5f\n",
 385                     segment->correspondence[j].fromCoord / 65536.0,
 386                     segment->correspondence[j].toCoord / 65536.0 ));
 387       }
 388 
 389       FT_TRACE5(( "\n" ));
 390     }
 391 
 392   Exit:
 393     FT_FRAME_EXIT();
 394   }
 395 
 396 
 397   /* some macros we need */
 398 #define FT_FIXED_ONE  ( (FT_Fixed)0x10000 )
 399 
 400 #define FT_fdot14ToFixed( x )                \
 401         ( (FT_Fixed)( (FT_ULong)(x) << 2 ) )
 402 #define FT_intToFixed( i )                    \
 403         ( (FT_Fixed)( (FT_ULong)(i) << 16 ) )
 404 #define FT_fixedToInt( x )                                   \
 405         ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
 406 
 407 
 408   static FT_Error
 409   ft_var_load_item_variation_store( TT_Face          face,
 410                                     FT_ULong         offset,
 411                                     GX_ItemVarStore  itemStore )
 412   {
 413     FT_Stream  stream = FT_FACE_STREAM( face );
 414     FT_Memory  memory = stream->memory;
 415 
 416     FT_Error   error;
 417     FT_UShort  format;
 418     FT_ULong   region_offset;
 419     FT_UInt    i, j, k;
 420     FT_UInt    shortDeltaCount;
 421 
 422     GX_Blend        blend = face->blend;
 423     GX_ItemVarData  varData;
 424 
 425     FT_ULong*  dataOffsetArray = NULL;
 426 
 427 
 428     if ( FT_STREAM_SEEK( offset ) ||
 429          FT_READ_USHORT( format ) )
 430       goto Exit;
 431 
 432     if ( format != 1 )
 433     {
 434       FT_TRACE2(( "ft_var_load_item_variation_store: bad store format %d\n",
 435                   format ));
 436       error = FT_THROW( Invalid_Table );
 437       goto Exit;
 438     }
 439 
 440     /* read top level fields */
 441     if ( FT_READ_ULONG( region_offset )         ||
 442          FT_READ_USHORT( itemStore->dataCount ) )
 443       goto Exit;
 444 
 445     /* we need at least one entry in `itemStore->varData' */
 446     if ( !itemStore->dataCount )
 447     {
 448       FT_TRACE2(( "ft_var_load_item_variation_store: missing varData\n" ));
 449       error = FT_THROW( Invalid_Table );
 450       goto Exit;
 451     }
 452 
 453     /* make temporary copy of item variation data offsets; */
 454     /* we will parse region list first, then come back     */
 455     if ( FT_NEW_ARRAY( dataOffsetArray, itemStore->dataCount ) )
 456       goto Exit;
 457 
 458     for ( i = 0; i < itemStore->dataCount; i++ )
 459     {
 460       if ( FT_READ_ULONG( dataOffsetArray[i] ) )
 461         goto Exit;
 462     }
 463 
 464     /* parse array of region records (region list) */
 465     if ( FT_STREAM_SEEK( offset + region_offset ) )
 466       goto Exit;
 467 
 468     if ( FT_READ_USHORT( itemStore->axisCount )   ||
 469          FT_READ_USHORT( itemStore->regionCount ) )
 470       goto Exit;
 471 
 472     if ( itemStore->axisCount != (FT_Long)blend->mmvar->num_axis )
 473     {
 474       FT_TRACE2(( "ft_var_load_item_variation_store:"
 475                   " number of axes in item variation store\n"
 476                   "                                 "
 477                   " and `fvar' table are different\n" ));
 478       error = FT_THROW( Invalid_Table );
 479       goto Exit;
 480     }
 481 
 482     if ( FT_NEW_ARRAY( itemStore->varRegionList, itemStore->regionCount ) )
 483       goto Exit;
 484 
 485     for ( i = 0; i < itemStore->regionCount; i++ )
 486     {
 487       GX_AxisCoords  axisCoords;
 488 
 489 
 490       if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList,
 491                          itemStore->axisCount ) )
 492         goto Exit;
 493 
 494       axisCoords = itemStore->varRegionList[i].axisList;
 495 
 496       for ( j = 0; j < itemStore->axisCount; j++ )
 497       {
 498         FT_Short  start, peak, end;
 499 
 500 
 501         if ( FT_READ_SHORT( start ) ||
 502              FT_READ_SHORT( peak )  ||
 503              FT_READ_SHORT( end )   )
 504           goto Exit;
 505 
 506         axisCoords[j].startCoord = FT_fdot14ToFixed( start );
 507         axisCoords[j].peakCoord  = FT_fdot14ToFixed( peak );
 508         axisCoords[j].endCoord   = FT_fdot14ToFixed( end );
 509       }
 510     }
 511 
 512     /* end of region list parse */
 513 
 514     /* use dataOffsetArray now to parse varData items */
 515     if ( FT_NEW_ARRAY( itemStore->varData, itemStore->dataCount ) )
 516       goto Exit;
 517 
 518     for ( i = 0; i < itemStore->dataCount; i++ )
 519     {
 520       varData = &itemStore->varData[i];
 521 
 522       if ( FT_STREAM_SEEK( offset + dataOffsetArray[i] ) )
 523         goto Exit;
 524 
 525       if ( FT_READ_USHORT( varData->itemCount )      ||
 526            FT_READ_USHORT( shortDeltaCount )         ||
 527            FT_READ_USHORT( varData->regionIdxCount ) )
 528         goto Exit;
 529 
 530       /* check some data consistency */
 531       if ( shortDeltaCount > varData->regionIdxCount )
 532       {
 533         FT_TRACE2(( "bad short count %d or region count %d\n",
 534                     shortDeltaCount,
 535                     varData->regionIdxCount ));
 536         error = FT_THROW( Invalid_Table );
 537         goto Exit;
 538       }
 539 
 540       if ( varData->regionIdxCount > itemStore->regionCount )
 541       {
 542         FT_TRACE2(( "inconsistent regionCount %d in varData[%d]\n",
 543                     varData->regionIdxCount,
 544                     i ));
 545         error = FT_THROW( Invalid_Table );
 546         goto Exit;
 547       }
 548 
 549       /* parse region indices */
 550       if ( FT_NEW_ARRAY( varData->regionIndices,
 551                          varData->regionIdxCount ) )
 552         goto Exit;
 553 
 554       for ( j = 0; j < varData->regionIdxCount; j++ )
 555       {
 556         if ( FT_READ_USHORT( varData->regionIndices[j] ) )
 557           goto Exit;
 558 
 559         if ( varData->regionIndices[j] >= itemStore->regionCount )
 560         {
 561           FT_TRACE2(( "bad region index %d\n",
 562                       varData->regionIndices[j] ));
 563           error = FT_THROW( Invalid_Table );
 564           goto Exit;
 565         }
 566       }
 567 
 568       /* Parse delta set.                                                */
 569       /*                                                                 */
 570       /* On input, deltas are (shortDeltaCount + regionIdxCount) bytes   */
 571       /* each; on output, deltas are expanded to `regionIdxCount' shorts */
 572       /* each.                                                           */
 573       if ( FT_NEW_ARRAY( varData->deltaSet,
 574                          varData->regionIdxCount * varData->itemCount ) )
 575         goto Exit;
 576 
 577       /* the delta set is stored as a 2-dimensional array of shorts; */
 578       /* sign-extend signed bytes to signed shorts                   */
 579       for ( j = 0; j < varData->itemCount * varData->regionIdxCount; )
 580       {
 581         for ( k = 0; k < shortDeltaCount; k++, j++ )
 582         {
 583           /* read the short deltas */
 584           FT_Short  delta;
 585 
 586 
 587           if ( FT_READ_SHORT( delta ) )
 588             goto Exit;
 589 
 590           varData->deltaSet[j] = delta;
 591         }
 592 
 593         for ( ; k < varData->regionIdxCount; k++, j++ )
 594         {
 595           /* read the (signed) byte deltas */
 596           FT_Char  delta;
 597 
 598 
 599           if ( FT_READ_CHAR( delta ) )
 600             goto Exit;
 601 
 602           varData->deltaSet[j] = delta;
 603         }
 604       }
 605     }
 606 
 607   Exit:
 608     FT_FREE( dataOffsetArray );
 609 
 610     return error;
 611   }
 612 
 613 
 614   static FT_Error
 615   ft_var_load_delta_set_index_mapping( TT_Face            face,
 616                                        FT_ULong           offset,
 617                                        GX_DeltaSetIdxMap  map,
 618                                        GX_ItemVarStore    itemStore )
 619   {
 620     FT_Stream  stream = FT_FACE_STREAM( face );
 621     FT_Memory  memory = stream->memory;
 622 
 623     FT_Error   error;
 624 
 625     FT_UShort  format;
 626     FT_UInt    entrySize;
 627     FT_UInt    innerBitCount;
 628     FT_UInt    innerIndexMask;
 629     FT_UInt    i, j;
 630 
 631 
 632     if ( FT_STREAM_SEEK( offset )        ||
 633          FT_READ_USHORT( format )        ||
 634          FT_READ_USHORT( map->mapCount ) )
 635       goto Exit;
 636 
 637     if ( format & 0xFFC0 )
 638     {
 639       FT_TRACE2(( "bad map format %d\n", format ));
 640       error = FT_THROW( Invalid_Table );
 641       goto Exit;
 642     }
 643 
 644     /* bytes per entry: 1, 2, 3, or 4 */
 645     entrySize      = ( ( format & 0x0030 ) >> 4 ) + 1;
 646     innerBitCount  = ( format & 0x000F ) + 1;
 647     innerIndexMask = ( 1 << innerBitCount ) - 1;
 648 
 649     if ( FT_NEW_ARRAY( map->innerIndex, map->mapCount ) )
 650       goto Exit;
 651 
 652     if ( FT_NEW_ARRAY( map->outerIndex, map->mapCount ) )
 653       goto Exit;
 654 
 655     for ( i = 0; i < map->mapCount; i++ )
 656     {
 657       FT_UInt  mapData = 0;
 658       FT_UInt  outerIndex, innerIndex;
 659 
 660 
 661       /* read map data one unsigned byte at a time, big endian */
 662       for ( j = 0; j < entrySize; j++ )
 663       {
 664         FT_Byte  data;
 665 
 666 
 667         if ( FT_READ_BYTE( data ) )
 668           goto Exit;
 669 
 670         mapData = ( mapData << 8 ) | data;
 671       }
 672 
 673       outerIndex = mapData >> innerBitCount;
 674 
 675       if ( outerIndex >= itemStore->dataCount )
 676       {
 677         FT_TRACE2(( "outerIndex[%d] == %d out of range\n",
 678                     i,
 679                     outerIndex ));
 680         error = FT_THROW( Invalid_Table );
 681         goto Exit;
 682       }
 683 
 684       map->outerIndex[i] = outerIndex;
 685 
 686       innerIndex = mapData & innerIndexMask;
 687 
 688       if ( innerIndex >= itemStore->varData[outerIndex].itemCount )
 689       {
 690         FT_TRACE2(( "innerIndex[%d] == %d out of range\n",
 691                     i,
 692                     innerIndex ));
 693         error = FT_THROW( Invalid_Table );
 694           goto Exit;
 695       }
 696 
 697       map->innerIndex[i] = innerIndex;
 698     }
 699 
 700   Exit:
 701     return error;
 702   }
 703 
 704 
 705   /*************************************************************************/
 706   /*                                                                       */
 707   /* <Function>                                                            */
 708   /*    ft_var_load_hvvar                                                  */
 709   /*                                                                       */
 710   /* <Description>                                                         */
 711   /*    If `vertical' is zero, parse the `HVAR' table and set              */
 712   /*    `blend->hvar_loaded' to TRUE.  On success, `blend->hvar_checked'   */
 713   /*    is set to TRUE.                                                    */
 714   /*                                                                       */
 715   /*    If `vertical' is not zero, parse the `VVAR' table and set          */
 716   /*    `blend->vvar_loaded' to TRUE.  On success, `blend->vvar_checked'   */
 717   /*    is set to TRUE.                                                    */
 718   /*                                                                       */
 719   /*    Some memory may remain allocated on error; it is always freed in   */
 720   /*    `tt_done_blend', however.                                          */
 721   /*                                                                       */
 722   /* <InOut>                                                               */
 723   /*    face :: The font face.                                             */
 724   /*                                                                       */
 725   /* <Return>                                                              */
 726   /*    FreeType error code.  0 means success.                             */
 727   /*                                                                       */
 728   static FT_Error
 729   ft_var_load_hvvar( TT_Face  face,
 730                      FT_Bool  vertical )
 731   {
 732     FT_Stream  stream = FT_FACE_STREAM( face );
 733     FT_Memory  memory = stream->memory;
 734 
 735     GX_Blend  blend = face->blend;
 736 
 737     GX_HVVarTable  table;
 738 
 739     FT_Error   error;
 740     FT_UShort  majorVersion;
 741     FT_ULong   table_len;
 742     FT_ULong   table_offset;
 743     FT_ULong   store_offset;
 744     FT_ULong   widthMap_offset;
 745 
 746 
 747     if ( vertical )
 748     {
 749       blend->vvar_loaded = TRUE;
 750 
 751       FT_TRACE2(( "VVAR " ));
 752 
 753       error = face->goto_table( face, TTAG_VVAR, stream, &table_len );
 754     }
 755     else
 756     {
 757       blend->hvar_loaded = TRUE;
 758 
 759       FT_TRACE2(( "HVAR " ));
 760 
 761       error = face->goto_table( face, TTAG_HVAR, stream, &table_len );
 762     }
 763 
 764     if ( error )
 765     {
 766       FT_TRACE2(( "is missing\n" ));
 767       goto Exit;
 768     }
 769 
 770     table_offset = FT_STREAM_POS();
 771 
 772     /* skip minor version */
 773     if ( FT_READ_USHORT( majorVersion ) ||
 774          FT_STREAM_SKIP( 2 )            )
 775       goto Exit;
 776 
 777     if ( majorVersion != 1 )
 778     {
 779       FT_TRACE2(( "bad table version %d\n", majorVersion ));
 780       error = FT_THROW( Invalid_Table );
 781       goto Exit;
 782     }
 783 
 784     if ( FT_READ_ULONG( store_offset )    ||
 785          FT_READ_ULONG( widthMap_offset ) )
 786       goto Exit;
 787 
 788     if ( vertical )
 789     {
 790       if ( FT_NEW( blend->vvar_table ) )
 791         goto Exit;
 792       table = blend->vvar_table;
 793     }
 794     else
 795     {
 796       if ( FT_NEW( blend->hvar_table ) )
 797         goto Exit;
 798       table = blend->hvar_table;
 799     }
 800 
 801     error = ft_var_load_item_variation_store(
 802               face,
 803               table_offset + store_offset,
 804               &table->itemStore );
 805     if ( error )
 806       goto Exit;
 807 
 808     if ( widthMap_offset )
 809     {
 810       error = ft_var_load_delta_set_index_mapping(
 811                 face,
 812                 table_offset + widthMap_offset,
 813                 &table->widthMap,
 814                 &table->itemStore );
 815       if ( error )
 816         goto Exit;
 817     }
 818 
 819     FT_TRACE2(( "loaded\n" ));
 820     error = FT_Err_Ok;
 821 
 822   Exit:
 823     if ( !error )
 824     {
 825       if ( vertical )
 826       {
 827         blend->vvar_checked = TRUE;
 828 
 829         /* FreeType doesn't provide functions to quickly retrieve    */
 830         /* TSB, BSB, or VORG values; we thus don't have to implement */
 831         /* support for those three item variation stores.            */
 832 
 833         face->variation_support |= TT_FACE_FLAG_VAR_VADVANCE;
 834       }
 835       else
 836       {
 837         blend->hvar_checked = TRUE;
 838 
 839         /* FreeType doesn't provide functions to quickly retrieve */
 840         /* LSB or RSB values; we thus don't have to implement     */
 841         /* support for those two item variation stores.           */
 842 
 843         face->variation_support |= TT_FACE_FLAG_VAR_HADVANCE;
 844       }
 845     }
 846 
 847     return error;
 848   }
 849 
 850 
 851   static FT_Int
 852   ft_var_get_item_delta( TT_Face          face,
 853                          GX_ItemVarStore  itemStore,
 854                          FT_UInt          outerIndex,
 855                          FT_UInt          innerIndex )
 856   {
 857     GX_ItemVarData  varData;
 858     FT_Short*       deltaSet;
 859 
 860     FT_UInt   master, j;
 861     FT_Fixed  netAdjustment = 0;     /* accumulated adjustment */
 862     FT_Fixed  scaledDelta;
 863     FT_Fixed  delta;
 864 
 865 
 866     /* See pseudo code from `Font Variations Overview' */
 867     /* in the OpenType specification.                  */
 868 
 869     varData  = &itemStore->varData[outerIndex];
 870     deltaSet = &varData->deltaSet[varData->regionIdxCount * innerIndex];
 871 
 872     /* outer loop steps through master designs to be blended */
 873     for ( master = 0; master < varData->regionIdxCount; master++ )
 874     {
 875       FT_Fixed  scalar      = FT_FIXED_ONE;
 876       FT_UInt   regionIndex = varData->regionIndices[master];
 877 
 878       GX_AxisCoords  axis = itemStore->varRegionList[regionIndex].axisList;
 879 
 880 
 881       /* inner loop steps through axes in this region */
 882       for ( j = 0; j < itemStore->axisCount; j++, axis++ )
 883       {
 884         FT_Fixed  axisScalar;
 885 
 886 
 887         /* compute the scalar contribution of this axis; */
 888         /* ignore invalid ranges                         */
 889         if ( axis->startCoord > axis->peakCoord ||
 890              axis->peakCoord > axis->endCoord   )
 891           axisScalar = FT_FIXED_ONE;
 892 
 893         else if ( axis->startCoord < 0 &&
 894                   axis->endCoord > 0   &&
 895                   axis->peakCoord != 0 )
 896           axisScalar = FT_FIXED_ONE;
 897 
 898         /* peak of 0 means ignore this axis */
 899         else if ( axis->peakCoord == 0 )
 900           axisScalar = FT_FIXED_ONE;
 901 
 902         /* ignore this region if coords are out of range */
 903         else if ( face->blend->normalizedcoords[j] < axis->startCoord ||
 904                   face->blend->normalizedcoords[j] > axis->endCoord   )
 905           axisScalar = 0;
 906 
 907         /* calculate a proportional factor */
 908         else
 909         {
 910           if ( face->blend->normalizedcoords[j] == axis->peakCoord )
 911             axisScalar = FT_FIXED_ONE;
 912           else if ( face->blend->normalizedcoords[j] < axis->peakCoord )
 913             axisScalar =
 914               FT_DivFix( face->blend->normalizedcoords[j] - axis->startCoord,
 915                          axis->peakCoord - axis->startCoord );
 916           else
 917             axisScalar =
 918               FT_DivFix( axis->endCoord - face->blend->normalizedcoords[j],
 919                          axis->endCoord - axis->peakCoord );
 920         }
 921 
 922         /* take product of all the axis scalars */
 923         scalar = FT_MulFix( scalar, axisScalar );
 924 
 925       } /* per-axis loop */
 926 
 927       /* get the scaled delta for this region */
 928       delta       = FT_intToFixed( deltaSet[master] );
 929       scaledDelta = FT_MulFix( scalar, delta );
 930 
 931       /* accumulate the adjustments from each region */
 932       netAdjustment = netAdjustment + scaledDelta;
 933 
 934     } /* per-region loop */
 935 
 936     return FT_fixedToInt( netAdjustment );
 937   }
 938 
 939 
 940   /*************************************************************************/
 941   /*                                                                       */
 942   /* <Function>                                                            */
 943   /*    tt_hvadvance_adjust                                                */
 944   /*                                                                       */
 945   /* <Description>                                                         */
 946   /*    Apply `HVAR' advance width or `VVAR' advance height adjustment of  */
 947   /*    a given glyph.                                                     */
 948   /*                                                                       */
 949   /* <Input>                                                               */
 950   /*    gindex   :: The glyph index.                                       */
 951   /*                                                                       */
 952   /*    vertical :: If set, handle `VVAR' table.                           */
 953   /*                                                                       */
 954   /* <InOut>                                                               */
 955   /*    face     :: The font face.                                         */
 956   /*                                                                       */
 957   /*    adelta   :: Points to width or height value that gets modified.    */
 958   /*                                                                       */
 959   static FT_Error
 960   tt_hvadvance_adjust( TT_Face  face,
 961                        FT_UInt  gindex,
 962                        FT_Int  *avalue,
 963                        FT_Bool  vertical )
 964   {
 965     FT_Error  error = FT_Err_Ok;
 966     FT_UInt   innerIndex, outerIndex;
 967     FT_Int    delta;
 968 
 969     GX_HVVarTable  table;
 970 
 971 
 972     if ( !face->doblend || !face->blend )
 973       goto Exit;
 974 
 975     if ( vertical )
 976     {
 977       if ( !face->blend->vvar_loaded )
 978       {
 979         /* initialize vvar table */
 980         face->blend->vvar_error = ft_var_load_hvvar( face, 1 );
 981       }
 982 
 983       if ( !face->blend->vvar_checked )
 984       {
 985         error = face->blend->vvar_error;
 986         goto Exit;
 987       }
 988 
 989       table = face->blend->vvar_table;
 990     }
 991     else
 992     {
 993       if ( !face->blend->hvar_loaded )
 994       {
 995         /* initialize hvar table */
 996         face->blend->hvar_error = ft_var_load_hvvar( face, 0 );
 997       }
 998 
 999       if ( !face->blend->hvar_checked )
1000       {
1001         error = face->blend->hvar_error;
1002         goto Exit;
1003       }
1004 
1005       table = face->blend->hvar_table;
1006     }
1007 
1008     /* advance width or height adjustments are always present in an */
1009     /* `HVAR' or `VVAR' table; no need to test for this capability  */
1010 
1011     if ( table->widthMap.innerIndex )
1012     {
1013       FT_UInt  idx = gindex;
1014 
1015 
1016       if ( idx >= table->widthMap.mapCount )
1017         idx = table->widthMap.mapCount - 1;
1018 
1019       /* trust that HVAR parser has checked indices */
1020       outerIndex = table->widthMap.outerIndex[idx];
1021       innerIndex = table->widthMap.innerIndex[idx];
1022     }
1023     else
1024     {
1025       GX_ItemVarData  varData;
1026 
1027 
1028       /* no widthMap data */
1029       outerIndex = 0;
1030       innerIndex = gindex;
1031 
1032       varData = &table->itemStore.varData[outerIndex];
1033       if ( gindex >= varData->itemCount )
1034       {
1035         FT_TRACE2(( "gindex %d out of range\n", gindex ));
1036         error = FT_THROW( Invalid_Argument );
1037         goto Exit;
1038       }
1039     }
1040 
1041     delta = ft_var_get_item_delta( face,
1042                                    &table->itemStore,
1043                                    outerIndex,
1044                                    innerIndex );
1045 
1046     FT_TRACE5(( "%s value %d adjusted by %d unit%s (%s)\n",
1047                 vertical ? "vertical height" : "horizontal width",
1048                 *avalue,
1049                 delta,
1050                 delta == 1 ? "" : "s",
1051                 vertical ? "VVAR" : "HVAR" ));
1052 
1053     *avalue += delta;
1054 
1055   Exit:
1056     return error;
1057   }
1058 
1059 
1060   FT_LOCAL_DEF( FT_Error )
1061   tt_hadvance_adjust( TT_Face  face,
1062                       FT_UInt  gindex,
1063                       FT_Int  *avalue )
1064   {
1065     return tt_hvadvance_adjust( face, gindex, avalue, 0 );
1066   }
1067 
1068 
1069   FT_LOCAL_DEF( FT_Error )
1070   tt_vadvance_adjust( TT_Face  face,
1071                       FT_UInt  gindex,
1072                       FT_Int  *avalue )
1073   {
1074     return tt_hvadvance_adjust( face, gindex, avalue, 1 );
1075   }
1076 
1077 
1078 #define GX_VALUE_SIZE  8
1079 
1080   /* all values are FT_Short or FT_UShort entities; */
1081   /* we treat them consistently as FT_Short         */
1082 #define GX_VALUE_CASE( tag, dflt )      \
1083           case MVAR_TAG_ ## tag :       \
1084             p = (FT_Short*)&face->dflt; \
1085             break
1086 
1087 #define GX_GASP_CASE( idx )                                       \
1088           case MVAR_TAG_GASP_ ## idx :                            \
1089             if ( idx < face->gasp.numRanges - 1 )                 \
1090               p = (FT_Short*)&face->gasp.gaspRanges[idx].maxPPEM; \
1091             else                                                  \
1092               p = NULL;                                           \
1093             break
1094 
1095 
1096   static FT_Short*
1097   ft_var_get_value_pointer( TT_Face   face,
1098                             FT_ULong  mvar_tag )
1099   {
1100     FT_Short*  p;
1101 
1102 
1103     switch ( mvar_tag )
1104     {
1105       GX_GASP_CASE( 0 );
1106       GX_GASP_CASE( 1 );
1107       GX_GASP_CASE( 2 );
1108       GX_GASP_CASE( 3 );
1109       GX_GASP_CASE( 4 );
1110       GX_GASP_CASE( 5 );
1111       GX_GASP_CASE( 6 );
1112       GX_GASP_CASE( 7 );
1113       GX_GASP_CASE( 8 );
1114       GX_GASP_CASE( 9 );
1115 
1116       GX_VALUE_CASE( CPHT, os2.sCapHeight );
1117       GX_VALUE_CASE( HASC, os2.sTypoAscender );
1118       GX_VALUE_CASE( HCLA, os2.usWinAscent );
1119       GX_VALUE_CASE( HCLD, os2.usWinDescent );
1120       GX_VALUE_CASE( HCOF, horizontal.caret_Offset );
1121       GX_VALUE_CASE( HCRN, horizontal.caret_Slope_Run );
1122       GX_VALUE_CASE( HCRS, horizontal.caret_Slope_Rise );
1123       GX_VALUE_CASE( HDSC, os2.sTypoDescender );
1124       GX_VALUE_CASE( HLGP, os2.sTypoLineGap );
1125       GX_VALUE_CASE( SBXO, os2.ySubscriptXOffset);
1126       GX_VALUE_CASE( SBXS, os2.ySubscriptXSize );
1127       GX_VALUE_CASE( SBYO, os2.ySubscriptYOffset );
1128       GX_VALUE_CASE( SBYS, os2.ySubscriptYSize );
1129       GX_VALUE_CASE( SPXO, os2.ySuperscriptXOffset );
1130       GX_VALUE_CASE( SPXS, os2.ySuperscriptXSize );
1131       GX_VALUE_CASE( SPYO, os2.ySuperscriptYOffset );
1132       GX_VALUE_CASE( SPYS, os2.ySuperscriptYSize );
1133       GX_VALUE_CASE( STRO, os2.yStrikeoutPosition );
1134       GX_VALUE_CASE( STRS, os2.yStrikeoutSize );
1135       GX_VALUE_CASE( UNDO, postscript.underlinePosition );
1136       GX_VALUE_CASE( UNDS, postscript.underlineThickness );
1137       GX_VALUE_CASE( VASC, vertical.Ascender );
1138       GX_VALUE_CASE( VCOF, vertical.caret_Offset );
1139       GX_VALUE_CASE( VCRN, vertical.caret_Slope_Run );
1140       GX_VALUE_CASE( VCRS, vertical.caret_Slope_Rise );
1141       GX_VALUE_CASE( VDSC, vertical.Descender );
1142       GX_VALUE_CASE( VLGP, vertical.Line_Gap );
1143       GX_VALUE_CASE( XHGT, os2.sxHeight );
1144 
1145     default:
1146       /* ignore unknown tag */
1147       p = NULL;
1148     }
1149 
1150     return p;
1151   }
1152 
1153 
1154   /*************************************************************************/
1155   /*                                                                       */
1156   /* <Function>                                                            */
1157   /*    ft_var_load_mvar                                                   */
1158   /*                                                                       */
1159   /* <Description>                                                         */
1160   /*    Parse the `MVAR' table.                                            */
1161   /*                                                                       */
1162   /*    Some memory may remain allocated on error; it is always freed in   */
1163   /*    `tt_done_blend', however.                                          */
1164   /*                                                                       */
1165   /* <InOut>                                                               */
1166   /*    face :: The font face.                                             */
1167   /*                                                                       */
1168   static void
1169   ft_var_load_mvar( TT_Face  face )
1170   {
1171     FT_Stream  stream = FT_FACE_STREAM( face );
1172     FT_Memory  memory = stream->memory;
1173 
1174     GX_Blend         blend = face->blend;
1175     GX_ItemVarStore  itemStore;
1176     GX_Value         value, limit;
1177 
1178     FT_Error   error;
1179     FT_UShort  majorVersion;
1180     FT_ULong   table_len;
1181     FT_ULong   table_offset;
1182     FT_UShort  store_offset;
1183     FT_ULong   records_offset;
1184 
1185 
1186     FT_TRACE2(( "MVAR " ));
1187 
1188     error = face->goto_table( face, TTAG_MVAR, stream, &table_len );
1189     if ( error )
1190     {
1191       FT_TRACE2(( "is missing\n" ));
1192       return;
1193     }
1194 
1195     table_offset = FT_STREAM_POS();
1196 
1197     /* skip minor version */
1198     if ( FT_READ_USHORT( majorVersion ) ||
1199          FT_STREAM_SKIP( 2 )            )
1200       return;
1201 
1202     if ( majorVersion != 1 )
1203     {
1204       FT_TRACE2(( "bad table version %d\n", majorVersion ));
1205       return;
1206     }
1207 
1208     if ( FT_NEW( blend->mvar_table ) )
1209       return;
1210 
1211     /* skip reserved entry and value record size */
1212     if ( FT_STREAM_SKIP( 4 )                             ||
1213          FT_READ_USHORT( blend->mvar_table->valueCount ) ||
1214          FT_READ_USHORT( store_offset )                  )
1215       return;
1216 
1217     records_offset = FT_STREAM_POS();
1218 
1219     error = ft_var_load_item_variation_store(
1220               face,
1221               table_offset + store_offset,
1222               &blend->mvar_table->itemStore );
1223     if ( error )
1224       return;
1225 
1226     if ( FT_NEW_ARRAY( blend->mvar_table->values,
1227                        blend->mvar_table->valueCount ) )
1228       return;
1229 
1230     if ( FT_STREAM_SEEK( records_offset )                                ||
1231          FT_FRAME_ENTER( blend->mvar_table->valueCount * GX_VALUE_SIZE ) )
1232       return;
1233 
1234     value     = blend->mvar_table->values;
1235     limit     = value + blend->mvar_table->valueCount;
1236     itemStore = &blend->mvar_table->itemStore;
1237 
1238     for ( ; value < limit; value++ )
1239     {
1240       value->tag        = FT_GET_ULONG();
1241       value->outerIndex = FT_GET_USHORT();
1242       value->innerIndex = FT_GET_USHORT();
1243 
1244       if ( value->outerIndex >= itemStore->dataCount                  ||
1245            value->innerIndex >= itemStore->varData[value->outerIndex]
1246                                                   .itemCount          )
1247       {
1248         error = FT_THROW( Invalid_Table );
1249         break;
1250       }
1251     }
1252 
1253     FT_FRAME_EXIT();
1254 
1255     if ( error )
1256       return;
1257 
1258     FT_TRACE2(( "loaded\n" ));
1259 
1260     value = blend->mvar_table->values;
1261     limit = value + blend->mvar_table->valueCount;
1262 
1263     /* save original values of the data MVAR is going to modify */
1264     for ( ; value < limit; value++ )
1265     {
1266       FT_Short*  p = ft_var_get_value_pointer( face, value->tag );
1267 
1268 
1269       if ( p )
1270         value->unmodified = *p;
1271 #ifdef FT_DEBUG_LEVEL_TRACE
1272       else
1273         FT_TRACE1(( "ft_var_load_mvar: Ignoring unknown tag `%c%c%c%c'\n",
1274                     (FT_Char)( value->tag >> 24 ),
1275                     (FT_Char)( value->tag >> 16 ),
1276                     (FT_Char)( value->tag >> 8 ),
1277                     (FT_Char)( value->tag ) ));
1278 #endif
1279     }
1280 
1281     face->variation_support |= TT_FACE_FLAG_VAR_MVAR;
1282   }
1283 
1284 
1285   static FT_Error
1286   tt_size_reset_iterator( FT_ListNode  node,
1287                           void*        user )
1288   {
1289     TT_Size  size = (TT_Size)node->data;
1290 
1291     FT_UNUSED( user );
1292 
1293 
1294     tt_size_reset( size, 1 );
1295 
1296     return FT_Err_Ok;
1297   }
1298 
1299 
1300   /*************************************************************************/
1301   /*                                                                       */
1302   /* <Function>                                                            */
1303   /*    tt_apply_mvar                                                      */
1304   /*                                                                       */
1305   /* <Description>                                                         */
1306   /*    Apply `MVAR' table adjustments.                                    */
1307   /*                                                                       */
1308   /* <InOut>                                                               */
1309   /*    face :: The font face.                                             */
1310   /*                                                                       */
1311   FT_LOCAL_DEF( void )
1312   tt_apply_mvar( TT_Face  face )
1313   {
1314     GX_Blend  blend = face->blend;
1315     GX_Value  value, limit;
1316 
1317 
1318     if ( !( face->variation_support & TT_FACE_FLAG_VAR_MVAR ) )
1319       return;
1320 
1321     value = blend->mvar_table->values;
1322     limit = value + blend->mvar_table->valueCount;
1323 
1324     for ( ; value < limit; value++ )
1325     {
1326       FT_Short*  p = ft_var_get_value_pointer( face, value->tag );
1327       FT_Int     delta;
1328 
1329 
1330       delta = ft_var_get_item_delta( face,
1331                                      &blend->mvar_table->itemStore,
1332                                      value->outerIndex,
1333                                      value->innerIndex );
1334 
1335       if ( p )
1336       {
1337         FT_TRACE5(( "value %c%c%c%c (%d unit%s) adjusted by %d unit%s (MVAR)\n",
1338                     (FT_Char)( value->tag >> 24 ),
1339                     (FT_Char)( value->tag >> 16 ),
1340                     (FT_Char)( value->tag >> 8 ),
1341                     (FT_Char)( value->tag ),
1342                     value->unmodified,
1343                     value->unmodified == 1 ? "" : "s",
1344                     delta,
1345                     delta == 1 ? "" : "s" ));
1346 
1347         /* since we handle both signed and unsigned values as FT_Short, */
1348         /* ensure proper overflow arithmetic                            */
1349         *p = (FT_Short)( value->unmodified + (FT_Short)delta );
1350       }
1351     }
1352 
1353     /* adjust all derived values */
1354     {
1355       FT_Face  root = &face->root;
1356 
1357 
1358       if ( face->os2.version != 0xFFFFU )
1359       {
1360         if ( face->os2.sTypoAscender || face->os2.sTypoDescender )
1361         {
1362           root->ascender  = face->os2.sTypoAscender;
1363           root->descender = face->os2.sTypoDescender;
1364 
1365           root->height = root->ascender - root->descender +
1366                          face->os2.sTypoLineGap;
1367         }
1368         else
1369         {
1370           root->ascender  =  (FT_Short)face->os2.usWinAscent;
1371           root->descender = -(FT_Short)face->os2.usWinDescent;
1372 
1373           root->height = root->ascender - root->descender;
1374         }
1375       }
1376 
1377       root->underline_position  = face->postscript.underlinePosition -
1378                                   face->postscript.underlineThickness / 2;
1379       root->underline_thickness = face->postscript.underlineThickness;
1380 
1381       /* iterate over all FT_Size objects and call `tt_size_reset' */
1382       /* to propagate the metrics changes                          */
1383       FT_List_Iterate( &root->sizes_list,
1384                        tt_size_reset_iterator,
1385                        NULL );
1386     }
1387   }
1388 
1389 
1390   typedef struct  GX_GVar_Head_
1391   {
1392     FT_Long    version;
1393     FT_UShort  axisCount;
1394     FT_UShort  globalCoordCount;
1395     FT_ULong   offsetToCoord;
1396     FT_UShort  glyphCount;
1397     FT_UShort  flags;
1398     FT_ULong   offsetToData;
1399 
1400   } GX_GVar_Head;
1401 
1402 
1403   /*************************************************************************/
1404   /*                                                                       */
1405   /* <Function>                                                            */
1406   /*    ft_var_load_gvar                                                   */
1407   /*                                                                       */
1408   /* <Description>                                                         */
1409   /*    Parse the `gvar' table if present.  If `fvar' is there, `gvar' had */
1410   /*    better be there too.                                               */
1411   /*                                                                       */
1412   /* <InOut>                                                               */
1413   /*    face :: The font face.                                             */
1414   /*                                                                       */
1415   /* <Return>                                                              */
1416   /*    FreeType error code.  0 means success.                             */
1417   /*                                                                       */
1418   static FT_Error
1419   ft_var_load_gvar( TT_Face  face )
1420   {
1421     FT_Stream     stream = FT_FACE_STREAM( face );
1422     FT_Memory     memory = stream->memory;
1423     GX_Blend      blend  = face->blend;
1424     FT_Error      error;
1425     FT_UInt       i, j;
1426     FT_ULong      table_len;
1427     FT_ULong      gvar_start;
1428     FT_ULong      offsetToData;
1429     GX_GVar_Head  gvar_head;
1430 
1431     static const FT_Frame_Field  gvar_fields[] =
1432     {
1433 
1434 #undef  FT_STRUCTURE
1435 #define FT_STRUCTURE  GX_GVar_Head
1436 
1437       FT_FRAME_START( 20 ),
1438         FT_FRAME_LONG  ( version ),
1439         FT_FRAME_USHORT( axisCount ),
1440         FT_FRAME_USHORT( globalCoordCount ),
1441         FT_FRAME_ULONG ( offsetToCoord ),
1442         FT_FRAME_USHORT( glyphCount ),
1443         FT_FRAME_USHORT( flags ),
1444         FT_FRAME_ULONG ( offsetToData ),
1445       FT_FRAME_END
1446     };
1447 
1448 
1449     FT_TRACE2(( "GVAR " ));
1450 
1451     if ( FT_SET_ERROR( face->goto_table( face,
1452                                          TTAG_gvar,
1453                                          stream,
1454                                          &table_len ) ) )
1455     {
1456       FT_TRACE2(( "is missing\n" ));
1457       goto Exit;
1458     }
1459 
1460     gvar_start = FT_STREAM_POS( );
1461     if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) )
1462       goto Exit;
1463 
1464     if ( gvar_head.version != 0x00010000L )
1465     {
1466       FT_TRACE1(( "bad table version\n" ));
1467       error = FT_THROW( Invalid_Table );
1468       goto Exit;
1469     }
1470 
1471     if ( gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
1472     {
1473       FT_TRACE1(( "ft_var_load_gvar: number of axes in `gvar' and `cvar'\n"
1474                   "                  table are different\n" ));
1475       error = FT_THROW( Invalid_Table );
1476       goto Exit;
1477     }
1478 
1479     /* rough sanity check, ignoring offsets */
1480     if ( (FT_ULong)gvar_head.globalCoordCount * gvar_head.axisCount >
1481            table_len / 2 )
1482     {
1483       FT_TRACE1(( "ft_var_load_gvar:"
1484                   " invalid number of global coordinates\n" ));
1485       error = FT_THROW( Invalid_Table );
1486       goto Exit;
1487     }
1488 
1489     /* rough sanity check: offsets can be either 2 or 4 bytes */
1490     if ( (FT_ULong)gvar_head.glyphCount *
1491            ( ( gvar_head.flags & 1 ) ? 4 : 2 ) > table_len )
1492     {
1493       FT_TRACE1(( "ft_var_load_gvar: invalid number of glyphs\n" ));
1494       error = FT_THROW( Invalid_Table );
1495       goto Exit;
1496     }
1497 
1498     FT_TRACE2(( "loaded\n" ));
1499 
1500     blend->gvar_size   = table_len;
1501     blend->tuplecount  = gvar_head.globalCoordCount;
1502     blend->gv_glyphcnt = gvar_head.glyphCount;
1503     offsetToData       = gvar_start + gvar_head.offsetToData;
1504 
1505     FT_TRACE5(( "gvar: there %s %d shared coordinate%s:\n",
1506                 blend->tuplecount == 1 ? "is" : "are",
1507                 blend->tuplecount,
1508                 blend->tuplecount == 1 ? "" : "s" ));
1509 
1510     if ( FT_NEW_ARRAY( blend->glyphoffsets, blend->gv_glyphcnt + 1 ) )
1511       goto Exit;
1512 
1513     if ( gvar_head.flags & 1 )
1514     {
1515       /* long offsets (one more offset than glyphs, to mark size of last) */
1516       if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 4L ) )
1517         goto Exit;
1518 
1519       for ( i = 0; i <= blend->gv_glyphcnt; i++ )
1520         blend->glyphoffsets[i] = offsetToData + FT_GET_ULONG();
1521 
1522       FT_FRAME_EXIT();
1523     }
1524     else
1525     {
1526       /* short offsets (one more offset than glyphs, to mark size of last) */
1527       if ( FT_FRAME_ENTER( ( blend->gv_glyphcnt + 1 ) * 2L ) )
1528         goto Exit;
1529 
1530       for ( i = 0; i <= blend->gv_glyphcnt; i++ )
1531         blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
1532                                                /* XXX: Undocumented: `*2'! */
1533 
1534       FT_FRAME_EXIT();
1535     }
1536 
1537     if ( blend->tuplecount != 0 )
1538     {
1539       if ( FT_NEW_ARRAY( blend->tuplecoords,
1540                          gvar_head.axisCount * blend->tuplecount ) )
1541         goto Exit;
1542 
1543       if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord )         ||
1544            FT_FRAME_ENTER( blend->tuplecount * gvar_head.axisCount * 2L ) )
1545         goto Exit;
1546 
1547       for ( i = 0; i < blend->tuplecount; i++ )
1548       {
1549         FT_TRACE5(( "  [ " ));
1550         for ( j = 0; j < (FT_UInt)gvar_head.axisCount; j++ )
1551         {
1552           blend->tuplecoords[i * gvar_head.axisCount + j] =
1553             FT_GET_SHORT() * 4;                 /* convert to FT_Fixed */
1554           FT_TRACE5(( "%.5f ",
1555             blend->tuplecoords[i * gvar_head.axisCount + j] / 65536.0 ));
1556         }
1557         FT_TRACE5(( "]\n" ));
1558       }
1559 
1560       FT_TRACE5(( "\n" ));
1561 
1562       FT_FRAME_EXIT();
1563     }
1564 
1565   Exit:
1566     return error;
1567   }
1568 
1569 
1570   /*************************************************************************/
1571   /*                                                                       */
1572   /* <Function>                                                            */
1573   /*    ft_var_apply_tuple                                                 */
1574   /*                                                                       */
1575   /* <Description>                                                         */
1576   /*    Figure out whether a given tuple (design) applies to the current   */
1577   /*    blend, and if so, what is the scaling factor.                      */
1578   /*                                                                       */
1579   /* <Input>                                                               */
1580   /*    blend           :: The current blend of the font.                  */
1581   /*                                                                       */
1582   /*    tupleIndex      :: A flag saying whether this is an intermediate   */
1583   /*                       tuple or not.                                   */
1584   /*                                                                       */
1585   /*    tuple_coords    :: The coordinates of the tuple in normalized axis */
1586   /*                       units.                                          */
1587   /*                                                                       */
1588   /*    im_start_coords :: The initial coordinates where this tuple starts */
1589   /*                       to apply (for intermediate coordinates).        */
1590   /*                                                                       */
1591   /*    im_end_coords   :: The final coordinates after which this tuple no */
1592   /*                       longer applies (for intermediate coordinates).  */
1593   /*                                                                       */
1594   /* <Return>                                                              */
1595   /*    An FT_Fixed value containing the scaling factor.                   */
1596   /*                                                                       */
1597   static FT_Fixed
1598   ft_var_apply_tuple( GX_Blend   blend,
1599                       FT_UShort  tupleIndex,
1600                       FT_Fixed*  tuple_coords,
1601                       FT_Fixed*  im_start_coords,
1602                       FT_Fixed*  im_end_coords )
1603   {
1604     FT_UInt   i;
1605     FT_Fixed  apply = 0x10000L;
1606 
1607 
1608     for ( i = 0; i < blend->num_axis; i++ )
1609     {
1610       FT_TRACE6(( "    axis coordinate %d (%.5f):\n",
1611                   i, blend->normalizedcoords[i] / 65536.0 ));
1612       if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
1613         FT_TRACE6(( "      intermediate coordinates %d (%.5f, %.5f):\n",
1614                     i,
1615                     im_start_coords[i] / 65536.0,
1616                     im_end_coords[i] / 65536.0 ));
1617 
1618       /* It's not clear why (for intermediate tuples) we don't need     */
1619       /* to check against start/end -- the documentation says we don't. */
1620       /* Similarly, it's unclear why we don't need to scale along the   */
1621       /* axis.                                                          */
1622 
1623       if ( tuple_coords[i] == 0 )
1624       {
1625         FT_TRACE6(( "      tuple coordinate is zero, ignored\n", i ));
1626         continue;
1627       }
1628 
1629       if ( blend->normalizedcoords[i] == 0 )
1630       {
1631         FT_TRACE6(( "      axis coordinate is zero, stop\n" ));
1632         apply = 0;
1633         break;
1634       }
1635 
1636       if ( blend->normalizedcoords[i] == tuple_coords[i] )
1637       {
1638         FT_TRACE6(( "      tuple coordinate value %.5f fits perfectly\n",
1639                     tuple_coords[i] / 65536.0 ));
1640         /* `apply' does not change */
1641         continue;
1642       }
1643 
1644       if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
1645       {
1646         /* not an intermediate tuple */
1647 
1648         if ( blend->normalizedcoords[i] < FT_MIN( 0, tuple_coords[i] ) ||
1649              blend->normalizedcoords[i] > FT_MAX( 0, tuple_coords[i] ) )
1650         {
1651           FT_TRACE6(( "      tuple coordinate value %.5f is exceeded, stop\n",
1652                       tuple_coords[i] / 65536.0 ));
1653           apply = 0;
1654           break;
1655         }
1656 
1657         FT_TRACE6(( "      tuple coordinate value %.5f fits\n",
1658                     tuple_coords[i] / 65536.0 ));
1659         apply = FT_MulDiv( apply,
1660                            blend->normalizedcoords[i],
1661                            tuple_coords[i] );
1662       }
1663       else
1664       {
1665         /* intermediate tuple */
1666 
1667         if ( blend->normalizedcoords[i] < im_start_coords[i] ||
1668              blend->normalizedcoords[i] > im_end_coords[i]   )
1669         {
1670           FT_TRACE6(( "      intermediate tuple range [%.5f;%.5f] is exceeded,"
1671                       " stop\n",
1672                       im_start_coords[i] / 65536.0,
1673                       im_end_coords[i] / 65536.0 ));
1674           apply = 0;
1675           break;
1676         }
1677 
1678         else if ( blend->normalizedcoords[i] < tuple_coords[i] )
1679         {
1680           FT_TRACE6(( "      intermediate tuple range [%.5f;%.5f] fits\n",
1681                       im_start_coords[i] / 65536.0,
1682                       im_end_coords[i] / 65536.0 ));
1683           apply = FT_MulDiv( apply,
1684                              blend->normalizedcoords[i] - im_start_coords[i],
1685                              tuple_coords[i] - im_start_coords[i] );
1686         }
1687 
1688         else
1689         {
1690           FT_TRACE6(( "      intermediate tuple range [%.5f;%.5f] fits\n",
1691                       im_start_coords[i] / 65536.0,
1692                       im_end_coords[i] / 65536.0 ));
1693           apply = FT_MulDiv( apply,
1694                              im_end_coords[i] - blend->normalizedcoords[i],
1695                              im_end_coords[i] - tuple_coords[i] );
1696         }
1697       }
1698     }
1699 
1700     FT_TRACE6(( "    apply factor is %.5f\n", apply / 65536.0 ));
1701 
1702     return apply;
1703   }
1704 
1705 
1706   /* convert from design coordinates to normalized coordinates */
1707 
1708   static void
1709   ft_var_to_normalized( TT_Face    face,
1710                         FT_UInt    num_coords,
1711                         FT_Fixed*  coords,
1712                         FT_Fixed*  normalized )
1713   {
1714     GX_Blend        blend;
1715     FT_MM_Var*      mmvar;
1716     FT_UInt         i, j;
1717     FT_Var_Axis*    a;
1718     GX_AVarSegment  av;
1719 
1720 
1721     blend = face->blend;
1722     mmvar = blend->mmvar;
1723 
1724     if ( num_coords > mmvar->num_axis )
1725     {
1726       FT_TRACE2(( "ft_var_to_normalized:"
1727                   " only using first %d of %d coordinates\n",
1728                   mmvar->num_axis, num_coords ));
1729       num_coords = mmvar->num_axis;
1730     }
1731 
1732     /* Axis normalization is a two-stage process.  First we normalize */
1733     /* based on the [min,def,max] values for the axis to be [-1,0,1]. */
1734     /* Then, if there's an `avar' table, we renormalize this range.   */
1735 
1736     FT_TRACE5(( "%d design coordinate%s:\n",
1737                 num_coords,
1738                 num_coords == 1 ? "" : "s" ));
1739 
1740     a = mmvar->axis;
1741     for ( i = 0; i < num_coords; i++, a++ )
1742     {
1743       FT_Fixed  coord = coords[i];
1744 
1745 
1746       FT_TRACE5(( "  %.5f\n", coord / 65536.0 ));
1747       if ( coord > a->maximum || coord < a->minimum )
1748       {
1749         FT_TRACE1((
1750           "ft_var_to_normalized: design coordinate %.5f\n"
1751           "                      is out of range [%.5f;%.5f]; clamping\n",
1752           coord / 65536.0,
1753           a->minimum / 65536.0,
1754           a->maximum / 65536.0 ));
1755 
1756         if ( coord > a->maximum )
1757           coord = a->maximum;
1758         else
1759           coord = a->minimum;
1760       }
1761 
1762       if ( coord < a->def )
1763         normalized[i] = -FT_DivFix( coord - a->def,
1764                                     a->minimum - a->def );
1765       else if ( coord > a->def )
1766         normalized[i] = FT_DivFix( coord - a->def,
1767                                    a->maximum - a->def );
1768       else
1769         normalized[i] = 0;
1770     }
1771 
1772     FT_TRACE5(( "\n" ));
1773 
1774     for ( ; i < mmvar->num_axis; i++ )
1775       normalized[i] = 0;
1776 
1777     if ( blend->avar_segment )
1778     {
1779       FT_TRACE5(( "normalized design coordinates"
1780                   " before applying `avar' data:\n" ));
1781 
1782       av = blend->avar_segment;
1783       for ( i = 0; i < mmvar->num_axis; i++, av++ )
1784       {
1785         for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
1786         {
1787           if ( normalized[i] < av->correspondence[j].fromCoord )
1788           {
1789             FT_TRACE5(( "  %.5f\n", normalized[i] / 65536.0 ));
1790 
1791             normalized[i] =
1792               FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord,
1793                          av->correspondence[j].toCoord -
1794                            av->correspondence[j - 1].toCoord,
1795                          av->correspondence[j].fromCoord -
1796                            av->correspondence[j - 1].fromCoord ) +
1797               av->correspondence[j - 1].toCoord;
1798             break;
1799           }
1800         }
1801       }
1802     }
1803   }
1804 
1805 
1806   /* convert from normalized coordinates to design coordinates */
1807 
1808   static void
1809   ft_var_to_design( TT_Face    face,
1810                     FT_UInt    num_coords,
1811                     FT_Fixed*  coords,
1812                     FT_Fixed*  design )
1813   {
1814     GX_Blend      blend;
1815     FT_MM_Var*    mmvar;
1816     FT_Var_Axis*  a;
1817 
1818     FT_UInt  i, j, nc;
1819 
1820 
1821     blend = face->blend;
1822 
1823     nc = num_coords;
1824     if ( num_coords > blend->num_axis )
1825     {
1826       FT_TRACE2(( "ft_var_to_design:"
1827                   " only using first %d of %d coordinates\n",
1828                   blend->num_axis, num_coords ));
1829       nc = blend->num_axis;
1830     }
1831 
1832     for ( i = 0; i < nc; i++ )
1833       design[i] = coords[i];
1834 
1835     for ( ; i < num_coords; i++ )
1836       design[i] = 0;
1837 
1838     if ( blend->avar_segment )
1839     {
1840       GX_AVarSegment  av = blend->avar_segment;
1841 
1842 
1843       FT_TRACE5(( "design coordinates"
1844                   " after removing `avar' distortion:\n" ));
1845 
1846       for ( i = 0; i < nc; i++, av++ )
1847       {
1848         for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
1849         {
1850           if ( design[i] < av->correspondence[j].toCoord )
1851           {
1852             design[i] =
1853               FT_MulDiv( design[i] - av->correspondence[j - 1].toCoord,
1854                          av->correspondence[j].fromCoord -
1855                            av->correspondence[j - 1].fromCoord,
1856                          av->correspondence[j].toCoord -
1857                            av->correspondence[j - 1].toCoord ) +
1858               av->correspondence[j - 1].fromCoord;
1859 
1860             FT_TRACE5(( "  %.5f\n", design[i] / 65536.0 ));
1861             break;
1862           }
1863         }
1864       }
1865     }
1866 
1867     mmvar = blend->mmvar;
1868     a     = mmvar->axis;
1869 
1870     for ( i = 0; i < nc; i++, a++ )
1871     {
1872       if ( design[i] < 0 )
1873         design[i] = a->def + FT_MulFix( design[i],
1874                                         a->def - a->minimum );
1875       else if ( design[i] > 0 )
1876         design[i] = a->def + FT_MulFix( design[i],
1877                                         a->maximum - a->def );
1878       else
1879         design[i] = a->def;
1880     }
1881   }
1882 
1883 
1884   /*************************************************************************/
1885   /*************************************************************************/
1886   /*****                                                               *****/
1887   /*****               MULTIPLE MASTERS SERVICE FUNCTIONS              *****/
1888   /*****                                                               *****/
1889   /*************************************************************************/
1890   /*************************************************************************/
1891 
1892 
1893   typedef struct  GX_FVar_Head_
1894   {
1895     FT_Long    version;
1896     FT_UShort  offsetToData;
1897     FT_UShort  axisCount;
1898     FT_UShort  axisSize;
1899     FT_UShort  instanceCount;
1900     FT_UShort  instanceSize;
1901 
1902   } GX_FVar_Head;
1903 
1904 
1905   typedef struct  fvar_axis_
1906   {
1907     FT_ULong   axisTag;
1908     FT_Fixed   minValue;
1909     FT_Fixed   defaultValue;
1910     FT_Fixed   maxValue;
1911     FT_UShort  flags;
1912     FT_UShort  nameID;
1913 
1914   } GX_FVar_Axis;
1915 
1916 
1917   /*************************************************************************/
1918   /*                                                                       */
1919   /* <Function>                                                            */
1920   /*    TT_Get_MM_Var                                                      */
1921   /*                                                                       */
1922   /* <Description>                                                         */
1923   /*    Check that the font's `fvar' table is valid, parse it, and return  */
1924   /*    those data.  It also loads (and parses) the `MVAR' table, if       */
1925   /*    possible.                                                          */
1926   /*                                                                       */
1927   /* <InOut>                                                               */
1928   /*    face   :: The font face.                                           */
1929   /*              TT_Get_MM_Var initializes the blend structure.           */
1930   /*                                                                       */
1931   /* <Output>                                                              */
1932   /*    master :: The `fvar' data (must be freed by caller).  Can be NULL, */
1933   /*              which makes this function simply load MM support.        */
1934   /*                                                                       */
1935   /* <Return>                                                              */
1936   /*    FreeType error code.  0 means success.                             */
1937   /*                                                                       */
1938   FT_LOCAL_DEF( FT_Error )
1939   TT_Get_MM_Var( TT_Face      face,
1940                  FT_MM_Var*  *master )
1941   {
1942     FT_Stream            stream     = face->root.stream;
1943     FT_Memory            memory     = face->root.memory;
1944     FT_ULong             table_len;
1945     FT_Error             error      = FT_Err_Ok;
1946     FT_ULong             fvar_start = 0;
1947     FT_UInt              i, j;
1948     FT_MM_Var*           mmvar = NULL;
1949     FT_Fixed*            next_coords;
1950     FT_Fixed*            nsc;
1951     FT_String*           next_name;
1952     FT_Var_Axis*         a;
1953     FT_Fixed*            c;
1954     FT_Var_Named_Style*  ns;
1955     GX_FVar_Head         fvar_head;
1956     FT_Bool              usePsName  = 0;
1957     FT_UInt              num_instances;
1958     FT_UInt              num_axes;
1959     FT_UShort*           axis_flags;
1960 
1961     FT_Offset  mmvar_size;
1962     FT_Offset  axis_flags_size;
1963     FT_Offset  axis_size;
1964     FT_Offset  namedstyle_size;
1965     FT_Offset  next_coords_size;
1966     FT_Offset  next_name_size;
1967 
1968     FT_Bool  need_init;
1969 
1970     static const FT_Frame_Field  fvar_fields[] =
1971     {
1972 
1973 #undef  FT_STRUCTURE
1974 #define FT_STRUCTURE  GX_FVar_Head
1975 
1976       FT_FRAME_START( 16 ),
1977         FT_FRAME_LONG      ( version ),
1978         FT_FRAME_USHORT    ( offsetToData ),
1979         FT_FRAME_SKIP_SHORT,
1980         FT_FRAME_USHORT    ( axisCount ),
1981         FT_FRAME_USHORT    ( axisSize ),
1982         FT_FRAME_USHORT    ( instanceCount ),
1983         FT_FRAME_USHORT    ( instanceSize ),
1984       FT_FRAME_END
1985     };
1986 
1987     static const FT_Frame_Field  fvaraxis_fields[] =
1988     {
1989 
1990 #undef  FT_STRUCTURE
1991 #define FT_STRUCTURE  GX_FVar_Axis
1992 
1993       FT_FRAME_START( 20 ),
1994         FT_FRAME_ULONG ( axisTag ),
1995         FT_FRAME_LONG  ( minValue ),
1996         FT_FRAME_LONG  ( defaultValue ),
1997         FT_FRAME_LONG  ( maxValue ),
1998         FT_FRAME_USHORT( flags ),
1999         FT_FRAME_USHORT( nameID ),
2000       FT_FRAME_END
2001     };
2002 
2003 
2004     /* read the font data and set up the internal representation */
2005     /* if not already done                                       */
2006 
2007     need_init = !face->blend;
2008 
2009     if ( need_init )
2010     {
2011       FT_TRACE2(( "FVAR " ));
2012 
2013       /* both `fvar' and `gvar' must be present */
2014       if ( FT_SET_ERROR( face->goto_table( face, TTAG_gvar,
2015                                            stream, &table_len ) ) )
2016       {
2017         /* CFF2 is an alternate to gvar here */
2018         if ( FT_SET_ERROR( face->goto_table( face, TTAG_CFF2,
2019                                              stream, &table_len ) ) )
2020         {
2021           FT_TRACE1(( "\n"
2022                       "TT_Get_MM_Var: `gvar' or `CFF2' table is missing\n" ));
2023           goto Exit;
2024         }
2025       }
2026 
2027       if ( FT_SET_ERROR( face->goto_table( face, TTAG_fvar,
2028                                            stream, &table_len ) ) )
2029       {
2030         FT_TRACE1(( "is missing\n" ));
2031         goto Exit;
2032       }
2033 
2034       fvar_start = FT_STREAM_POS( );
2035 
2036       /* the validity of the `fvar' header data was already checked */
2037       /* in function `sfnt_init_face'                               */
2038       if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
2039         goto Exit;
2040 
2041       usePsName = FT_BOOL( fvar_head.instanceSize ==
2042                            6 + 4 * fvar_head.axisCount );
2043 
2044       FT_TRACE2(( "loaded\n" ));
2045 
2046       FT_TRACE5(( "number of GX style axes: %d\n", fvar_head.axisCount ));
2047 
2048       if ( FT_NEW( face->blend ) )
2049         goto Exit;
2050 
2051       num_axes = fvar_head.axisCount;
2052     }
2053     else
2054       num_axes = face->blend->num_axis;
2055 
2056     /* `num_instances' holds the number of all named instances, */
2057     /* including the default instance which might be missing    */
2058     /* in fvar's table of named instances                       */
2059     num_instances = (FT_UInt)face->root.style_flags >> 16;
2060 
2061     /* prepare storage area for MM data; this cannot overflow   */
2062     /* 32-bit arithmetic because of the size limits used in the */
2063     /* `fvar' table validity check in `sfnt_init_face'          */
2064 
2065     /* the various `*_size' variables, which we also use as     */
2066     /* offsets into the `mmlen' array, must be multiples of the */
2067     /* pointer size (except the last one); without such an      */
2068     /* alignment there might be runtime errors due to           */
2069     /* misaligned addresses                                     */
2070 #undef  ALIGN_SIZE
2071 #define ALIGN_SIZE( n ) \
2072           ( ( (n) + sizeof (void*) - 1 ) & ~( sizeof (void*) - 1 ) )
2073 
2074     mmvar_size       = ALIGN_SIZE( sizeof ( FT_MM_Var ) );
2075     axis_flags_size  = ALIGN_SIZE( num_axes *
2076                                    sizeof ( FT_UShort ) );
2077     axis_size        = ALIGN_SIZE( num_axes *
2078                                    sizeof ( FT_Var_Axis ) );
2079     namedstyle_size  = ALIGN_SIZE( num_instances *
2080                                    sizeof ( FT_Var_Named_Style ) );
2081     next_coords_size = ALIGN_SIZE( num_instances *
2082                                    num_axes *
2083                                    sizeof ( FT_Fixed ) );
2084     next_name_size   = num_axes * 5;
2085 
2086     if ( need_init )
2087     {
2088       face->blend->mmvar_len = mmvar_size       +
2089                                axis_flags_size  +
2090                                axis_size        +
2091                                namedstyle_size  +
2092                                next_coords_size +
2093                                next_name_size;
2094 
2095       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
2096         goto Exit;
2097       face->blend->mmvar = mmvar;
2098 
2099       /* set up pointers and offsets into the `mmvar' array; */
2100       /* the data gets filled in later on                    */
2101 
2102       mmvar->num_axis =
2103         num_axes;
2104       mmvar->num_designs =
2105         ~0U;                   /* meaningless in this context; each glyph */
2106                                /* may have a different number of designs  */
2107                                /* (or tuples, as called by Apple)         */
2108       mmvar->num_namedstyles =
2109         num_instances;
2110 
2111       /* alas, no public field in `FT_Var_Axis' for axis flags */
2112       axis_flags =
2113         (FT_UShort*)( (char*)mmvar + mmvar_size );
2114       mmvar->axis =
2115         (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
2116       mmvar->namedstyle =
2117         (FT_Var_Named_Style*)( (char*)mmvar->axis + axis_size );
2118 
2119       next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
2120                                  namedstyle_size );
2121       for ( i = 0; i < num_instances; i++ )
2122       {
2123         mmvar->namedstyle[i].coords  = next_coords;
2124         next_coords                 += num_axes;
2125       }
2126 
2127       next_name = (FT_String*)( (char*)mmvar->namedstyle +
2128                                 namedstyle_size + next_coords_size );
2129       for ( i = 0; i < num_axes; i++ )
2130       {
2131         mmvar->axis[i].name  = next_name;
2132         next_name           += 5;
2133       }
2134 
2135       /* now fill in the data */
2136 
2137       if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
2138         goto Exit;
2139 
2140       a = mmvar->axis;
2141       for ( i = 0; i < num_axes; i++ )
2142       {
2143         GX_FVar_Axis  axis_rec;
2144 
2145 
2146         if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
2147           goto Exit;
2148         a->tag     = axis_rec.axisTag;
2149         a->minimum = axis_rec.minValue;
2150         a->def     = axis_rec.defaultValue;
2151         a->maximum = axis_rec.maxValue;
2152         a->strid   = axis_rec.nameID;
2153 
2154         a->name[0] = (FT_String)(   a->tag >> 24 );
2155         a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
2156         a->name[2] = (FT_String)( ( a->tag >>  8 ) & 0xFF );
2157         a->name[3] = (FT_String)( ( a->tag       ) & 0xFF );
2158         a->name[4] = '\0';
2159 
2160         *axis_flags = axis_rec.flags;
2161 
2162         if ( a->minimum > a->def ||
2163              a->def > a->maximum )
2164         {
2165           FT_TRACE2(( "TT_Get_MM_Var:"
2166                       " invalid \"%s\" axis record; disabling\n",
2167                       a->name ));
2168 
2169           a->minimum = a->def;
2170           a->maximum = a->def;
2171         }
2172 
2173         FT_TRACE5(( "  \"%s\":"
2174                     " minimum=%.5f, default=%.5f, maximum=%.5f,"
2175                     " flags=0x%04X\n",
2176                     a->name,
2177                     a->minimum / 65536.0,
2178                     a->def / 65536.0,
2179                     a->maximum / 65536.0,
2180                     *axis_flags ));
2181 
2182         a++;
2183         axis_flags++;
2184       }
2185 
2186       FT_TRACE5(( "\n" ));
2187 
2188       /* named instance coordinates are stored as design coordinates; */
2189       /* we have to convert them to normalized coordinates also       */
2190       if ( FT_NEW_ARRAY( face->blend->normalized_stylecoords,
2191                          num_axes * num_instances ) )
2192         goto Exit;
2193 
2194       if ( fvar_head.instanceCount && !face->blend->avar_loaded )
2195       {
2196         FT_ULong  offset = FT_STREAM_POS();
2197 
2198 
2199         ft_var_load_avar( face );
2200 
2201         if ( FT_STREAM_SEEK( offset ) )
2202           goto Exit;
2203       }
2204 
2205       ns  = mmvar->namedstyle;
2206       nsc = face->blend->normalized_stylecoords;
2207       for ( i = 0; i < fvar_head.instanceCount; i++, ns++ )
2208       {
2209         /* PostScript names add 2 bytes to the instance record size */
2210         if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) +
2211                              4L * num_axes ) )
2212           goto Exit;
2213 
2214         ns->strid       =    FT_GET_USHORT();
2215         (void) /* flags = */ FT_GET_USHORT();
2216 
2217         c = ns->coords;
2218         for ( j = 0; j < num_axes; j++, c++ )
2219           *c = FT_GET_LONG();
2220 
2221         /* valid psid values are 6, [256;32767], and 0xFFFF */
2222         if ( usePsName )
2223           ns->psid = FT_GET_USHORT();
2224         else
2225           ns->psid = 0xFFFF;
2226 
2227         ft_var_to_normalized( face, num_axes, ns->coords, nsc );
2228         nsc += num_axes;
2229 
2230         FT_FRAME_EXIT();
2231       }
2232 
2233       if ( num_instances != fvar_head.instanceCount )
2234       {
2235         SFNT_Service  sfnt = (SFNT_Service)face->sfnt;
2236 
2237         FT_Int   found, dummy1, dummy2;
2238         FT_UInt  strid = ~0U;
2239 
2240 
2241         /* the default instance is missing in array the   */
2242         /* of named instances; try to synthesize an entry */
2243         found = sfnt->get_name_id( face,
2244                                    TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY,
2245                                    &dummy1,
2246                                    &dummy2 );
2247         if ( found )
2248           strid = TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY;
2249         else
2250         {
2251           found = sfnt->get_name_id( face,
2252                                      TT_NAME_ID_FONT_SUBFAMILY,
2253                                      &dummy1,
2254                                      &dummy2 );
2255           if ( found )
2256             strid = TT_NAME_ID_FONT_SUBFAMILY;
2257         }
2258 
2259         if ( found )
2260         {
2261           found = sfnt->get_name_id( face,
2262                                      TT_NAME_ID_PS_NAME,
2263                                      &dummy1,
2264                                      &dummy2 );
2265           if ( found )
2266           {
2267             FT_TRACE5(( "TT_Get_MM_Var:"
2268                         " Adding default instance to named instances\n" ));
2269 
2270             ns = &mmvar->namedstyle[fvar_head.instanceCount];
2271 
2272             ns->strid = strid;
2273             ns->psid  = TT_NAME_ID_PS_NAME;
2274 
2275             a = mmvar->axis;
2276             c = ns->coords;
2277             for ( j = 0; j < num_axes; j++, a++, c++ )
2278               *c = a->def;
2279           }
2280         }
2281       }
2282 
2283       ft_var_load_mvar( face );
2284     }
2285 
2286     /* fill the output array if requested */
2287 
2288     if ( master )
2289     {
2290       FT_UInt  n;
2291 
2292 
2293       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
2294         goto Exit;
2295       FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
2296 
2297       axis_flags =
2298         (FT_UShort*)( (char*)mmvar + mmvar_size );
2299       mmvar->axis =
2300         (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
2301       mmvar->namedstyle =
2302         (FT_Var_Named_Style*)( (char*)mmvar->axis+ axis_size );
2303 
2304       next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
2305                                  namedstyle_size );
2306       for ( n = 0; n < mmvar->num_namedstyles; n++ )
2307       {
2308         mmvar->namedstyle[n].coords  = next_coords;
2309         next_coords                 += num_axes;
2310       }
2311 
2312       a         = mmvar->axis;
2313       next_name = (FT_String*)( (char*)mmvar->namedstyle +
2314                                 namedstyle_size + next_coords_size );
2315       for ( n = 0; n < num_axes; n++ )
2316       {
2317         a->name = next_name;
2318 
2319         /* standard PostScript names for some standard apple tags */
2320         if ( a->tag == TTAG_wght )
2321           a->name = (char*)"Weight";
2322         else if ( a->tag == TTAG_wdth )
2323           a->name = (char*)"Width";
2324         else if ( a->tag == TTAG_opsz )
2325           a->name = (char*)"OpticalSize";
2326         else if ( a->tag == TTAG_slnt )
2327           a->name = (char*)"Slant";
2328 
2329         next_name += 5;
2330         a++;
2331       }
2332 
2333       *master = mmvar;
2334     }
2335 
2336   Exit:
2337     return error;
2338   }
2339 
2340 
2341   static FT_Error
2342   tt_set_mm_blend( TT_Face    face,
2343                    FT_UInt    num_coords,
2344                    FT_Fixed*  coords,
2345                    FT_Bool    set_design_coords )
2346   {
2347     FT_Error    error = FT_Err_Ok;
2348     GX_Blend    blend;
2349     FT_MM_Var*  mmvar;
2350     FT_UInt     i;
2351 
2352     FT_Bool     all_design_coords = FALSE;
2353 
2354     FT_Memory   memory = face->root.memory;
2355 
2356     enum
2357     {
2358       mcvt_retain,
2359       mcvt_modify,
2360       mcvt_load
2361 
2362     } manageCvt;
2363 
2364 
2365     face->doblend = FALSE;
2366 
2367     if ( !face->blend )
2368     {
2369       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2370         goto Exit;
2371     }
2372 
2373     blend = face->blend;
2374     mmvar = blend->mmvar;
2375 
2376     if ( num_coords > mmvar->num_axis )
2377     {
2378       FT_TRACE2(( "TT_Set_MM_Blend:"
2379                   " only using first %d of %d coordinates\n",
2380                   mmvar->num_axis, num_coords ));
2381       num_coords = mmvar->num_axis;
2382     }
2383 
2384     FT_TRACE5(( "normalized design coordinates:\n" ));
2385 
2386     for ( i = 0; i < num_coords; i++ )
2387     {
2388       FT_TRACE5(( "  %.5f\n", coords[i] / 65536.0 ));
2389       if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
2390       {
2391         FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n"
2392                     "                 is out of range [-1;1]\n",
2393                     coords[i] / 65536.0 ));
2394         error = FT_THROW( Invalid_Argument );
2395         goto Exit;
2396       }
2397     }
2398 
2399     FT_TRACE5(( "\n" ));
2400 
2401     if ( !face->is_cff2 && !blend->glyphoffsets )
2402       if ( FT_SET_ERROR( ft_var_load_gvar( face ) ) )
2403         goto Exit;
2404 
2405     if ( !blend->coords )
2406     {
2407       if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
2408         goto Exit;
2409 
2410       /* the first time we have to compute all design coordinates */
2411       all_design_coords = TRUE;
2412     }
2413 
2414     if ( !blend->normalizedcoords )
2415     {
2416       if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) )
2417         goto Exit;
2418 
2419       manageCvt = mcvt_modify;
2420 
2421       /* If we have not set the blend coordinates before this, then the  */
2422       /* cvt table will still be what we read from the `cvt ' table and  */
2423       /* we don't need to reload it.  We may need to change it though... */
2424     }
2425     else
2426     {
2427       FT_Bool    have_diff = 0;
2428       FT_UInt    j;
2429       FT_Fixed*  c;
2430       FT_Fixed*  n;
2431 
2432 
2433       manageCvt = mcvt_retain;
2434 
2435       for ( i = 0; i < num_coords; i++ )
2436       {
2437         if ( blend->normalizedcoords[i] != coords[i] )
2438         {
2439           manageCvt = mcvt_load;
2440           have_diff = 1;
2441           break;
2442         }
2443       }
2444 
2445       if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2446       {
2447         FT_UInt  idx = (FT_UInt)face->root.face_index >> 16;
2448 
2449 
2450         c = blend->normalizedcoords + i;
2451         n = blend->normalized_stylecoords + idx * mmvar->num_axis + i;
2452         for ( j = i; j < mmvar->num_axis; j++, n++, c++ )
2453           if ( *c != *n )
2454             have_diff = 1;
2455       }
2456       else
2457       {
2458         c = blend->normalizedcoords + i;
2459         for ( j = i; j < mmvar->num_axis; j++, c++ )
2460           if ( *c != 0 )
2461             have_diff = 1;
2462       }
2463 
2464       /* return value -1 indicates `no change' */
2465       if ( !have_diff )
2466         return -1;
2467 
2468       for ( ; i < mmvar->num_axis; i++ )
2469       {
2470         if ( blend->normalizedcoords[i] != 0 )
2471         {
2472           manageCvt = mcvt_load;
2473           break;
2474         }
2475       }
2476 
2477       /* If we don't change the blend coords then we don't need to do  */
2478       /* anything to the cvt table.  It will be correct.  Otherwise we */
2479       /* no longer have the original cvt (it was modified when we set  */
2480       /* the blend last time), so we must reload and then modify it.   */
2481     }
2482 
2483     blend->num_axis = mmvar->num_axis;
2484     FT_MEM_COPY( blend->normalizedcoords,
2485                  coords,
2486                  num_coords * sizeof ( FT_Fixed ) );
2487 
2488     if ( set_design_coords )
2489       ft_var_to_design( face,
2490                         all_design_coords ? blend->num_axis : num_coords,
2491                         blend->normalizedcoords,
2492                         blend->coords );
2493 
2494     face->doblend = TRUE;
2495 
2496     if ( face->cvt )
2497     {
2498       switch ( manageCvt )
2499       {
2500       case mcvt_load:
2501         /* The cvt table has been loaded already; every time we change the */
2502         /* blend we may need to reload and remodify the cvt table.         */
2503         FT_FREE( face->cvt );
2504         face->cvt = NULL;
2505 
2506         error = tt_face_load_cvt( face, face->root.stream );
2507         break;
2508 
2509       case mcvt_modify:
2510         /* The original cvt table is in memory.  All we need to do is */
2511         /* apply the `cvar' table (if any).                           */
2512         error = tt_face_vary_cvt( face, face->root.stream );
2513         break;
2514 
2515       case mcvt_retain:
2516         /* The cvt table is correct for this set of coordinates. */
2517         break;
2518       }
2519     }
2520 
2521     /* enforce recomputation of the PostScript name; */
2522     FT_FREE( face->postscript_name );
2523     face->postscript_name = NULL;
2524 
2525   Exit:
2526     return error;
2527   }
2528 
2529 
2530   /*************************************************************************/
2531   /*                                                                       */
2532   /* <Function>                                                            */
2533   /*    TT_Set_MM_Blend                                                    */
2534   /*                                                                       */
2535   /* <Description>                                                         */
2536   /*    Set the blend (normalized) coordinates for this instance of the    */
2537   /*    font.  Check that the `gvar' table is reasonable and does some     */
2538   /*    initial preparation.                                               */
2539   /*                                                                       */
2540   /* <InOut>                                                               */
2541   /*    face       :: The font.                                            */
2542   /*                  Initialize the blend structure with `gvar' data.     */
2543   /*                                                                       */
2544   /* <Input>                                                               */
2545   /*    num_coords :: The number of available coordinates.  If it is       */
2546   /*                  larger than the number of axes, ignore the excess    */
2547   /*                  values.  If it is smaller than the number of axes,   */
2548   /*                  use the default value (0) for the remaining axes.    */
2549   /*                                                                       */
2550   /*    coords     :: An array of `num_coords', each between [-1,1].       */
2551   /*                                                                       */
2552   /* <Return>                                                              */
2553   /*    FreeType error code.  0 means success.                             */
2554   /*                                                                       */
2555   FT_LOCAL_DEF( FT_Error )
2556   TT_Set_MM_Blend( TT_Face    face,
2557                    FT_UInt    num_coords,
2558                    FT_Fixed*  coords )
2559   {
2560     FT_Error  error;
2561 
2562 
2563     error = tt_set_mm_blend( face, num_coords, coords, 1 );
2564     if ( error )
2565       return error;
2566 
2567     if ( num_coords )
2568       face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2569     else
2570       face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2571 
2572     return FT_Err_Ok;
2573   }
2574 
2575 
2576   /*************************************************************************/
2577   /*                                                                       */
2578   /* <Function>                                                            */
2579   /*    TT_Get_MM_Blend                                                    */
2580   /*                                                                       */
2581   /* <Description>                                                         */
2582   /*    Get the blend (normalized) coordinates for this instance of the    */
2583   /*    font.                                                              */
2584   /*                                                                       */
2585   /* <InOut>                                                               */
2586   /*    face       :: The font.                                            */
2587   /*                  Initialize the blend structure with `gvar' data.     */
2588   /*                                                                       */
2589   /* <Input>                                                               */
2590   /*    num_coords :: The number of available coordinates.  If it is       */
2591   /*                  larger than the number of axes, set the excess       */
2592   /*                  values to 0.                                         */
2593   /*                                                                       */
2594   /*    coords     :: An array of `num_coords', each between [-1,1].       */
2595   /*                                                                       */
2596   /* <Return>                                                              */
2597   /*    FreeType error code.  0 means success.                             */
2598   /*                                                                       */
2599   FT_LOCAL_DEF( FT_Error )
2600   TT_Get_MM_Blend( TT_Face    face,
2601                    FT_UInt    num_coords,
2602                    FT_Fixed*  coords )
2603   {
2604     FT_Error  error = FT_Err_Ok;
2605     GX_Blend  blend;
2606     FT_UInt   i, nc;
2607 
2608 
2609     if ( !face->blend )
2610     {
2611       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2612         return error;
2613     }
2614 
2615     blend = face->blend;
2616 
2617     if ( !blend->coords )
2618     {
2619       /* select default instance coordinates */
2620       /* if no instance is selected yet      */
2621       if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
2622         return error;
2623     }
2624 
2625     nc = num_coords;
2626     if ( num_coords > blend->num_axis )
2627     {
2628       FT_TRACE2(( "TT_Get_MM_Blend:"
2629                   " only using first %d of %d coordinates\n",
2630                   blend->num_axis, num_coords ));
2631       nc = blend->num_axis;
2632     }
2633 
2634     if ( face->doblend )
2635     {
2636       for ( i = 0; i < nc; i++ )
2637         coords[i] = blend->normalizedcoords[i];
2638     }
2639     else
2640     {
2641       for ( i = 0; i < nc; i++ )
2642         coords[i] = 0;
2643     }
2644 
2645     for ( ; i < num_coords; i++ )
2646       coords[i] = 0;
2647 
2648     return FT_Err_Ok;
2649   }
2650 
2651 
2652   /*************************************************************************/
2653   /*                                                                       */
2654   /* <Function>                                                            */
2655   /*    TT_Set_Var_Design                                                  */
2656   /*                                                                       */
2657   /* <Description>                                                         */
2658   /*    Set the coordinates for the instance, measured in the user         */
2659   /*    coordinate system.  Parse the `avar' table (if present) to convert */
2660   /*    from user to normalized coordinates.                               */
2661   /*                                                                       */
2662   /* <InOut>                                                               */
2663   /*    face       :: The font face.                                       */
2664   /*                  Initialize the blend struct with `gvar' data.        */
2665   /*                                                                       */
2666   /* <Input>                                                               */
2667   /*    num_coords :: The number of available coordinates.  If it is       */
2668   /*                  larger than the number of axes, ignore the excess    */
2669   /*                  values.  If it is smaller than the number of axes,   */
2670   /*                  use the default values for the remaining axes.       */
2671   /*                                                                       */
2672   /*    coords     :: A coordinate array with `num_coords' elements.       */
2673   /*                                                                       */
2674   /* <Return>                                                              */
2675   /*    FreeType error code.  0 means success.                             */
2676   /*                                                                       */
2677   FT_LOCAL_DEF( FT_Error )
2678   TT_Set_Var_Design( TT_Face    face,
2679                      FT_UInt    num_coords,
2680                      FT_Fixed*  coords )
2681   {
2682     FT_Error    error  = FT_Err_Ok;
2683     GX_Blend    blend;
2684     FT_MM_Var*  mmvar;
2685     FT_UInt     i;
2686     FT_Memory   memory = face->root.memory;
2687 
2688     FT_Fixed*  c;
2689     FT_Fixed*  n;
2690     FT_Fixed*  normalized = NULL;
2691 
2692     FT_Bool  have_diff = 0;
2693 
2694 
2695     if ( !face->blend )
2696     {
2697       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2698         goto Exit;
2699     }
2700 
2701     blend = face->blend;
2702     mmvar = blend->mmvar;
2703 
2704     if ( num_coords > mmvar->num_axis )
2705     {
2706       FT_TRACE2(( "TT_Set_Var_Design:"
2707                   " only using first %d of %d coordinates\n",
2708                   mmvar->num_axis, num_coords ));
2709       num_coords = mmvar->num_axis;
2710     }
2711 
2712     if ( !blend->coords )
2713     {
2714       if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
2715         goto Exit;
2716     }
2717 
2718     c = blend->coords;
2719     n = coords;
2720     for ( i = 0; i < num_coords; i++, n++, c++ )
2721     {
2722       if ( *c != *n )
2723       {
2724         *c        = *n;
2725         have_diff = 1;
2726       }
2727     }
2728 
2729     if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2730     {
2731       FT_UInt              instance_index;
2732       FT_Var_Named_Style*  named_style;
2733 
2734 
2735       instance_index = (FT_UInt)face->root.face_index >> 16;
2736       named_style    = mmvar->namedstyle + instance_index - 1;
2737 
2738       n = named_style->coords + num_coords;
2739       for ( ; i < mmvar->num_axis; i++, n++, c++ )
2740       {
2741         if ( *c != *n )
2742         {
2743           *c        = *n;
2744           have_diff = 1;
2745         }
2746       }
2747     }
2748     else
2749     {
2750       FT_Var_Axis*  a;
2751 
2752 
2753       a = mmvar->axis + num_coords;
2754       for ( ; i < mmvar->num_axis; i++, a++, c++ )
2755       {
2756         if ( *c != a->def )
2757         {
2758           *c        = a->def;
2759           have_diff = 1;
2760         }
2761       }
2762     }
2763 
2764     /* return value -1 indicates `no change' */
2765     if ( !have_diff )
2766       return -1;
2767 
2768     if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
2769       goto Exit;
2770 
2771     if ( !face->blend->avar_loaded )
2772       ft_var_load_avar( face );
2773 
2774     ft_var_to_normalized( face, num_coords, blend->coords, normalized );
2775 
2776     error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 );
2777     if ( error )
2778       goto Exit;
2779 
2780     if ( num_coords )
2781       face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2782     else
2783       face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2784 
2785   Exit:
2786     FT_FREE( normalized );
2787     return error;
2788   }
2789 
2790 
2791   /*************************************************************************/
2792   /*                                                                       */
2793   /* <Function>                                                            */
2794   /*    TT_Get_Var_Design                                                  */
2795   /*                                                                       */
2796   /* <Description>                                                         */
2797   /*    Get the design coordinates of the currently selected interpolated  */
2798   /*    font.                                                              */
2799   /*                                                                       */
2800   /* <Input>                                                               */
2801   /*    face       :: A handle to the source face.                         */
2802   /*                                                                       */
2803   /*    num_coords :: The number of design coordinates to retrieve.  If it */
2804   /*                  is larger than the number of axes, set the excess    */
2805   /*                  values to~0.                                         */
2806   /*                                                                       */
2807   /* <Output>                                                              */
2808   /*    coords     :: The design coordinates array.                        */
2809   /*                                                                       */
2810   /* <Return>                                                              */
2811   /*    FreeType error code.  0~means success.                             */
2812   /*                                                                       */
2813   FT_LOCAL_DEF( FT_Error )
2814   TT_Get_Var_Design( TT_Face    face,
2815                      FT_UInt    num_coords,
2816                      FT_Fixed*  coords )
2817   {
2818     FT_Error  error = FT_Err_Ok;
2819     GX_Blend  blend;
2820     FT_UInt   i, nc;
2821 
2822 
2823     if ( !face->blend )
2824     {
2825       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2826         return error;
2827     }
2828 
2829     blend = face->blend;
2830 
2831     if ( !blend->coords )
2832     {
2833       /* select default instance coordinates */
2834       /* if no instance is selected yet      */
2835       if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
2836         return error;
2837     }
2838 
2839     nc = num_coords;
2840     if ( num_coords > blend->num_axis )
2841     {
2842       FT_TRACE2(( "TT_Get_Var_Design:"
2843                   " only using first %d of %d coordinates\n",
2844                   blend->num_axis, num_coords ));
2845       nc = blend->num_axis;
2846     }
2847 
2848     if ( face->doblend )
2849     {
2850       for ( i = 0; i < nc; i++ )
2851         coords[i] = blend->coords[i];
2852     }
2853     else
2854     {
2855       for ( i = 0; i < nc; i++ )
2856         coords[i] = 0;
2857     }
2858 
2859     for ( ; i < num_coords; i++ )
2860       coords[i] = 0;
2861 
2862     return FT_Err_Ok;
2863   }
2864 
2865 
2866   /*************************************************************************/
2867   /*                                                                       */
2868   /* <Function>                                                            */
2869   /*    TT_Set_Named_Instance                                              */
2870   /*                                                                       */
2871   /* <Description>                                                         */
2872   /*    Set the given named instance, also resetting any further           */
2873   /*    variation.                                                         */
2874   /*                                                                       */
2875   /* <Input>                                                               */
2876   /*    face           :: A handle to the source face.                     */
2877   /*                                                                       */
2878   /*    instance_index :: The instance index, starting with value 1.       */
2879   /*                      Value 0 indicates to not use an instance.        */
2880   /*                                                                       */
2881   /* <Return>                                                              */
2882   /*    FreeType error code.  0~means success.                             */
2883   /*                                                                       */
2884   FT_LOCAL_DEF( FT_Error )
2885   TT_Set_Named_Instance( TT_Face  face,
2886                          FT_UInt  instance_index )
2887   {
2888     FT_Error    error = FT_ERR( Invalid_Argument );
2889     GX_Blend    blend;
2890     FT_MM_Var*  mmvar;
2891 
2892     FT_UInt  num_instances;
2893 
2894 
2895     if ( !face->blend )
2896     {
2897       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2898         goto Exit;
2899     }
2900 
2901     blend = face->blend;
2902     mmvar = blend->mmvar;
2903 
2904     num_instances = (FT_UInt)face->root.style_flags >> 16;
2905 
2906     /* `instance_index' starts with value 1, thus `>' */
2907     if ( instance_index > num_instances )
2908       goto Exit;
2909 
2910     if ( instance_index > 0 && mmvar->namedstyle )
2911     {
2912       FT_Memory     memory = face->root.memory;
2913       SFNT_Service  sfnt   = (SFNT_Service)face->sfnt;
2914 
2915       FT_Var_Named_Style*  named_style;
2916       FT_String*           style_name;
2917 
2918 
2919       named_style = mmvar->namedstyle + instance_index - 1;
2920 
2921       error = sfnt->get_name( face,
2922                               (FT_UShort)named_style->strid,
2923                               &style_name );
2924       if ( error )
2925         goto Exit;
2926 
2927       /* set (or replace) style name */
2928       FT_FREE( face->root.style_name );
2929       face->root.style_name = style_name;
2930 
2931       /* finally, select the named instance */
2932       error = TT_Set_Var_Design( face,
2933                                  mmvar->num_axis,
2934                                  named_style->coords );
2935       if ( error )
2936         goto Exit;
2937     }
2938     else
2939       error = TT_Set_Var_Design( face, 0, NULL );
2940 
2941     face->root.face_index  = ( instance_index << 16 )             |
2942                              ( face->root.face_index & 0xFFFFL );
2943     face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2944 
2945   Exit:
2946     return error;
2947   }
2948 
2949 
2950   /*************************************************************************/
2951   /*************************************************************************/
2952   /*****                                                               *****/
2953   /*****                     GX VAR PARSING ROUTINES                   *****/
2954   /*****                                                               *****/
2955   /*************************************************************************/
2956   /*************************************************************************/
2957 
2958 
2959   /*************************************************************************/
2960   /*                                                                       */
2961   /* <Function>                                                            */
2962   /*    tt_face_vary_cvt                                                   */
2963   /*                                                                       */
2964   /* <Description>                                                         */
2965   /*    Modify the loaded cvt table according to the `cvar' table and the  */
2966   /*    font's blend.                                                      */
2967   /*                                                                       */
2968   /* <InOut>                                                               */
2969   /*    face   :: A handle to the target face object.                      */
2970   /*                                                                       */
2971   /* <Input>                                                               */
2972   /*    stream :: A handle to the input stream.                            */
2973   /*                                                                       */
2974   /* <Return>                                                              */
2975   /*    FreeType error code.  0 means success.                             */
2976   /*                                                                       */
2977   /*    Most errors are ignored.  It is perfectly valid not to have a      */
2978   /*    `cvar' table even if there is a `gvar' and `fvar' table.           */
2979   /*                                                                       */
2980   FT_LOCAL_DEF( FT_Error )
2981   tt_face_vary_cvt( TT_Face    face,
2982                     FT_Stream  stream )
2983   {
2984     FT_Error    error;
2985     FT_Memory   memory = stream->memory;
2986     FT_ULong    table_start;
2987     FT_ULong    table_len;
2988     FT_UInt     tupleCount;
2989     FT_ULong    offsetToData;
2990     FT_ULong    here;
2991     FT_UInt     i, j;
2992     FT_Fixed*   tuple_coords    = NULL;
2993     FT_Fixed*   im_start_coords = NULL;
2994     FT_Fixed*   im_end_coords   = NULL;
2995     GX_Blend    blend           = face->blend;
2996     FT_UInt     point_count, spoint_count = 0;
2997     FT_UShort*  sharedpoints = NULL;
2998     FT_UShort*  localpoints  = NULL;
2999     FT_UShort*  points;
3000     FT_Short*   deltas;
3001 
3002 
3003     FT_TRACE2(( "CVAR " ));
3004 
3005     if ( !blend )
3006     {
3007       FT_TRACE2(( "\n"
3008                   "tt_face_vary_cvt: no blend specified\n" ));
3009       error = FT_Err_Ok;
3010       goto Exit;
3011     }
3012 
3013     if ( !face->cvt )
3014     {
3015       FT_TRACE2(( "\n"
3016                   "tt_face_vary_cvt: no `cvt ' table\n" ));
3017       error = FT_Err_Ok;
3018       goto Exit;
3019     }
3020 
3021     error = face->goto_table( face, TTAG_cvar, stream, &table_len );
3022     if ( error )
3023     {
3024       FT_TRACE2(( "is missing\n" ));
3025 
3026       error = FT_Err_Ok;
3027       goto Exit;
3028     }
3029 
3030     if ( FT_FRAME_ENTER( table_len ) )
3031     {
3032       error = FT_Err_Ok;
3033       goto Exit;
3034     }
3035 
3036     table_start = FT_Stream_FTell( stream );
3037     if ( FT_GET_LONG() != 0x00010000L )
3038     {
3039       FT_TRACE2(( "bad table version\n" ));
3040 
3041       error = FT_Err_Ok;
3042       goto FExit;
3043     }
3044 
3045     FT_TRACE2(( "loaded\n" ));
3046 
3047     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
3048          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3049          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
3050       goto FExit;
3051 
3052     tupleCount   = FT_GET_USHORT();
3053     offsetToData = FT_GET_USHORT();
3054 
3055     /* rough sanity test */
3056     if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
3057            table_len )
3058     {
3059       FT_TRACE2(( "tt_face_vary_cvt:"
3060                   " invalid CVT variation array header\n" ));
3061 
3062       error = FT_THROW( Invalid_Table );
3063       goto FExit;
3064     }
3065 
3066     offsetToData += table_start;
3067 
3068     if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3069     {
3070       here = FT_Stream_FTell( stream );
3071 
3072       FT_Stream_SeekSet( stream, offsetToData );
3073 
3074       sharedpoints = ft_var_readpackedpoints( stream,
3075                                               table_len,
3076                                               &spoint_count );
3077       offsetToData = FT_Stream_FTell( stream );
3078 
3079       FT_Stream_SeekSet( stream, here );
3080     }
3081 
3082     FT_TRACE5(( "cvar: there %s %d tuple%s:\n",
3083                 ( tupleCount & 0xFFF ) == 1 ? "is" : "are",
3084                 tupleCount & 0xFFF,
3085                 ( tupleCount & 0xFFF ) == 1 ? "" : "s" ));
3086 
3087     for ( i = 0; i < ( tupleCount & 0xFFF ); i++ )
3088     {
3089       FT_UInt   tupleDataSize;
3090       FT_UInt   tupleIndex;
3091       FT_Fixed  apply;
3092 
3093 
3094       FT_TRACE6(( "  tuple %d:\n", i ));
3095 
3096       tupleDataSize = FT_GET_USHORT();
3097       tupleIndex    = FT_GET_USHORT();
3098 
3099       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3100       {
3101         for ( j = 0; j < blend->num_axis; j++ )
3102           tuple_coords[j] = FT_GET_SHORT() * 4;  /* convert from        */
3103                                                  /* short frac to fixed */
3104       }
3105       else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3106       {
3107         FT_TRACE2(( "tt_face_vary_cvt:"
3108                     " invalid tuple index\n" ));
3109 
3110         error = FT_THROW( Invalid_Table );
3111         goto Exit;
3112       }
3113       else
3114         FT_MEM_COPY(
3115           tuple_coords,
3116           &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis],
3117           blend->num_axis * sizeof ( FT_Fixed ) );
3118 
3119       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3120       {
3121         for ( j = 0; j < blend->num_axis; j++ )
3122           im_start_coords[j] = FT_GET_SHORT() * 4;
3123         for ( j = 0; j < blend->num_axis; j++ )
3124           im_end_coords[j] = FT_GET_SHORT() * 4;
3125       }
3126 
3127       apply = ft_var_apply_tuple( blend,
3128                                   (FT_UShort)tupleIndex,
3129                                   tuple_coords,
3130                                   im_start_coords,
3131                                   im_end_coords );
3132 
3133       if ( apply == 0 )              /* tuple isn't active for our blend */
3134       {
3135         offsetToData += tupleDataSize;
3136         continue;
3137       }
3138 
3139       here = FT_Stream_FTell( stream );
3140 
3141       FT_Stream_SeekSet( stream, offsetToData );
3142 
3143       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
3144       {
3145         localpoints = ft_var_readpackedpoints( stream,
3146                                                table_len,
3147                                                &point_count );
3148         points      = localpoints;
3149       }
3150       else
3151       {
3152         points      = sharedpoints;
3153         point_count = spoint_count;
3154       }
3155 
3156       deltas = ft_var_readpackeddeltas( stream,
3157                                         table_len,
3158                                         point_count == 0 ? face->cvt_size
3159                                                          : point_count );
3160 
3161       if ( !points                                                        ||
3162            !deltas                                                        ||
3163            ( localpoints == ALL_POINTS && point_count != face->cvt_size ) )
3164         ; /* failure, ignore it */
3165 
3166       else if ( localpoints == ALL_POINTS )
3167       {
3168 #ifdef FT_DEBUG_LEVEL_TRACE
3169         int  count = 0;
3170 #endif
3171 
3172 
3173         FT_TRACE7(( "    CVT deltas:\n" ));
3174 
3175         /* this means that there are deltas for every entry in cvt */
3176         for ( j = 0; j < face->cvt_size; j++ )
3177         {
3178           FT_Long  orig_cvt = face->cvt[j];
3179 
3180 
3181           face->cvt[j] = (FT_Short)( orig_cvt +
3182                                      FT_MulFix( deltas[j], apply ) );
3183 
3184 #ifdef FT_DEBUG_LEVEL_TRACE
3185           if ( orig_cvt != face->cvt[j] )
3186           {
3187             FT_TRACE7(( "      %d: %d -> %d\n",
3188                         j, orig_cvt, face->cvt[j] ));
3189             count++;
3190           }
3191 #endif
3192         }
3193 
3194 #ifdef FT_DEBUG_LEVEL_TRACE
3195         if ( !count )
3196           FT_TRACE7(( "      none\n" ));
3197 #endif
3198       }
3199 
3200       else
3201       {
3202 #ifdef FT_DEBUG_LEVEL_TRACE
3203         int  count = 0;
3204 #endif
3205 
3206 
3207         FT_TRACE7(( "    CVT deltas:\n" ));
3208 
3209         for ( j = 0; j < point_count; j++ )
3210         {
3211           int      pindex;
3212           FT_Long  orig_cvt;
3213 
3214 
3215           pindex = points[j];
3216           if ( (FT_ULong)pindex >= face->cvt_size )
3217             continue;
3218 
3219           orig_cvt          = face->cvt[pindex];
3220           face->cvt[pindex] = (FT_Short)( orig_cvt +
3221                                           FT_MulFix( deltas[j], apply ) );
3222 
3223 #ifdef FT_DEBUG_LEVEL_TRACE
3224           if ( orig_cvt != face->cvt[pindex] )
3225           {
3226             FT_TRACE7(( "      %d: %d -> %d\n",
3227                         pindex, orig_cvt, face->cvt[pindex] ));
3228             count++;
3229           }
3230 #endif
3231         }
3232 
3233 #ifdef FT_DEBUG_LEVEL_TRACE
3234         if ( !count )
3235           FT_TRACE7(( "      none\n" ));
3236 #endif
3237       }
3238 
3239       if ( localpoints != ALL_POINTS )
3240         FT_FREE( localpoints );
3241       FT_FREE( deltas );
3242 
3243       offsetToData += tupleDataSize;
3244 
3245       FT_Stream_SeekSet( stream, here );
3246     }
3247 
3248     FT_TRACE5(( "\n" ));
3249 
3250   FExit:
3251     FT_FRAME_EXIT();
3252 
3253   Exit:
3254     if ( sharedpoints != ALL_POINTS )
3255       FT_FREE( sharedpoints );
3256     FT_FREE( tuple_coords );
3257     FT_FREE( im_start_coords );
3258     FT_FREE( im_end_coords );
3259 
3260     return error;
3261   }
3262 
3263 
3264   /* Shift the original coordinates of all points between indices `p1' */
3265   /* and `p2', using the same difference as given by index `ref'.      */
3266 
3267   /* modeled after `af_iup_shift' */
3268 
3269   static void
3270   tt_delta_shift( int         p1,
3271                   int         p2,
3272                   int         ref,
3273                   FT_Vector*  in_points,
3274                   FT_Vector*  out_points )
3275   {
3276     int        p;
3277     FT_Vector  delta;
3278 
3279 
3280     delta.x = out_points[ref].x - in_points[ref].x;
3281     delta.y = out_points[ref].y - in_points[ref].y;
3282 
3283     if ( delta.x == 0 && delta.y == 0 )
3284       return;
3285 
3286     for ( p = p1; p < ref; p++ )
3287     {
3288       out_points[p].x += delta.x;
3289       out_points[p].y += delta.y;
3290     }
3291 
3292     for ( p = ref + 1; p <= p2; p++ )
3293     {
3294       out_points[p].x += delta.x;
3295       out_points[p].y += delta.y;
3296     }
3297   }
3298 
3299 
3300   /* Interpolate the original coordinates of all points with indices */
3301   /* between `p1' and `p2', using `ref1' and `ref2' as the reference */
3302   /* point indices.                                                  */
3303 
3304   /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */
3305   /* `Ins_IUP'                                                     */
3306 
3307   static void
3308   tt_delta_interpolate( int         p1,
3309                         int         p2,
3310                         int         ref1,
3311                         int         ref2,
3312                         FT_Vector*  in_points,
3313                         FT_Vector*  out_points )
3314   {
3315     int  p, i;
3316 
3317     FT_Pos  out, in1, in2, out1, out2, d1, d2;
3318 
3319 
3320     if ( p1 > p2 )
3321       return;
3322 
3323     /* handle both horizontal and vertical coordinates */
3324     for ( i = 0; i <= 1; i++ )
3325     {
3326       /* shift array pointers so that we can access `foo.y' as `foo.x' */
3327       in_points  = (FT_Vector*)( (FT_Pos*)in_points + i );
3328       out_points = (FT_Vector*)( (FT_Pos*)out_points + i );
3329 
3330       if ( in_points[ref1].x > in_points[ref2].x )
3331       {
3332         p    = ref1;
3333         ref1 = ref2;
3334         ref2 = p;
3335       }
3336 
3337       in1  = in_points[ref1].x;
3338       in2  = in_points[ref2].x;
3339       out1 = out_points[ref1].x;
3340       out2 = out_points[ref2].x;
3341       d1   = out1 - in1;
3342       d2   = out2 - in2;
3343 
3344       /* If the reference points have the same coordinate but different */
3345       /* delta, inferred delta is zero.  Otherwise interpolate.         */
3346       if ( in1 != in2 || out1 == out2 )
3347       {
3348         FT_Fixed  scale = in1 != in2 ? FT_DivFix( out2 - out1, in2 - in1 )
3349                                      : 0;
3350 
3351 
3352         for ( p = p1; p <= p2; p++ )
3353         {
3354           out = in_points[p].x;
3355 
3356           if ( out <= in1 )
3357             out += d1;
3358           else if ( out >= in2 )
3359             out += d2;
3360           else
3361             out = out1 + FT_MulFix( out - in1, scale );
3362 
3363           out_points[p].x = out;
3364         }
3365       }
3366     }
3367   }
3368 
3369 
3370   /* Interpolate points without delta values, similar to */
3371   /* the `IUP' hinting instruction.                      */
3372 
3373   /* modeled after `Ins_IUP */
3374 
3375   static void
3376   tt_interpolate_deltas( FT_Outline*  outline,
3377                          FT_Vector*   out_points,
3378                          FT_Vector*   in_points,
3379                          FT_Bool*     has_delta )
3380   {
3381     FT_Int  first_point;
3382     FT_Int  end_point;
3383 
3384     FT_Int  first_delta;
3385     FT_Int  cur_delta;
3386 
3387     FT_Int    point;
3388     FT_Short  contour;
3389 
3390 
3391     /* ignore empty outlines */
3392     if ( !outline->n_contours )
3393       return;
3394 
3395     contour = 0;
3396     point   = 0;
3397 
3398     do
3399     {
3400       end_point   = outline->contours[contour];
3401       first_point = point;
3402 
3403       /* search first point that has a delta */
3404       while ( point <= end_point && !has_delta[point] )
3405         point++;
3406 
3407       if ( point <= end_point )
3408       {
3409         first_delta = point;
3410         cur_delta   = point;
3411 
3412         point++;
3413 
3414         while ( point <= end_point )
3415         {
3416           /* search next point that has a delta  */
3417           /* and interpolate intermediate points */
3418           if ( has_delta[point] )
3419           {
3420             tt_delta_interpolate( cur_delta + 1,
3421                                   point - 1,
3422                                   cur_delta,
3423                                   point,
3424                                   in_points,
3425                                   out_points );
3426             cur_delta = point;
3427           }
3428 
3429           point++;
3430         }
3431 
3432         /* shift contour if we only have a single delta */
3433         if ( cur_delta == first_delta )
3434           tt_delta_shift( first_point,
3435                           end_point,
3436                           cur_delta,
3437                           in_points,
3438                           out_points );
3439         else
3440         {
3441           /* otherwise handle remaining points       */
3442           /* at the end and beginning of the contour */
3443           tt_delta_interpolate( cur_delta + 1,
3444                                 end_point,
3445                                 cur_delta,
3446                                 first_delta,
3447                                 in_points,
3448                                 out_points );
3449 
3450           if ( first_delta > 0 )
3451             tt_delta_interpolate( first_point,
3452                                   first_delta - 1,
3453                                   cur_delta,
3454                                   first_delta,
3455                                   in_points,
3456                                   out_points );
3457         }
3458       }
3459       contour++;
3460 
3461     } while ( contour < outline->n_contours );
3462   }
3463 
3464 
3465   /*************************************************************************/
3466   /*                                                                       */
3467   /* <Function>                                                            */
3468   /*    TT_Vary_Apply_Glyph_Deltas                                         */
3469   /*                                                                       */
3470   /* <Description>                                                         */
3471   /*    Apply the appropriate deltas to the current glyph.                 */
3472   /*                                                                       */
3473   /* <Input>                                                               */
3474   /*    face        :: A handle to the target face object.                 */
3475   /*                                                                       */
3476   /*    glyph_index :: The index of the glyph being modified.              */
3477   /*                                                                       */
3478   /*    n_points    :: The number of the points in the glyph, including    */
3479   /*                   phantom points.                                     */
3480   /*                                                                       */
3481   /* <InOut>                                                               */
3482   /*    outline     :: The outline to change.                              */
3483   /*                                                                       */
3484   /* <Return>                                                              */
3485   /*    FreeType error code.  0 means success.                             */
3486   /*                                                                       */
3487   FT_LOCAL_DEF( FT_Error )
3488   TT_Vary_Apply_Glyph_Deltas( TT_Face      face,
3489                               FT_UInt      glyph_index,
3490                               FT_Outline*  outline,
3491                               FT_UInt      n_points )
3492   {
3493     FT_Stream   stream = face->root.stream;
3494     FT_Memory   memory = stream->memory;
3495     GX_Blend    blend  = face->blend;
3496 
3497     FT_Vector*  points_org = NULL;
3498     FT_Vector*  points_out = NULL;
3499     FT_Bool*    has_delta  = NULL;
3500 
3501     FT_Error    error;
3502     FT_ULong    glyph_start;
3503     FT_UInt     tupleCount;
3504     FT_ULong    offsetToData;
3505     FT_ULong    here;
3506     FT_UInt     i, j;
3507     FT_Fixed*   tuple_coords    = NULL;
3508     FT_Fixed*   im_start_coords = NULL;
3509     FT_Fixed*   im_end_coords   = NULL;
3510     FT_UInt     point_count, spoint_count = 0;
3511     FT_UShort*  sharedpoints = NULL;
3512     FT_UShort*  localpoints  = NULL;
3513     FT_UShort*  points;
3514     FT_Short    *deltas_x, *deltas_y;
3515 
3516 
3517     if ( !face->doblend || !blend )
3518       return FT_THROW( Invalid_Argument );
3519 
3520     if ( glyph_index >= blend->gv_glyphcnt      ||
3521          blend->glyphoffsets[glyph_index] ==
3522            blend->glyphoffsets[glyph_index + 1] )
3523     {
3524       FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3525                   " no variation data for this glyph\n" ));
3526       return FT_Err_Ok;
3527     }
3528 
3529     if ( FT_NEW_ARRAY( points_org, n_points ) ||
3530          FT_NEW_ARRAY( points_out, n_points ) ||
3531          FT_NEW_ARRAY( has_delta, n_points )  )
3532       goto Fail1;
3533 
3534     if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] )   ||
3535          FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] -
3536                            blend->glyphoffsets[glyph_index] ) )
3537       goto Fail1;
3538 
3539     glyph_start = FT_Stream_FTell( stream );
3540 
3541     /* each set of glyph variation data is formatted similarly to `cvar' */
3542 
3543     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
3544          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3545          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
3546       goto Fail2;
3547 
3548     tupleCount   = FT_GET_USHORT();
3549     offsetToData = FT_GET_USHORT();
3550 
3551     /* rough sanity test */
3552     if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
3553            blend->gvar_size )
3554     {
3555       FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3556                   " invalid glyph variation array header\n" ));
3557 
3558       error = FT_THROW( Invalid_Table );
3559       goto Fail2;
3560     }
3561 
3562     offsetToData += glyph_start;
3563 
3564     if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3565     {
3566       here = FT_Stream_FTell( stream );
3567 
3568       FT_Stream_SeekSet( stream, offsetToData );
3569 
3570       sharedpoints = ft_var_readpackedpoints( stream,
3571                                               blend->gvar_size,
3572                                               &spoint_count );
3573       offsetToData = FT_Stream_FTell( stream );
3574 
3575       FT_Stream_SeekSet( stream, here );
3576     }
3577 
3578     FT_TRACE5(( "gvar: there %s %d tuple%s:\n",
3579                 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
3580                 tupleCount & GX_TC_TUPLE_COUNT_MASK,
3581                 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
3582 
3583     for ( j = 0; j < n_points; j++ )
3584       points_org[j] = outline->points[j];
3585 
3586     for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
3587     {
3588       FT_UInt   tupleDataSize;
3589       FT_UInt   tupleIndex;
3590       FT_Fixed  apply;
3591 
3592 
3593       FT_TRACE6(( "  tuple %d:\n", i ));
3594 
3595       tupleDataSize = FT_GET_USHORT();
3596       tupleIndex    = FT_GET_USHORT();
3597 
3598       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3599       {
3600         for ( j = 0; j < blend->num_axis; j++ )
3601           tuple_coords[j] = FT_GET_SHORT() * 4;   /* convert from        */
3602                                                   /* short frac to fixed */
3603       }
3604       else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3605       {
3606         FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3607                     " invalid tuple index\n" ));
3608 
3609         error = FT_THROW( Invalid_Table );
3610         goto Fail2;
3611       }
3612       else
3613         FT_MEM_COPY(
3614           tuple_coords,
3615           &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis],
3616           blend->num_axis * sizeof ( FT_Fixed ) );
3617 
3618       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3619       {
3620         for ( j = 0; j < blend->num_axis; j++ )
3621           im_start_coords[j] = FT_GET_SHORT() * 4;
3622         for ( j = 0; j < blend->num_axis; j++ )
3623           im_end_coords[j] = FT_GET_SHORT() * 4;
3624       }
3625 
3626       apply = ft_var_apply_tuple( blend,
3627                                   (FT_UShort)tupleIndex,
3628                                   tuple_coords,
3629                                   im_start_coords,
3630                                   im_end_coords );
3631 
3632       if ( apply == 0 )              /* tuple isn't active for our blend */
3633       {
3634         offsetToData += tupleDataSize;
3635         continue;
3636       }
3637 
3638       here = FT_Stream_FTell( stream );
3639 
3640       FT_Stream_SeekSet( stream, offsetToData );
3641 
3642       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
3643       {
3644         localpoints = ft_var_readpackedpoints( stream,
3645                                                blend->gvar_size,
3646                                                &point_count );
3647         points      = localpoints;
3648       }
3649       else
3650       {
3651         points      = sharedpoints;
3652         point_count = spoint_count;
3653       }
3654 
3655       deltas_x = ft_var_readpackeddeltas( stream,
3656                                           blend->gvar_size,
3657                                           point_count == 0 ? n_points
3658                                                            : point_count );
3659       deltas_y = ft_var_readpackeddeltas( stream,
3660                                           blend->gvar_size,
3661                                           point_count == 0 ? n_points
3662                                                            : point_count );
3663 
3664       if ( !points || !deltas_y || !deltas_x )
3665         ; /* failure, ignore it */
3666 
3667       else if ( points == ALL_POINTS )
3668       {
3669 #ifdef FT_DEBUG_LEVEL_TRACE
3670         int  count = 0;
3671 #endif
3672 
3673 
3674         FT_TRACE7(( "    point deltas:\n" ));
3675 
3676         /* this means that there are deltas for every point in the glyph */
3677         for ( j = 0; j < n_points; j++ )
3678         {
3679           FT_Pos  delta_x = FT_MulFix( deltas_x[j], apply );
3680           FT_Pos  delta_y = FT_MulFix( deltas_y[j], apply );
3681 
3682 
3683           if ( j < n_points - 4 )
3684           {
3685             outline->points[j].x += delta_x;
3686             outline->points[j].y += delta_y;
3687           }
3688           else
3689           {
3690             /* To avoid double adjustment of advance width or height, */
3691             /* adjust phantom points only if there is no HVAR or VVAR */
3692             /* support, respectively.                                 */
3693             if ( j == ( n_points - 4 )        &&
3694                  !( face->variation_support &
3695                     TT_FACE_FLAG_VAR_LSB    ) )
3696               outline->points[j].x += delta_x;
3697 
3698             else if ( j == ( n_points - 3 )          &&
3699                       !( face->variation_support   &
3700                          TT_FACE_FLAG_VAR_HADVANCE ) )
3701               outline->points[j].x += delta_x;
3702 
3703             else if ( j == ( n_points - 2 )        &&
3704                       !( face->variation_support &
3705                          TT_FACE_FLAG_VAR_TSB    ) )
3706               outline->points[j].y += delta_y;
3707 
3708             else if ( j == ( n_points - 1 )          &&
3709                       !( face->variation_support   &
3710                          TT_FACE_FLAG_VAR_VADVANCE ) )
3711               outline->points[j].y += delta_y;
3712           }
3713 
3714 #ifdef FT_DEBUG_LEVEL_TRACE
3715           if ( delta_x || delta_y )
3716           {
3717             FT_TRACE7(( "      %d: (%d, %d) -> (%d, %d)\n",
3718                         j,
3719                         outline->points[j].x - delta_x,
3720                         outline->points[j].y - delta_y,
3721                         outline->points[j].x,
3722                         outline->points[j].y ));
3723             count++;
3724           }
3725 #endif
3726         }
3727 
3728 #ifdef FT_DEBUG_LEVEL_TRACE
3729         if ( !count )
3730           FT_TRACE7(( "      none\n" ));
3731 #endif
3732       }
3733 
3734       else
3735       {
3736 #ifdef FT_DEBUG_LEVEL_TRACE
3737         int  count = 0;
3738 #endif
3739 
3740 
3741         /* we have to interpolate the missing deltas similar to the */
3742         /* IUP bytecode instruction                                 */
3743         for ( j = 0; j < n_points; j++ )
3744         {
3745           has_delta[j]  = FALSE;
3746           points_out[j] = points_org[j];
3747         }
3748 
3749         for ( j = 0; j < point_count; j++ )
3750         {
3751           FT_UShort  idx = points[j];
3752 
3753 
3754           if ( idx >= n_points )
3755             continue;
3756 
3757           has_delta[idx] = TRUE;
3758 
3759           points_out[idx].x += FT_MulFix( deltas_x[j], apply );
3760           points_out[idx].y += FT_MulFix( deltas_y[j], apply );
3761         }
3762 
3763         /* no need to handle phantom points here,      */
3764         /* since solitary points can't be interpolated */
3765         tt_interpolate_deltas( outline,
3766                                points_out,
3767                                points_org,
3768                                has_delta );
3769 
3770         FT_TRACE7(( "    point deltas:\n" ));
3771 
3772         for ( j = 0; j < n_points; j++ )
3773         {
3774           FT_Pos  delta_x = points_out[j].x - points_org[j].x;
3775           FT_Pos  delta_y = points_out[j].y - points_org[j].y;
3776 
3777 
3778           if ( j < n_points - 4 )
3779           {
3780             outline->points[j].x += delta_x;
3781             outline->points[j].y += delta_y;
3782           }
3783           else
3784           {
3785             /* To avoid double adjustment of advance width or height, */
3786             /* adjust phantom points only if there is no HVAR or VVAR */
3787             /* support, respectively.                                 */
3788             if ( j == ( n_points - 4 )        &&
3789                  !( face->variation_support &
3790                     TT_FACE_FLAG_VAR_LSB    ) )
3791               outline->points[j].x += delta_x;
3792 
3793             else if ( j == ( n_points - 3 )          &&
3794                       !( face->variation_support   &
3795                          TT_FACE_FLAG_VAR_HADVANCE ) )
3796               outline->points[j].x += delta_x;
3797 
3798             else if ( j == ( n_points - 2 )        &&
3799                       !( face->variation_support &
3800                          TT_FACE_FLAG_VAR_TSB    ) )
3801               outline->points[j].y += delta_y;
3802 
3803             else if ( j == ( n_points - 1 )          &&
3804                       !( face->variation_support   &
3805                          TT_FACE_FLAG_VAR_VADVANCE ) )
3806               outline->points[j].y += delta_y;
3807           }
3808 
3809 #ifdef FT_DEBUG_LEVEL_TRACE
3810           if ( delta_x || delta_y )
3811           {
3812             FT_TRACE7(( "      %d: (%d, %d) -> (%d, %d)\n",
3813                         j,
3814                         outline->points[j].x - delta_x,
3815                         outline->points[j].y - delta_y,
3816                         outline->points[j].x,
3817                         outline->points[j].y ));
3818             count++;
3819           }
3820 #endif
3821         }
3822 
3823 #ifdef FT_DEBUG_LEVEL_TRACE
3824         if ( !count )
3825           FT_TRACE7(( "      none\n" ));
3826 #endif
3827       }
3828 
3829       if ( localpoints != ALL_POINTS )
3830         FT_FREE( localpoints );
3831       FT_FREE( deltas_x );
3832       FT_FREE( deltas_y );
3833 
3834       offsetToData += tupleDataSize;
3835 
3836       FT_Stream_SeekSet( stream, here );
3837     }
3838 
3839     FT_TRACE5(( "\n" ));
3840 
3841   Fail2:
3842     if ( sharedpoints != ALL_POINTS )
3843       FT_FREE( sharedpoints );
3844     FT_FREE( tuple_coords );
3845     FT_FREE( im_start_coords );
3846     FT_FREE( im_end_coords );
3847 
3848     FT_FRAME_EXIT();
3849 
3850   Fail1:
3851     FT_FREE( points_org );
3852     FT_FREE( points_out );
3853     FT_FREE( has_delta );
3854 
3855     return error;
3856   }
3857 
3858 
3859   /*************************************************************************/
3860   /*                                                                       */
3861   /* <Function>                                                            */
3862   /*    tt_get_var_blend                                                   */
3863   /*                                                                       */
3864   /* <Description>                                                         */
3865   /*    An extended internal version of `TT_Get_MM_Blend' that returns     */
3866   /*    pointers instead of copying data, without any initialization of    */
3867   /*    the MM machinery in case it isn't loaded yet.                      */
3868   /*                                                                       */
3869   FT_LOCAL_DEF( FT_Error )
3870   tt_get_var_blend( TT_Face      face,
3871                     FT_UInt     *num_coords,
3872                     FT_Fixed*   *coords,
3873                     FT_Fixed*   *normalizedcoords,
3874                     FT_MM_Var*  *mm_var )
3875   {
3876     if ( face->blend )
3877     {
3878       if ( num_coords )
3879         *num_coords       = face->blend->num_axis;
3880       if ( coords )
3881         *coords           = face->blend->coords;
3882       if ( normalizedcoords )
3883         *normalizedcoords = face->blend->normalizedcoords;
3884       if ( mm_var )
3885         *mm_var           = face->blend->mmvar;
3886     }
3887     else
3888     {
3889       if ( num_coords )
3890         *num_coords = 0;
3891       if ( coords )
3892         *coords     = NULL;
3893       if ( mm_var )
3894         *mm_var     = NULL;
3895     }
3896 
3897     return FT_Err_Ok;
3898   }
3899 
3900 
3901   static void
3902   ft_var_done_item_variation_store( TT_Face          face,
3903                                     GX_ItemVarStore  itemStore )
3904   {
3905     FT_Memory  memory = FT_FACE_MEMORY( face );
3906     FT_UInt    i;
3907 
3908 
3909     if ( itemStore->varData )
3910     {
3911       for ( i = 0; i < itemStore->dataCount; i++ )
3912       {
3913         FT_FREE( itemStore->varData[i].regionIndices );
3914         FT_FREE( itemStore->varData[i].deltaSet );
3915       }
3916 
3917       FT_FREE( itemStore->varData );
3918     }
3919 
3920     if ( itemStore->varRegionList )
3921     {
3922       for ( i = 0; i < itemStore->regionCount; i++ )
3923         FT_FREE( itemStore->varRegionList[i].axisList );
3924 
3925       FT_FREE( itemStore->varRegionList );
3926     }
3927   }
3928 
3929 
3930   /*************************************************************************/
3931   /*                                                                       */
3932   /* <Function>                                                            */
3933   /*    tt_done_blend                                                      */
3934   /*                                                                       */
3935   /* <Description>                                                         */
3936   /*    Free the blend internal data structure.                            */
3937   /*                                                                       */
3938   FT_LOCAL_DEF( void )
3939   tt_done_blend( TT_Face  face )
3940   {
3941     FT_Memory  memory = FT_FACE_MEMORY( face );
3942     GX_Blend   blend  = face->blend;
3943 
3944 
3945     if ( blend )
3946     {
3947       FT_UInt  i, num_axes;
3948 
3949 
3950       /* blend->num_axis might not be set up yet */
3951       num_axes = blend->mmvar->num_axis;
3952 
3953       FT_FREE( blend->coords );
3954       FT_FREE( blend->normalizedcoords );
3955       FT_FREE( blend->normalized_stylecoords );
3956       FT_FREE( blend->mmvar );
3957 
3958       if ( blend->avar_segment )
3959       {
3960         for ( i = 0; i < num_axes; i++ )
3961           FT_FREE( blend->avar_segment[i].correspondence );
3962         FT_FREE( blend->avar_segment );
3963       }
3964 
3965       if ( blend->hvar_table )
3966       {
3967         ft_var_done_item_variation_store( face,
3968                                           &blend->hvar_table->itemStore );
3969 
3970         FT_FREE( blend->hvar_table->widthMap.innerIndex );
3971         FT_FREE( blend->hvar_table->widthMap.outerIndex );
3972         FT_FREE( blend->hvar_table );
3973       }
3974 
3975       if ( blend->vvar_table )
3976       {
3977         ft_var_done_item_variation_store( face,
3978                                           &blend->vvar_table->itemStore );
3979 
3980         FT_FREE( blend->vvar_table->widthMap.innerIndex );
3981         FT_FREE( blend->vvar_table->widthMap.outerIndex );
3982         FT_FREE( blend->vvar_table );
3983       }
3984 
3985       if ( blend->mvar_table )
3986       {
3987         ft_var_done_item_variation_store( face,
3988                                           &blend->mvar_table->itemStore );
3989 
3990         FT_FREE( blend->mvar_table->values );
3991         FT_FREE( blend->mvar_table );
3992       }
3993 
3994       FT_FREE( blend->tuplecoords );
3995       FT_FREE( blend->glyphoffsets );
3996       FT_FREE( blend );
3997     }
3998   }
3999 
4000 #else /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
4001 
4002   /* ANSI C doesn't like empty source files */
4003   typedef int  _tt_gxvar_dummy;
4004 
4005 #endif /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
4006 
4007 
4008 /* END */