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     a = mmvar->axis;
1737     for ( i = 0; i < num_coords; i++, a++ )
1738     {
1739       FT_Fixed  coord = coords[i];
1740 
1741 
1742       FT_TRACE5(( "    %d: %.5f\n", i, coord / 65536.0 ));
1743       if ( coord > a->maximum || coord < a->minimum )
1744       {
1745         FT_TRACE1((
1746           "ft_var_to_normalized: design coordinate %.5f\n"
1747           "                      is out of range [%.5f;%.5f]; clamping\n",
1748           coord / 65536.0,
1749           a->minimum / 65536.0,
1750           a->maximum / 65536.0 ));
1751 
1752         if ( coord > a->maximum )
1753           coord = a->maximum;
1754         else
1755           coord = a->minimum;
1756       }
1757 
1758       if ( coord < a->def )
1759         normalized[i] = -FT_DivFix( coord - a->def,
1760                                     a->minimum - a->def );
1761       else if ( coord > a->def )
1762         normalized[i] = FT_DivFix( coord - a->def,
1763                                    a->maximum - a->def );
1764       else
1765         normalized[i] = 0;
1766     }
1767 
1768     FT_TRACE5(( "\n" ));
1769 
1770     for ( ; i < mmvar->num_axis; i++ )
1771       normalized[i] = 0;
1772 
1773     if ( blend->avar_segment )
1774     {
1775       FT_TRACE5(( "normalized design coordinates"
1776                   " before applying `avar' data:\n" ));
1777 
1778       av = blend->avar_segment;
1779       for ( i = 0; i < mmvar->num_axis; i++, av++ )
1780       {
1781         for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
1782         {
1783           if ( normalized[i] < av->correspondence[j].fromCoord )
1784           {
1785             FT_TRACE5(( "  %.5f\n", normalized[i] / 65536.0 ));
1786 
1787             normalized[i] =
1788               FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord,
1789                          av->correspondence[j].toCoord -
1790                            av->correspondence[j - 1].toCoord,
1791                          av->correspondence[j].fromCoord -
1792                            av->correspondence[j - 1].fromCoord ) +
1793               av->correspondence[j - 1].toCoord;
1794             break;
1795           }
1796         }
1797       }
1798     }
1799   }
1800 
1801 
1802   /* convert from normalized coordinates to design coordinates */
1803 
1804   static void
1805   ft_var_to_design( TT_Face    face,
1806                     FT_UInt    num_coords,
1807                     FT_Fixed*  coords,
1808                     FT_Fixed*  design )
1809   {
1810     GX_Blend      blend;
1811     FT_MM_Var*    mmvar;
1812     FT_Var_Axis*  a;
1813 
1814     FT_UInt  i, j, nc;
1815 
1816 
1817     blend = face->blend;
1818 
1819     nc = num_coords;
1820     if ( num_coords > blend->num_axis )
1821     {
1822       FT_TRACE2(( "ft_var_to_design:"
1823                   " only using first %d of %d coordinates\n",
1824                   blend->num_axis, num_coords ));
1825       nc = blend->num_axis;
1826     }
1827 
1828     for ( i = 0; i < nc; i++ )
1829       design[i] = coords[i];
1830 
1831     for ( ; i < num_coords; i++ )
1832       design[i] = 0;
1833 
1834     if ( blend->avar_segment )
1835     {
1836       GX_AVarSegment  av = blend->avar_segment;
1837 
1838 
1839       FT_TRACE5(( "design coordinates"
1840                   " after removing `avar' distortion:\n" ));
1841 
1842       for ( i = 0; i < nc; i++, av++ )
1843       {
1844         for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
1845         {
1846           if ( design[i] < av->correspondence[j].toCoord )
1847           {
1848             design[i] =
1849               FT_MulDiv( design[i] - av->correspondence[j - 1].toCoord,
1850                          av->correspondence[j].fromCoord -
1851                            av->correspondence[j - 1].fromCoord,
1852                          av->correspondence[j].toCoord -
1853                            av->correspondence[j - 1].toCoord ) +
1854               av->correspondence[j - 1].fromCoord;
1855 
1856             FT_TRACE5(( "  %.5f\n", design[i] / 65536.0 ));
1857             break;
1858           }
1859         }
1860       }
1861     }
1862 
1863     mmvar = blend->mmvar;
1864     a     = mmvar->axis;
1865 
1866     for ( i = 0; i < nc; i++, a++ )
1867     {
1868       if ( design[i] < 0 )
1869         design[i] = a->def + FT_MulFix( design[i],
1870                                         a->def - a->minimum );
1871       else if ( design[i] > 0 )
1872         design[i] = a->def + FT_MulFix( design[i],
1873                                         a->maximum - a->def );
1874       else
1875         design[i] = a->def;
1876     }
1877   }
1878 
1879 
1880   /*************************************************************************/
1881   /*************************************************************************/
1882   /*****                                                               *****/
1883   /*****               MULTIPLE MASTERS SERVICE FUNCTIONS              *****/
1884   /*****                                                               *****/
1885   /*************************************************************************/
1886   /*************************************************************************/
1887 
1888 
1889   typedef struct  GX_FVar_Head_
1890   {
1891     FT_Long    version;
1892     FT_UShort  offsetToData;
1893     FT_UShort  axisCount;
1894     FT_UShort  axisSize;
1895     FT_UShort  instanceCount;
1896     FT_UShort  instanceSize;
1897 
1898   } GX_FVar_Head;
1899 
1900 
1901   typedef struct  fvar_axis_
1902   {
1903     FT_ULong   axisTag;
1904     FT_Fixed   minValue;
1905     FT_Fixed   defaultValue;
1906     FT_Fixed   maxValue;
1907     FT_UShort  flags;
1908     FT_UShort  nameID;
1909 
1910   } GX_FVar_Axis;
1911 
1912 
1913   /*************************************************************************/
1914   /*                                                                       */
1915   /* <Function>                                                            */
1916   /*    TT_Get_MM_Var                                                      */
1917   /*                                                                       */
1918   /* <Description>                                                         */
1919   /*    Check that the font's `fvar' table is valid, parse it, and return  */
1920   /*    those data.  It also loads (and parses) the `MVAR' table, if       */
1921   /*    possible.                                                          */
1922   /*                                                                       */
1923   /* <InOut>                                                               */
1924   /*    face   :: The font face.                                           */
1925   /*              TT_Get_MM_Var initializes the blend structure.           */
1926   /*                                                                       */
1927   /* <Output>                                                              */
1928   /*    master :: The `fvar' data (must be freed by caller).  Can be NULL, */
1929   /*              which makes this function simply load MM support.        */
1930   /*                                                                       */
1931   /* <Return>                                                              */
1932   /*    FreeType error code.  0 means success.                             */
1933   /*                                                                       */
1934   FT_LOCAL_DEF( FT_Error )
1935   TT_Get_MM_Var( TT_Face      face,
1936                  FT_MM_Var*  *master )
1937   {
1938     FT_Stream            stream     = face->root.stream;
1939     FT_Memory            memory     = face->root.memory;
1940     FT_ULong             table_len;
1941     FT_Error             error      = FT_Err_Ok;
1942     FT_ULong             fvar_start = 0;
1943     FT_UInt              i, j;
1944     FT_MM_Var*           mmvar = NULL;
1945     FT_Fixed*            next_coords;
1946     FT_Fixed*            nsc;
1947     FT_String*           next_name;
1948     FT_Var_Axis*         a;
1949     FT_Fixed*            c;
1950     FT_Var_Named_Style*  ns;
1951     GX_FVar_Head         fvar_head;
1952     FT_Bool              usePsName  = 0;
1953     FT_UInt              num_instances;
1954     FT_UInt              num_axes;
1955     FT_UShort*           axis_flags;
1956 
1957     FT_Offset  mmvar_size;
1958     FT_Offset  axis_flags_size;
1959     FT_Offset  axis_size;
1960     FT_Offset  namedstyle_size;
1961     FT_Offset  next_coords_size;
1962     FT_Offset  next_name_size;
1963 
1964     FT_Bool  need_init;
1965 
1966     static const FT_Frame_Field  fvar_fields[] =
1967     {
1968 
1969 #undef  FT_STRUCTURE
1970 #define FT_STRUCTURE  GX_FVar_Head
1971 
1972       FT_FRAME_START( 16 ),
1973         FT_FRAME_LONG      ( version ),
1974         FT_FRAME_USHORT    ( offsetToData ),
1975         FT_FRAME_SKIP_SHORT,
1976         FT_FRAME_USHORT    ( axisCount ),
1977         FT_FRAME_USHORT    ( axisSize ),
1978         FT_FRAME_USHORT    ( instanceCount ),
1979         FT_FRAME_USHORT    ( instanceSize ),
1980       FT_FRAME_END
1981     };
1982 
1983     static const FT_Frame_Field  fvaraxis_fields[] =
1984     {
1985 
1986 #undef  FT_STRUCTURE
1987 #define FT_STRUCTURE  GX_FVar_Axis
1988 
1989       FT_FRAME_START( 20 ),
1990         FT_FRAME_ULONG ( axisTag ),
1991         FT_FRAME_LONG  ( minValue ),
1992         FT_FRAME_LONG  ( defaultValue ),
1993         FT_FRAME_LONG  ( maxValue ),
1994         FT_FRAME_USHORT( flags ),
1995         FT_FRAME_USHORT( nameID ),
1996       FT_FRAME_END
1997     };
1998 
1999 
2000     /* read the font data and set up the internal representation */
2001     /* if not already done                                       */
2002 
2003     need_init = !face->blend;
2004 
2005     if ( need_init )
2006     {
2007       FT_TRACE2(( "FVAR " ));
2008 
2009       /* both `fvar' and `gvar' must be present */
2010       if ( FT_SET_ERROR( face->goto_table( face, TTAG_gvar,
2011                                            stream, &table_len ) ) )
2012       {
2013         /* CFF2 is an alternate to gvar here */
2014         if ( FT_SET_ERROR( face->goto_table( face, TTAG_CFF2,
2015                                              stream, &table_len ) ) )
2016         {
2017           FT_TRACE1(( "\n"
2018                       "TT_Get_MM_Var: `gvar' or `CFF2' table is missing\n" ));
2019           goto Exit;
2020         }
2021       }
2022 
2023       if ( FT_SET_ERROR( face->goto_table( face, TTAG_fvar,
2024                                            stream, &table_len ) ) )
2025       {
2026         FT_TRACE1(( "is missing\n" ));
2027         goto Exit;
2028       }
2029 
2030       fvar_start = FT_STREAM_POS( );
2031 
2032       /* the validity of the `fvar' header data was already checked */
2033       /* in function `sfnt_init_face'                               */
2034       if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
2035         goto Exit;
2036 
2037       usePsName = FT_BOOL( fvar_head.instanceSize ==
2038                            6 + 4 * fvar_head.axisCount );
2039 
2040       FT_TRACE2(( "loaded\n" ));
2041 
2042       FT_TRACE5(( "%d variation ax%s\n",
2043                   fvar_head.axisCount,
2044                   fvar_head.axisCount == 1 ? "is" : "es" ));
2045 
2046       if ( FT_NEW( face->blend ) )
2047         goto Exit;
2048 
2049       num_axes              = fvar_head.axisCount;
2050       face->blend->num_axis = num_axes;
2051     }
2052     else
2053       num_axes = face->blend->num_axis;
2054 
2055     /* `num_instances' holds the number of all named instances, */
2056     /* including the default instance which might be missing    */
2057     /* in fvar's table of named instances                       */
2058     num_instances = (FT_UInt)face->root.style_flags >> 16;
2059 
2060     /* prepare storage area for MM data; this cannot overflow   */
2061     /* 32-bit arithmetic because of the size limits used in the */
2062     /* `fvar' table validity check in `sfnt_init_face'          */
2063 
2064     /* the various `*_size' variables, which we also use as     */
2065     /* offsets into the `mmlen' array, must be multiples of the */
2066     /* pointer size (except the last one); without such an      */
2067     /* alignment there might be runtime errors due to           */
2068     /* misaligned addresses                                     */
2069 #undef  ALIGN_SIZE
2070 #define ALIGN_SIZE( n ) \
2071           ( ( (n) + sizeof (void*) - 1 ) & ~( sizeof (void*) - 1 ) )
2072 
2073     mmvar_size       = ALIGN_SIZE( sizeof ( FT_MM_Var ) );
2074     axis_flags_size  = ALIGN_SIZE( num_axes *
2075                                    sizeof ( FT_UShort ) );
2076     axis_size        = ALIGN_SIZE( num_axes *
2077                                    sizeof ( FT_Var_Axis ) );
2078     namedstyle_size  = ALIGN_SIZE( num_instances *
2079                                    sizeof ( FT_Var_Named_Style ) );
2080     next_coords_size = ALIGN_SIZE( num_instances *
2081                                    num_axes *
2082                                    sizeof ( FT_Fixed ) );
2083     next_name_size   = num_axes * 5;
2084 
2085     if ( need_init )
2086     {
2087       face->blend->mmvar_len = mmvar_size       +
2088                                axis_flags_size  +
2089                                axis_size        +
2090                                namedstyle_size  +
2091                                next_coords_size +
2092                                next_name_size;
2093 
2094       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
2095         goto Exit;
2096       face->blend->mmvar = mmvar;
2097 
2098       /* set up pointers and offsets into the `mmvar' array; */
2099       /* the data gets filled in later on                    */
2100 
2101       mmvar->num_axis =
2102         num_axes;
2103       mmvar->num_designs =
2104         ~0U;                   /* meaningless in this context; each glyph */
2105                                /* may have a different number of designs  */
2106                                /* (or tuples, as called by Apple)         */
2107       mmvar->num_namedstyles =
2108         num_instances;
2109 
2110       /* alas, no public field in `FT_Var_Axis' for axis flags */
2111       axis_flags =
2112         (FT_UShort*)( (char*)mmvar + mmvar_size );
2113       mmvar->axis =
2114         (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
2115       mmvar->namedstyle =
2116         (FT_Var_Named_Style*)( (char*)mmvar->axis + axis_size );
2117 
2118       next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
2119                                  namedstyle_size );
2120       for ( i = 0; i < num_instances; i++ )
2121       {
2122         mmvar->namedstyle[i].coords  = next_coords;
2123         next_coords                 += num_axes;
2124       }
2125 
2126       next_name = (FT_String*)( (char*)mmvar->namedstyle +
2127                                 namedstyle_size + next_coords_size );
2128       for ( i = 0; i < num_axes; i++ )
2129       {
2130         mmvar->axis[i].name  = next_name;
2131         next_name           += 5;
2132       }
2133 
2134       /* now fill in the data */
2135 
2136       if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
2137         goto Exit;
2138 
2139       a = mmvar->axis;
2140       for ( i = 0; i < num_axes; i++ )
2141       {
2142         GX_FVar_Axis  axis_rec;
2143 
2144 #ifdef FT_DEBUG_LEVEL_TRACE
2145         int  invalid = 0;
2146 #endif
2147 
2148 
2149         if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
2150           goto Exit;
2151         a->tag     = axis_rec.axisTag;
2152         a->minimum = axis_rec.minValue;
2153         a->def     = axis_rec.defaultValue;
2154         a->maximum = axis_rec.maxValue;
2155         a->strid   = axis_rec.nameID;
2156 
2157         a->name[0] = (FT_String)(   a->tag >> 24 );
2158         a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
2159         a->name[2] = (FT_String)( ( a->tag >>  8 ) & 0xFF );
2160         a->name[3] = (FT_String)( ( a->tag       ) & 0xFF );
2161         a->name[4] = '\0';
2162 
2163         *axis_flags = axis_rec.flags;
2164 
2165         if ( a->minimum > a->def ||
2166              a->def > a->maximum )
2167         {
2168           a->minimum = a->def;
2169           a->maximum = a->def;
2170 
2171 #ifdef FT_DEBUG_LEVEL_TRACE
2172           invalid = 1;
2173 #endif
2174         }
2175 
2176 #ifdef FT_DEBUG_LEVEL_TRACE
2177         if ( i == 0 )
2178           FT_TRACE5(( "  idx   tag  "
2179                    /* "  XXX  `XXXX'" */
2180                       "    minimum     default     maximum   flags\n" ));
2181                    /* "  XXXX.XXXXX  XXXX.XXXXX  XXXX.XXXXX  0xXXXX" */
2182 
2183         FT_TRACE5(( "  %3d  `%s'"
2184                     "  %10.5f  %10.5f  %10.5f  0x%04X%s\n",
2185                     i,
2186                     a->name,
2187                     a->minimum / 65536.0,
2188                     a->def / 65536.0,
2189                     a->maximum / 65536.0,
2190                     *axis_flags,
2191                     invalid ? " (invalid, disabled)" : "" ));
2192 #endif
2193 
2194         a++;
2195         axis_flags++;
2196       }
2197 
2198       FT_TRACE5(( "\n" ));
2199 
2200       /* named instance coordinates are stored as design coordinates; */
2201       /* we have to convert them to normalized coordinates also       */
2202       if ( FT_NEW_ARRAY( face->blend->normalized_stylecoords,
2203                          num_axes * num_instances ) )
2204         goto Exit;
2205 
2206       if ( fvar_head.instanceCount && !face->blend->avar_loaded )
2207       {
2208         FT_ULong  offset = FT_STREAM_POS();
2209 
2210 
2211         ft_var_load_avar( face );
2212 
2213         if ( FT_STREAM_SEEK( offset ) )
2214           goto Exit;
2215       }
2216 
2217       FT_TRACE5(( "%d instance%s\n",
2218                   fvar_head.instanceCount,
2219                   fvar_head.instanceCount == 1 ? "" : "s" ));
2220 
2221       ns  = mmvar->namedstyle;
2222       nsc = face->blend->normalized_stylecoords;
2223       for ( i = 0; i < fvar_head.instanceCount; i++, ns++ )
2224       {
2225         /* PostScript names add 2 bytes to the instance record size */
2226         if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) +
2227                              4L * num_axes ) )
2228           goto Exit;
2229 
2230         ns->strid       =    FT_GET_USHORT();
2231         (void) /* flags = */ FT_GET_USHORT();
2232 
2233         c = ns->coords;
2234         for ( j = 0; j < num_axes; j++, c++ )
2235           *c = FT_GET_LONG();
2236 
2237         /* valid psid values are 6, [256;32767], and 0xFFFF */
2238         if ( usePsName )
2239           ns->psid = FT_GET_USHORT();
2240         else
2241           ns->psid = 0xFFFF;
2242 
2243 #ifdef FT_DEBUG_LEVEL_TRACE
2244         {
2245           SFNT_Service  sfnt = (SFNT_Service)face->sfnt;
2246 
2247           FT_String*  strname = NULL;
2248           FT_String*  psname  = NULL;
2249 
2250           FT_ULong  pos;
2251 
2252 
2253           pos = FT_STREAM_POS();
2254 
2255           if ( ns->strid != 0xFFFF )
2256           {
2257             (void)sfnt->get_name( face,
2258                                   (FT_UShort)ns->strid,
2259                                   &strname );
2260             if ( strname && !ft_strcmp( strname, ".notdef" ) )
2261               strname = NULL;
2262           }
2263 
2264           if ( ns->psid != 0xFFFF )
2265           {
2266             (void)sfnt->get_name( face,
2267                                   (FT_UShort)ns->psid,
2268                                   &psname );
2269             if ( psname && !ft_strcmp( psname, ".notdef" ) )
2270               psname = NULL;
2271           }
2272 
2273           (void)FT_STREAM_SEEK( pos );
2274 
2275           FT_TRACE5(( "  instance %d (%s%s%s, %s%s%s)\n",
2276                       i,
2277                       strname ? "name: `" : "",
2278                       strname ? strname : "unnamed",
2279                       strname ? "'" : "",
2280                       psname ? "PS name: `" : "",
2281                       psname ? psname : "no PS name",
2282                       psname ? "'" : "" ));
2283 
2284           FT_FREE( strname );
2285           FT_FREE( psname );
2286         }
2287 #endif /* FT_DEBUG_LEVEL_TRACE */
2288 
2289         ft_var_to_normalized( face, num_axes, ns->coords, nsc );
2290         nsc += num_axes;
2291 
2292         FT_FRAME_EXIT();
2293       }
2294 
2295       if ( num_instances != fvar_head.instanceCount )
2296       {
2297         SFNT_Service  sfnt = (SFNT_Service)face->sfnt;
2298 
2299         FT_Int   found, dummy1, dummy2;
2300         FT_UInt  strid = ~0U;
2301 
2302 
2303         /* the default instance is missing in array the   */
2304         /* of named instances; try to synthesize an entry */
2305         found = sfnt->get_name_id( face,
2306                                    TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY,
2307                                    &dummy1,
2308                                    &dummy2 );
2309         if ( found )
2310           strid = TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY;
2311         else
2312         {
2313           found = sfnt->get_name_id( face,
2314                                      TT_NAME_ID_FONT_SUBFAMILY,
2315                                      &dummy1,
2316                                      &dummy2 );
2317           if ( found )
2318             strid = TT_NAME_ID_FONT_SUBFAMILY;
2319         }
2320 
2321         if ( found )
2322         {
2323           found = sfnt->get_name_id( face,
2324                                      TT_NAME_ID_PS_NAME,
2325                                      &dummy1,
2326                                      &dummy2 );
2327           if ( found )
2328           {
2329             FT_TRACE5(( "TT_Get_MM_Var:"
2330                         " Adding default instance to named instances\n" ));
2331 
2332             ns = &mmvar->namedstyle[fvar_head.instanceCount];
2333 
2334             ns->strid = strid;
2335             ns->psid  = TT_NAME_ID_PS_NAME;
2336 
2337             a = mmvar->axis;
2338             c = ns->coords;
2339             for ( j = 0; j < num_axes; j++, a++, c++ )
2340               *c = a->def;
2341           }
2342         }
2343       }
2344 
2345       ft_var_load_mvar( face );
2346     }
2347 
2348     /* fill the output array if requested */
2349 
2350     if ( master )
2351     {
2352       FT_UInt  n;
2353 
2354 
2355       if ( FT_ALLOC( mmvar, face->blend->mmvar_len ) )
2356         goto Exit;
2357       FT_MEM_COPY( mmvar, face->blend->mmvar, face->blend->mmvar_len );
2358 
2359       axis_flags =
2360         (FT_UShort*)( (char*)mmvar + mmvar_size );
2361       mmvar->axis =
2362         (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
2363       mmvar->namedstyle =
2364         (FT_Var_Named_Style*)( (char*)mmvar->axis+ axis_size );
2365 
2366       next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
2367                                  namedstyle_size );
2368       for ( n = 0; n < mmvar->num_namedstyles; n++ )
2369       {
2370         mmvar->namedstyle[n].coords  = next_coords;
2371         next_coords                 += num_axes;
2372       }
2373 
2374       a         = mmvar->axis;
2375       next_name = (FT_String*)( (char*)mmvar->namedstyle +
2376                                 namedstyle_size + next_coords_size );
2377       for ( n = 0; n < num_axes; n++ )
2378       {
2379         a->name = next_name;
2380 
2381         /* standard PostScript names for some standard apple tags */
2382         if ( a->tag == TTAG_wght )
2383           a->name = (char*)"Weight";
2384         else if ( a->tag == TTAG_wdth )
2385           a->name = (char*)"Width";
2386         else if ( a->tag == TTAG_opsz )
2387           a->name = (char*)"OpticalSize";
2388         else if ( a->tag == TTAG_slnt )
2389           a->name = (char*)"Slant";
2390 
2391         next_name += 5;
2392         a++;
2393       }
2394 
2395       *master = mmvar;
2396     }
2397 
2398   Exit:
2399     return error;
2400   }
2401 
2402 
2403   static FT_Error
2404   tt_set_mm_blend( TT_Face    face,
2405                    FT_UInt    num_coords,
2406                    FT_Fixed*  coords,
2407                    FT_Bool    set_design_coords )
2408   {
2409     FT_Error    error = FT_Err_Ok;
2410     GX_Blend    blend;
2411     FT_MM_Var*  mmvar;
2412     FT_UInt     i;
2413 
2414     FT_Bool     all_design_coords = FALSE;
2415 
2416     FT_Memory   memory = face->root.memory;
2417 
2418     enum
2419     {
2420       mcvt_retain,
2421       mcvt_modify,
2422       mcvt_load
2423 
2424     } manageCvt;
2425 
2426 
2427     face->doblend = FALSE;
2428 
2429     if ( !face->blend )
2430     {
2431       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2432         goto Exit;
2433     }
2434 
2435     blend = face->blend;
2436     mmvar = blend->mmvar;
2437 
2438     if ( num_coords > mmvar->num_axis )
2439     {
2440       FT_TRACE2(( "TT_Set_MM_Blend:"
2441                   " only using first %d of %d coordinates\n",
2442                   mmvar->num_axis, num_coords ));
2443       num_coords = mmvar->num_axis;
2444     }
2445 
2446     FT_TRACE5(( "TT_Set_MM_Blend:\n"
2447                 "  normalized design coordinates:\n" ));
2448 
2449     for ( i = 0; i < num_coords; i++ )
2450     {
2451       FT_TRACE5(( "    %.5f\n", coords[i] / 65536.0 ));
2452       if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
2453       {
2454         FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n"
2455                     "                 is out of range [-1;1]\n",
2456                     coords[i] / 65536.0 ));
2457         error = FT_THROW( Invalid_Argument );
2458         goto Exit;
2459       }
2460     }
2461 
2462     FT_TRACE5(( "\n" ));
2463 
2464     if ( !face->is_cff2 && !blend->glyphoffsets )
2465       if ( FT_SET_ERROR( ft_var_load_gvar( face ) ) )
2466         goto Exit;
2467 
2468     if ( !blend->coords )
2469     {
2470       if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
2471         goto Exit;
2472 
2473       /* the first time we have to compute all design coordinates */
2474       all_design_coords = TRUE;
2475     }
2476 
2477     if ( !blend->normalizedcoords )
2478     {
2479       if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) )
2480         goto Exit;
2481 
2482       manageCvt = mcvt_modify;
2483 
2484       /* If we have not set the blend coordinates before this, then the  */
2485       /* cvt table will still be what we read from the `cvt ' table and  */
2486       /* we don't need to reload it.  We may need to change it though... */
2487     }
2488     else
2489     {
2490       FT_Bool    have_diff = 0;
2491       FT_UInt    j;
2492       FT_Fixed*  c;
2493       FT_Fixed*  n;
2494 
2495 
2496       manageCvt = mcvt_retain;
2497 
2498       for ( i = 0; i < num_coords; i++ )
2499       {
2500         if ( blend->normalizedcoords[i] != coords[i] )
2501         {
2502           manageCvt = mcvt_load;
2503           have_diff = 1;
2504           break;
2505         }
2506       }
2507 
2508       if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2509       {
2510         FT_UInt  idx = (FT_UInt)face->root.face_index >> 16;
2511 
2512 
2513         c = blend->normalizedcoords + i;
2514         n = blend->normalized_stylecoords + idx * mmvar->num_axis + i;
2515         for ( j = i; j < mmvar->num_axis; j++, n++, c++ )
2516           if ( *c != *n )
2517             have_diff = 1;
2518       }
2519       else
2520       {
2521         c = blend->normalizedcoords + i;
2522         for ( j = i; j < mmvar->num_axis; j++, c++ )
2523           if ( *c != 0 )
2524             have_diff = 1;
2525       }
2526 
2527       /* return value -1 indicates `no change' */
2528       if ( !have_diff )
2529         return -1;
2530 
2531       for ( ; i < mmvar->num_axis; i++ )
2532       {
2533         if ( blend->normalizedcoords[i] != 0 )
2534         {
2535           manageCvt = mcvt_load;
2536           break;
2537         }
2538       }
2539 
2540       /* If we don't change the blend coords then we don't need to do  */
2541       /* anything to the cvt table.  It will be correct.  Otherwise we */
2542       /* no longer have the original cvt (it was modified when we set  */
2543       /* the blend last time), so we must reload and then modify it.   */
2544     }
2545 
2546     blend->num_axis = mmvar->num_axis;
2547     FT_MEM_COPY( blend->normalizedcoords,
2548                  coords,
2549                  num_coords * sizeof ( FT_Fixed ) );
2550 
2551     if ( set_design_coords )
2552       ft_var_to_design( face,
2553                         all_design_coords ? blend->num_axis : num_coords,
2554                         blend->normalizedcoords,
2555                         blend->coords );
2556 
2557     face->doblend = TRUE;
2558 
2559     if ( face->cvt )
2560     {
2561       switch ( manageCvt )
2562       {
2563       case mcvt_load:
2564         /* The cvt table has been loaded already; every time we change the */
2565         /* blend we may need to reload and remodify the cvt table.         */
2566         FT_FREE( face->cvt );
2567         face->cvt = NULL;
2568 
2569         error = tt_face_load_cvt( face, face->root.stream );
2570         break;
2571 
2572       case mcvt_modify:
2573         /* The original cvt table is in memory.  All we need to do is */
2574         /* apply the `cvar' table (if any).                           */
2575         error = tt_face_vary_cvt( face, face->root.stream );
2576         break;
2577 
2578       case mcvt_retain:
2579         /* The cvt table is correct for this set of coordinates. */
2580         break;
2581       }
2582     }
2583 
2584     /* enforce recomputation of the PostScript name; */
2585     FT_FREE( face->postscript_name );
2586     face->postscript_name = NULL;
2587 
2588   Exit:
2589     return error;
2590   }
2591 
2592 
2593   /*************************************************************************/
2594   /*                                                                       */
2595   /* <Function>                                                            */
2596   /*    TT_Set_MM_Blend                                                    */
2597   /*                                                                       */
2598   /* <Description>                                                         */
2599   /*    Set the blend (normalized) coordinates for this instance of the    */
2600   /*    font.  Check that the `gvar' table is reasonable and does some     */
2601   /*    initial preparation.                                               */
2602   /*                                                                       */
2603   /* <InOut>                                                               */
2604   /*    face       :: The font.                                            */
2605   /*                  Initialize the blend structure with `gvar' data.     */
2606   /*                                                                       */
2607   /* <Input>                                                               */
2608   /*    num_coords :: The number of available coordinates.  If it is       */
2609   /*                  larger than the number of axes, ignore the excess    */
2610   /*                  values.  If it is smaller than the number of axes,   */
2611   /*                  use the default value (0) for the remaining axes.    */
2612   /*                                                                       */
2613   /*    coords     :: An array of `num_coords', each between [-1,1].       */
2614   /*                                                                       */
2615   /* <Return>                                                              */
2616   /*    FreeType error code.  0 means success.                             */
2617   /*                                                                       */
2618   FT_LOCAL_DEF( FT_Error )
2619   TT_Set_MM_Blend( TT_Face    face,
2620                    FT_UInt    num_coords,
2621                    FT_Fixed*  coords )
2622   {
2623     FT_Error  error;
2624 
2625 
2626     error = tt_set_mm_blend( face, num_coords, coords, 1 );
2627     if ( error )
2628       return error;
2629 
2630     if ( num_coords )
2631       face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2632     else
2633       face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2634 
2635     return FT_Err_Ok;
2636   }
2637 
2638 
2639   /*************************************************************************/
2640   /*                                                                       */
2641   /* <Function>                                                            */
2642   /*    TT_Get_MM_Blend                                                    */
2643   /*                                                                       */
2644   /* <Description>                                                         */
2645   /*    Get the blend (normalized) coordinates for this instance of the    */
2646   /*    font.                                                              */
2647   /*                                                                       */
2648   /* <InOut>                                                               */
2649   /*    face       :: The font.                                            */
2650   /*                  Initialize the blend structure with `gvar' data.     */
2651   /*                                                                       */
2652   /* <Input>                                                               */
2653   /*    num_coords :: The number of available coordinates.  If it is       */
2654   /*                  larger than the number of axes, set the excess       */
2655   /*                  values to 0.                                         */
2656   /*                                                                       */
2657   /*    coords     :: An array of `num_coords', each between [-1,1].       */
2658   /*                                                                       */
2659   /* <Return>                                                              */
2660   /*    FreeType error code.  0 means success.                             */
2661   /*                                                                       */
2662   FT_LOCAL_DEF( FT_Error )
2663   TT_Get_MM_Blend( TT_Face    face,
2664                    FT_UInt    num_coords,
2665                    FT_Fixed*  coords )
2666   {
2667     FT_Error  error = FT_Err_Ok;
2668     GX_Blend  blend;
2669     FT_UInt   i, nc;
2670 
2671 
2672     if ( !face->blend )
2673     {
2674       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2675         return error;
2676     }
2677 
2678     blend = face->blend;
2679 
2680     if ( !blend->coords )
2681     {
2682       /* select default instance coordinates */
2683       /* if no instance is selected yet      */
2684       if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
2685         return error;
2686     }
2687 
2688     nc = num_coords;
2689     if ( num_coords > blend->num_axis )
2690     {
2691       FT_TRACE2(( "TT_Get_MM_Blend:"
2692                   " only using first %d of %d coordinates\n",
2693                   blend->num_axis, num_coords ));
2694       nc = blend->num_axis;
2695     }
2696 
2697     if ( face->doblend )
2698     {
2699       for ( i = 0; i < nc; i++ )
2700         coords[i] = blend->normalizedcoords[i];
2701     }
2702     else
2703     {
2704       for ( i = 0; i < nc; i++ )
2705         coords[i] = 0;
2706     }
2707 
2708     for ( ; i < num_coords; i++ )
2709       coords[i] = 0;
2710 
2711     return FT_Err_Ok;
2712   }
2713 
2714 
2715   /*************************************************************************/
2716   /*                                                                       */
2717   /* <Function>                                                            */
2718   /*    TT_Set_Var_Design                                                  */
2719   /*                                                                       */
2720   /* <Description>                                                         */
2721   /*    Set the coordinates for the instance, measured in the user         */
2722   /*    coordinate system.  Parse the `avar' table (if present) to convert */
2723   /*    from user to normalized coordinates.                               */
2724   /*                                                                       */
2725   /* <InOut>                                                               */
2726   /*    face       :: The font face.                                       */
2727   /*                  Initialize the blend struct with `gvar' data.        */
2728   /*                                                                       */
2729   /* <Input>                                                               */
2730   /*    num_coords :: The number of available coordinates.  If it is       */
2731   /*                  larger than the number of axes, ignore the excess    */
2732   /*                  values.  If it is smaller than the number of axes,   */
2733   /*                  use the default values for the remaining axes.       */
2734   /*                                                                       */
2735   /*    coords     :: A coordinate array with `num_coords' elements.       */
2736   /*                                                                       */
2737   /* <Return>                                                              */
2738   /*    FreeType error code.  0 means success.                             */
2739   /*                                                                       */
2740   FT_LOCAL_DEF( FT_Error )
2741   TT_Set_Var_Design( TT_Face    face,
2742                      FT_UInt    num_coords,
2743                      FT_Fixed*  coords )
2744   {
2745     FT_Error    error  = FT_Err_Ok;
2746     GX_Blend    blend;
2747     FT_MM_Var*  mmvar;
2748     FT_UInt     i;
2749     FT_Memory   memory = face->root.memory;
2750 
2751     FT_Fixed*  c;
2752     FT_Fixed*  n;
2753     FT_Fixed*  normalized = NULL;
2754 
2755     FT_Bool  have_diff = 0;
2756 
2757 
2758     if ( !face->blend )
2759     {
2760       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2761         goto Exit;
2762     }
2763 
2764     blend = face->blend;
2765     mmvar = blend->mmvar;
2766 
2767     if ( num_coords > mmvar->num_axis )
2768     {
2769       FT_TRACE2(( "TT_Set_Var_Design:"
2770                   " only using first %d of %d coordinates\n",
2771                   mmvar->num_axis, num_coords ));
2772       num_coords = mmvar->num_axis;
2773     }
2774 
2775     if ( !blend->coords )
2776     {
2777       if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
2778         goto Exit;
2779     }
2780 
2781     c = blend->coords;
2782     n = coords;
2783     for ( i = 0; i < num_coords; i++, n++, c++ )
2784     {
2785       if ( *c != *n )
2786       {
2787         *c        = *n;
2788         have_diff = 1;
2789       }
2790     }
2791 
2792     if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
2793     {
2794       FT_UInt              instance_index;
2795       FT_Var_Named_Style*  named_style;
2796 
2797 
2798       instance_index = (FT_UInt)face->root.face_index >> 16;
2799       named_style    = mmvar->namedstyle + instance_index - 1;
2800 
2801       n = named_style->coords + num_coords;
2802       for ( ; i < mmvar->num_axis; i++, n++, c++ )
2803       {
2804         if ( *c != *n )
2805         {
2806           *c        = *n;
2807           have_diff = 1;
2808         }
2809       }
2810     }
2811     else
2812     {
2813       FT_Var_Axis*  a;
2814 
2815 
2816       a = mmvar->axis + num_coords;
2817       for ( ; i < mmvar->num_axis; i++, a++, c++ )
2818       {
2819         if ( *c != a->def )
2820         {
2821           *c        = a->def;
2822           have_diff = 1;
2823         }
2824       }
2825     }
2826 
2827     /* return value -1 indicates `no change';                      */
2828     /* we can exit early if `normalizedcoords' is already computed */
2829     if ( blend->normalizedcoords && !have_diff )
2830       return -1;
2831 
2832     if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
2833       goto Exit;
2834 
2835     if ( !face->blend->avar_loaded )
2836       ft_var_load_avar( face );
2837 
2838     FT_TRACE5(( "TT_Set_Var_Design:\n"
2839                 "  normalized design coordinates:\n" ));
2840     ft_var_to_normalized( face, num_coords, blend->coords, normalized );
2841 
2842     error = tt_set_mm_blend( face, mmvar->num_axis, normalized, 0 );
2843     if ( error )
2844       goto Exit;
2845 
2846     if ( num_coords )
2847       face->root.face_flags |= FT_FACE_FLAG_VARIATION;
2848     else
2849       face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
2850 
2851   Exit:
2852     FT_FREE( normalized );
2853     return error;
2854   }
2855 
2856 
2857   /*************************************************************************/
2858   /*                                                                       */
2859   /* <Function>                                                            */
2860   /*    TT_Get_Var_Design                                                  */
2861   /*                                                                       */
2862   /* <Description>                                                         */
2863   /*    Get the design coordinates of the currently selected interpolated  */
2864   /*    font.                                                              */
2865   /*                                                                       */
2866   /* <Input>                                                               */
2867   /*    face       :: A handle to the source face.                         */
2868   /*                                                                       */
2869   /*    num_coords :: The number of design coordinates to retrieve.  If it */
2870   /*                  is larger than the number of axes, set the excess    */
2871   /*                  values to~0.                                         */
2872   /*                                                                       */
2873   /* <Output>                                                              */
2874   /*    coords     :: The design coordinates array.                        */
2875   /*                                                                       */
2876   /* <Return>                                                              */
2877   /*    FreeType error code.  0~means success.                             */
2878   /*                                                                       */
2879   FT_LOCAL_DEF( FT_Error )
2880   TT_Get_Var_Design( TT_Face    face,
2881                      FT_UInt    num_coords,
2882                      FT_Fixed*  coords )
2883   {
2884     FT_Error  error = FT_Err_Ok;
2885     GX_Blend  blend;
2886     FT_UInt   i, nc;
2887 
2888 
2889     if ( !face->blend )
2890     {
2891       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2892         return error;
2893     }
2894 
2895     blend = face->blend;
2896 
2897     if ( !blend->coords )
2898     {
2899       /* select default instance coordinates */
2900       /* if no instance is selected yet      */
2901       if ( FT_SET_ERROR( tt_set_mm_blend( face, 0, NULL, 1 ) ) )
2902         return error;
2903     }
2904 
2905     nc = num_coords;
2906     if ( num_coords > blend->num_axis )
2907     {
2908       FT_TRACE2(( "TT_Get_Var_Design:"
2909                   " only using first %d of %d coordinates\n",
2910                   blend->num_axis, num_coords ));
2911       nc = blend->num_axis;
2912     }
2913 
2914     if ( face->doblend )
2915     {
2916       for ( i = 0; i < nc; i++ )
2917         coords[i] = blend->coords[i];
2918     }
2919     else
2920     {
2921       for ( i = 0; i < nc; i++ )
2922         coords[i] = 0;
2923     }
2924 
2925     for ( ; i < num_coords; i++ )
2926       coords[i] = 0;
2927 
2928     return FT_Err_Ok;
2929   }
2930 
2931 
2932   /*************************************************************************/
2933   /*                                                                       */
2934   /* <Function>                                                            */
2935   /*    TT_Set_Named_Instance                                              */
2936   /*                                                                       */
2937   /* <Description>                                                         */
2938   /*    Set the given named instance, also resetting any further           */
2939   /*    variation.                                                         */
2940   /*                                                                       */
2941   /* <Input>                                                               */
2942   /*    face           :: A handle to the source face.                     */
2943   /*                                                                       */
2944   /*    instance_index :: The instance index, starting with value 1.       */
2945   /*                      Value 0 indicates to not use an instance.        */
2946   /*                                                                       */
2947   /* <Return>                                                              */
2948   /*    FreeType error code.  0~means success.                             */
2949   /*                                                                       */
2950   FT_LOCAL_DEF( FT_Error )
2951   TT_Set_Named_Instance( TT_Face  face,
2952                          FT_UInt  instance_index )
2953   {
2954     FT_Error    error = FT_ERR( Invalid_Argument );
2955     GX_Blend    blend;
2956     FT_MM_Var*  mmvar;
2957 
2958     FT_UInt  num_instances;
2959 
2960 
2961     if ( !face->blend )
2962     {
2963       if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
2964         goto Exit;
2965     }
2966 
2967     blend = face->blend;
2968     mmvar = blend->mmvar;
2969 
2970     num_instances = (FT_UInt)face->root.style_flags >> 16;
2971 
2972     /* `instance_index' starts with value 1, thus `>' */
2973     if ( instance_index > num_instances )
2974       goto Exit;
2975 
2976     if ( instance_index > 0 && mmvar->namedstyle )
2977     {
2978       FT_Memory     memory = face->root.memory;
2979       SFNT_Service  sfnt   = (SFNT_Service)face->sfnt;
2980 
2981       FT_Var_Named_Style*  named_style;
2982       FT_String*           style_name;
2983 
2984 
2985       named_style = mmvar->namedstyle + instance_index - 1;
2986 
2987       error = sfnt->get_name( face,
2988                               (FT_UShort)named_style->strid,
2989                               &style_name );
2990       if ( error )
2991         goto Exit;
2992 
2993       /* set (or replace) style name */
2994       FT_FREE( face->root.style_name );
2995       face->root.style_name = style_name;
2996 
2997       /* finally, select the named instance */
2998       error = TT_Set_Var_Design( face,
2999                                  mmvar->num_axis,
3000                                  named_style->coords );
3001       if ( error )
3002         goto Exit;
3003     }
3004     else
3005       error = TT_Set_Var_Design( face, 0, NULL );
3006 
3007     face->root.face_index  = ( instance_index << 16 )             |
3008                              ( face->root.face_index & 0xFFFFL );
3009     face->root.face_flags &= ~FT_FACE_FLAG_VARIATION;
3010 
3011   Exit:
3012     return error;
3013   }
3014 
3015 
3016   /*************************************************************************/
3017   /*************************************************************************/
3018   /*****                                                               *****/
3019   /*****                     GX VAR PARSING ROUTINES                   *****/
3020   /*****                                                               *****/
3021   /*************************************************************************/
3022   /*************************************************************************/
3023 
3024 
3025   /*************************************************************************/
3026   /*                                                                       */
3027   /* <Function>                                                            */
3028   /*    tt_face_vary_cvt                                                   */
3029   /*                                                                       */
3030   /* <Description>                                                         */
3031   /*    Modify the loaded cvt table according to the `cvar' table and the  */
3032   /*    font's blend.                                                      */
3033   /*                                                                       */
3034   /* <InOut>                                                               */
3035   /*    face   :: A handle to the target face object.                      */
3036   /*                                                                       */
3037   /* <Input>                                                               */
3038   /*    stream :: A handle to the input stream.                            */
3039   /*                                                                       */
3040   /* <Return>                                                              */
3041   /*    FreeType error code.  0 means success.                             */
3042   /*                                                                       */
3043   /*    Most errors are ignored.  It is perfectly valid not to have a      */
3044   /*    `cvar' table even if there is a `gvar' and `fvar' table.           */
3045   /*                                                                       */
3046   FT_LOCAL_DEF( FT_Error )
3047   tt_face_vary_cvt( TT_Face    face,
3048                     FT_Stream  stream )
3049   {
3050     FT_Error    error;
3051     FT_Memory   memory = stream->memory;
3052     FT_ULong    table_start;
3053     FT_ULong    table_len;
3054     FT_UInt     tupleCount;
3055     FT_ULong    offsetToData;
3056     FT_ULong    here;
3057     FT_UInt     i, j;
3058     FT_Fixed*   tuple_coords    = NULL;
3059     FT_Fixed*   im_start_coords = NULL;
3060     FT_Fixed*   im_end_coords   = NULL;
3061     GX_Blend    blend           = face->blend;
3062     FT_UInt     point_count, spoint_count = 0;
3063     FT_UShort*  sharedpoints = NULL;
3064     FT_UShort*  localpoints  = NULL;
3065     FT_UShort*  points;
3066     FT_Short*   deltas;
3067 
3068 
3069     FT_TRACE2(( "CVAR " ));
3070 
3071     if ( !blend )
3072     {
3073       FT_TRACE2(( "\n"
3074                   "tt_face_vary_cvt: no blend specified\n" ));
3075       error = FT_Err_Ok;
3076       goto Exit;
3077     }
3078 
3079     if ( !face->cvt )
3080     {
3081       FT_TRACE2(( "\n"
3082                   "tt_face_vary_cvt: no `cvt ' table\n" ));
3083       error = FT_Err_Ok;
3084       goto Exit;
3085     }
3086 
3087     error = face->goto_table( face, TTAG_cvar, stream, &table_len );
3088     if ( error )
3089     {
3090       FT_TRACE2(( "is missing\n" ));
3091 
3092       error = FT_Err_Ok;
3093       goto Exit;
3094     }
3095 
3096     if ( FT_FRAME_ENTER( table_len ) )
3097     {
3098       error = FT_Err_Ok;
3099       goto Exit;
3100     }
3101 
3102     table_start = FT_Stream_FTell( stream );
3103     if ( FT_GET_LONG() != 0x00010000L )
3104     {
3105       FT_TRACE2(( "bad table version\n" ));
3106 
3107       error = FT_Err_Ok;
3108       goto FExit;
3109     }
3110 
3111     FT_TRACE2(( "loaded\n" ));
3112 
3113     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
3114          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3115          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
3116       goto FExit;
3117 
3118     tupleCount   = FT_GET_USHORT();
3119     offsetToData = FT_GET_USHORT();
3120 
3121     /* rough sanity test */
3122     if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
3123            table_len )
3124     {
3125       FT_TRACE2(( "tt_face_vary_cvt:"
3126                   " invalid CVT variation array header\n" ));
3127 
3128       error = FT_THROW( Invalid_Table );
3129       goto FExit;
3130     }
3131 
3132     offsetToData += table_start;
3133 
3134     if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3135     {
3136       here = FT_Stream_FTell( stream );
3137 
3138       FT_Stream_SeekSet( stream, offsetToData );
3139 
3140       sharedpoints = ft_var_readpackedpoints( stream,
3141                                               table_len,
3142                                               &spoint_count );
3143       offsetToData = FT_Stream_FTell( stream );
3144 
3145       FT_Stream_SeekSet( stream, here );
3146     }
3147 
3148     FT_TRACE5(( "cvar: there %s %d tuple%s:\n",
3149                 ( tupleCount & 0xFFF ) == 1 ? "is" : "are",
3150                 tupleCount & 0xFFF,
3151                 ( tupleCount & 0xFFF ) == 1 ? "" : "s" ));
3152 
3153     for ( i = 0; i < ( tupleCount & 0xFFF ); i++ )
3154     {
3155       FT_UInt   tupleDataSize;
3156       FT_UInt   tupleIndex;
3157       FT_Fixed  apply;
3158 
3159 
3160       FT_TRACE6(( "  tuple %d:\n", i ));
3161 
3162       tupleDataSize = FT_GET_USHORT();
3163       tupleIndex    = FT_GET_USHORT();
3164 
3165       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3166       {
3167         for ( j = 0; j < blend->num_axis; j++ )
3168           tuple_coords[j] = FT_GET_SHORT() * 4;  /* convert from        */
3169                                                  /* short frac to fixed */
3170       }
3171       else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3172       {
3173         FT_TRACE2(( "tt_face_vary_cvt:"
3174                     " invalid tuple index\n" ));
3175 
3176         error = FT_THROW( Invalid_Table );
3177         goto Exit;
3178       }
3179       else
3180         FT_MEM_COPY(
3181           tuple_coords,
3182           &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis],
3183           blend->num_axis * sizeof ( FT_Fixed ) );
3184 
3185       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3186       {
3187         for ( j = 0; j < blend->num_axis; j++ )
3188           im_start_coords[j] = FT_GET_SHORT() * 4;
3189         for ( j = 0; j < blend->num_axis; j++ )
3190           im_end_coords[j] = FT_GET_SHORT() * 4;
3191       }
3192 
3193       apply = ft_var_apply_tuple( blend,
3194                                   (FT_UShort)tupleIndex,
3195                                   tuple_coords,
3196                                   im_start_coords,
3197                                   im_end_coords );
3198 
3199       if ( apply == 0 )              /* tuple isn't active for our blend */
3200       {
3201         offsetToData += tupleDataSize;
3202         continue;
3203       }
3204 
3205       here = FT_Stream_FTell( stream );
3206 
3207       FT_Stream_SeekSet( stream, offsetToData );
3208 
3209       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
3210       {
3211         localpoints = ft_var_readpackedpoints( stream,
3212                                                table_len,
3213                                                &point_count );
3214         points      = localpoints;
3215       }
3216       else
3217       {
3218         points      = sharedpoints;
3219         point_count = spoint_count;
3220       }
3221 
3222       deltas = ft_var_readpackeddeltas( stream,
3223                                         table_len,
3224                                         point_count == 0 ? face->cvt_size
3225                                                          : point_count );
3226 
3227       if ( !points                                                        ||
3228            !deltas                                                        ||
3229            ( localpoints == ALL_POINTS && point_count != face->cvt_size ) )
3230         ; /* failure, ignore it */
3231 
3232       else if ( localpoints == ALL_POINTS )
3233       {
3234 #ifdef FT_DEBUG_LEVEL_TRACE
3235         int  count = 0;
3236 #endif
3237 
3238 
3239         FT_TRACE7(( "    CVT deltas:\n" ));
3240 
3241         /* this means that there are deltas for every entry in cvt */
3242         for ( j = 0; j < face->cvt_size; j++ )
3243         {
3244           FT_Long  orig_cvt = face->cvt[j];
3245 
3246 
3247           face->cvt[j] = (FT_Short)( orig_cvt +
3248                                      FT_MulFix( deltas[j], apply ) );
3249 
3250 #ifdef FT_DEBUG_LEVEL_TRACE
3251           if ( orig_cvt != face->cvt[j] )
3252           {
3253             FT_TRACE7(( "      %d: %d -> %d\n",
3254                         j, orig_cvt, face->cvt[j] ));
3255             count++;
3256           }
3257 #endif
3258         }
3259 
3260 #ifdef FT_DEBUG_LEVEL_TRACE
3261         if ( !count )
3262           FT_TRACE7(( "      none\n" ));
3263 #endif
3264       }
3265 
3266       else
3267       {
3268 #ifdef FT_DEBUG_LEVEL_TRACE
3269         int  count = 0;
3270 #endif
3271 
3272 
3273         FT_TRACE7(( "    CVT deltas:\n" ));
3274 
3275         for ( j = 0; j < point_count; j++ )
3276         {
3277           int      pindex;
3278           FT_Long  orig_cvt;
3279 
3280 
3281           pindex = points[j];
3282           if ( (FT_ULong)pindex >= face->cvt_size )
3283             continue;
3284 
3285           orig_cvt          = face->cvt[pindex];
3286           face->cvt[pindex] = (FT_Short)( orig_cvt +
3287                                           FT_MulFix( deltas[j], apply ) );
3288 
3289 #ifdef FT_DEBUG_LEVEL_TRACE
3290           if ( orig_cvt != face->cvt[pindex] )
3291           {
3292             FT_TRACE7(( "      %d: %d -> %d\n",
3293                         pindex, orig_cvt, face->cvt[pindex] ));
3294             count++;
3295           }
3296 #endif
3297         }
3298 
3299 #ifdef FT_DEBUG_LEVEL_TRACE
3300         if ( !count )
3301           FT_TRACE7(( "      none\n" ));
3302 #endif
3303       }
3304 
3305       if ( localpoints != ALL_POINTS )
3306         FT_FREE( localpoints );
3307       FT_FREE( deltas );
3308 
3309       offsetToData += tupleDataSize;
3310 
3311       FT_Stream_SeekSet( stream, here );
3312     }
3313 
3314     FT_TRACE5(( "\n" ));
3315 
3316   FExit:
3317     FT_FRAME_EXIT();
3318 
3319   Exit:
3320     if ( sharedpoints != ALL_POINTS )
3321       FT_FREE( sharedpoints );
3322     FT_FREE( tuple_coords );
3323     FT_FREE( im_start_coords );
3324     FT_FREE( im_end_coords );
3325 
3326     return error;
3327   }
3328 
3329 
3330   /* Shift the original coordinates of all points between indices `p1' */
3331   /* and `p2', using the same difference as given by index `ref'.      */
3332 
3333   /* modeled after `af_iup_shift' */
3334 
3335   static void
3336   tt_delta_shift( int         p1,
3337                   int         p2,
3338                   int         ref,
3339                   FT_Vector*  in_points,
3340                   FT_Vector*  out_points )
3341   {
3342     int        p;
3343     FT_Vector  delta;
3344 
3345 
3346     delta.x = out_points[ref].x - in_points[ref].x;
3347     delta.y = out_points[ref].y - in_points[ref].y;
3348 
3349     if ( delta.x == 0 && delta.y == 0 )
3350       return;
3351 
3352     for ( p = p1; p < ref; p++ )
3353     {
3354       out_points[p].x += delta.x;
3355       out_points[p].y += delta.y;
3356     }
3357 
3358     for ( p = ref + 1; p <= p2; p++ )
3359     {
3360       out_points[p].x += delta.x;
3361       out_points[p].y += delta.y;
3362     }
3363   }
3364 
3365 
3366   /* Interpolate the original coordinates of all points with indices */
3367   /* between `p1' and `p2', using `ref1' and `ref2' as the reference */
3368   /* point indices.                                                  */
3369 
3370   /* modeled after `af_iup_interp', `_iup_worker_interpolate', and */
3371   /* `Ins_IUP'                                                     */
3372 
3373   static void
3374   tt_delta_interpolate( int         p1,
3375                         int         p2,
3376                         int         ref1,
3377                         int         ref2,
3378                         FT_Vector*  in_points,
3379                         FT_Vector*  out_points )
3380   {
3381     int  p, i;
3382 
3383     FT_Pos  out, in1, in2, out1, out2, d1, d2;
3384 
3385 
3386     if ( p1 > p2 )
3387       return;
3388 
3389     /* handle both horizontal and vertical coordinates */
3390     for ( i = 0; i <= 1; i++ )
3391     {
3392       /* shift array pointers so that we can access `foo.y' as `foo.x' */
3393       in_points  = (FT_Vector*)( (FT_Pos*)in_points + i );
3394       out_points = (FT_Vector*)( (FT_Pos*)out_points + i );
3395 
3396       if ( in_points[ref1].x > in_points[ref2].x )
3397       {
3398         p    = ref1;
3399         ref1 = ref2;
3400         ref2 = p;
3401       }
3402 
3403       in1  = in_points[ref1].x;
3404       in2  = in_points[ref2].x;
3405       out1 = out_points[ref1].x;
3406       out2 = out_points[ref2].x;
3407       d1   = out1 - in1;
3408       d2   = out2 - in2;
3409 
3410       /* If the reference points have the same coordinate but different */
3411       /* delta, inferred delta is zero.  Otherwise interpolate.         */
3412       if ( in1 != in2 || out1 == out2 )
3413       {
3414         FT_Fixed  scale = in1 != in2 ? FT_DivFix( out2 - out1, in2 - in1 )
3415                                      : 0;
3416 
3417 
3418         for ( p = p1; p <= p2; p++ )
3419         {
3420           out = in_points[p].x;
3421 
3422           if ( out <= in1 )
3423             out += d1;
3424           else if ( out >= in2 )
3425             out += d2;
3426           else
3427             out = out1 + FT_MulFix( out - in1, scale );
3428 
3429           out_points[p].x = out;
3430         }
3431       }
3432     }
3433   }
3434 
3435 
3436   /* Interpolate points without delta values, similar to */
3437   /* the `IUP' hinting instruction.                      */
3438 
3439   /* modeled after `Ins_IUP */
3440 
3441   static void
3442   tt_interpolate_deltas( FT_Outline*  outline,
3443                          FT_Vector*   out_points,
3444                          FT_Vector*   in_points,
3445                          FT_Bool*     has_delta )
3446   {
3447     FT_Int  first_point;
3448     FT_Int  end_point;
3449 
3450     FT_Int  first_delta;
3451     FT_Int  cur_delta;
3452 
3453     FT_Int    point;
3454     FT_Short  contour;
3455 
3456 
3457     /* ignore empty outlines */
3458     if ( !outline->n_contours )
3459       return;
3460 
3461     contour = 0;
3462     point   = 0;
3463 
3464     do
3465     {
3466       end_point   = outline->contours[contour];
3467       first_point = point;
3468 
3469       /* search first point that has a delta */
3470       while ( point <= end_point && !has_delta[point] )
3471         point++;
3472 
3473       if ( point <= end_point )
3474       {
3475         first_delta = point;
3476         cur_delta   = point;
3477 
3478         point++;
3479 
3480         while ( point <= end_point )
3481         {
3482           /* search next point that has a delta  */
3483           /* and interpolate intermediate points */
3484           if ( has_delta[point] )
3485           {
3486             tt_delta_interpolate( cur_delta + 1,
3487                                   point - 1,
3488                                   cur_delta,
3489                                   point,
3490                                   in_points,
3491                                   out_points );
3492             cur_delta = point;
3493           }
3494 
3495           point++;
3496         }
3497 
3498         /* shift contour if we only have a single delta */
3499         if ( cur_delta == first_delta )
3500           tt_delta_shift( first_point,
3501                           end_point,
3502                           cur_delta,
3503                           in_points,
3504                           out_points );
3505         else
3506         {
3507           /* otherwise handle remaining points       */
3508           /* at the end and beginning of the contour */
3509           tt_delta_interpolate( cur_delta + 1,
3510                                 end_point,
3511                                 cur_delta,
3512                                 first_delta,
3513                                 in_points,
3514                                 out_points );
3515 
3516           if ( first_delta > 0 )
3517             tt_delta_interpolate( first_point,
3518                                   first_delta - 1,
3519                                   cur_delta,
3520                                   first_delta,
3521                                   in_points,
3522                                   out_points );
3523         }
3524       }
3525       contour++;
3526 
3527     } while ( contour < outline->n_contours );
3528   }
3529 
3530 
3531   /*************************************************************************/
3532   /*                                                                       */
3533   /* <Function>                                                            */
3534   /*    TT_Vary_Apply_Glyph_Deltas                                         */
3535   /*                                                                       */
3536   /* <Description>                                                         */
3537   /*    Apply the appropriate deltas to the current glyph.                 */
3538   /*                                                                       */
3539   /* <Input>                                                               */
3540   /*    face        :: A handle to the target face object.                 */
3541   /*                                                                       */
3542   /*    glyph_index :: The index of the glyph being modified.              */
3543   /*                                                                       */
3544   /*    n_points    :: The number of the points in the glyph, including    */
3545   /*                   phantom points.                                     */
3546   /*                                                                       */
3547   /* <InOut>                                                               */
3548   /*    outline     :: The outline to change.                              */
3549   /*                                                                       */
3550   /* <Return>                                                              */
3551   /*    FreeType error code.  0 means success.                             */
3552   /*                                                                       */
3553   FT_LOCAL_DEF( FT_Error )
3554   TT_Vary_Apply_Glyph_Deltas( TT_Face      face,
3555                               FT_UInt      glyph_index,
3556                               FT_Outline*  outline,
3557                               FT_UInt      n_points )
3558   {
3559     FT_Stream   stream = face->root.stream;
3560     FT_Memory   memory = stream->memory;
3561     GX_Blend    blend  = face->blend;
3562 
3563     FT_Vector*  points_org = NULL;
3564     FT_Vector*  points_out = NULL;
3565     FT_Bool*    has_delta  = NULL;
3566 
3567     FT_Error    error;
3568     FT_ULong    glyph_start;
3569     FT_UInt     tupleCount;
3570     FT_ULong    offsetToData;
3571     FT_ULong    here;
3572     FT_UInt     i, j;
3573     FT_Fixed*   tuple_coords    = NULL;
3574     FT_Fixed*   im_start_coords = NULL;
3575     FT_Fixed*   im_end_coords   = NULL;
3576     FT_UInt     point_count, spoint_count = 0;
3577     FT_UShort*  sharedpoints = NULL;
3578     FT_UShort*  localpoints  = NULL;
3579     FT_UShort*  points;
3580     FT_Short    *deltas_x, *deltas_y;
3581 
3582 
3583     if ( !face->doblend || !blend )
3584       return FT_THROW( Invalid_Argument );
3585 
3586     if ( glyph_index >= blend->gv_glyphcnt      ||
3587          blend->glyphoffsets[glyph_index] ==
3588            blend->glyphoffsets[glyph_index + 1] )
3589     {
3590       FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3591                   " no variation data for this glyph\n" ));
3592       return FT_Err_Ok;
3593     }
3594 
3595     if ( FT_NEW_ARRAY( points_org, n_points ) ||
3596          FT_NEW_ARRAY( points_out, n_points ) ||
3597          FT_NEW_ARRAY( has_delta, n_points )  )
3598       goto Fail1;
3599 
3600     if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] )   ||
3601          FT_FRAME_ENTER( blend->glyphoffsets[glyph_index + 1] -
3602                            blend->glyphoffsets[glyph_index] ) )
3603       goto Fail1;
3604 
3605     glyph_start = FT_Stream_FTell( stream );
3606 
3607     /* each set of glyph variation data is formatted similarly to `cvar' */
3608 
3609     if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
3610          FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
3611          FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
3612       goto Fail2;
3613 
3614     tupleCount   = FT_GET_USHORT();
3615     offsetToData = FT_GET_USHORT();
3616 
3617     /* rough sanity test */
3618     if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
3619            blend->gvar_size )
3620     {
3621       FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3622                   " invalid glyph variation array header\n" ));
3623 
3624       error = FT_THROW( Invalid_Table );
3625       goto Fail2;
3626     }
3627 
3628     offsetToData += glyph_start;
3629 
3630     if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
3631     {
3632       here = FT_Stream_FTell( stream );
3633 
3634       FT_Stream_SeekSet( stream, offsetToData );
3635 
3636       sharedpoints = ft_var_readpackedpoints( stream,
3637                                               blend->gvar_size,
3638                                               &spoint_count );
3639       offsetToData = FT_Stream_FTell( stream );
3640 
3641       FT_Stream_SeekSet( stream, here );
3642     }
3643 
3644     FT_TRACE5(( "gvar: there %s %d tuple%s:\n",
3645                 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
3646                 tupleCount & GX_TC_TUPLE_COUNT_MASK,
3647                 ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
3648 
3649     for ( j = 0; j < n_points; j++ )
3650       points_org[j] = outline->points[j];
3651 
3652     for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
3653     {
3654       FT_UInt   tupleDataSize;
3655       FT_UInt   tupleIndex;
3656       FT_Fixed  apply;
3657 
3658 
3659       FT_TRACE6(( "  tuple %d:\n", i ));
3660 
3661       tupleDataSize = FT_GET_USHORT();
3662       tupleIndex    = FT_GET_USHORT();
3663 
3664       if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
3665       {
3666         for ( j = 0; j < blend->num_axis; j++ )
3667           tuple_coords[j] = FT_GET_SHORT() * 4;   /* convert from        */
3668                                                   /* short frac to fixed */
3669       }
3670       else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
3671       {
3672         FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
3673                     " invalid tuple index\n" ));
3674 
3675         error = FT_THROW( Invalid_Table );
3676         goto Fail2;
3677       }
3678       else
3679         FT_MEM_COPY(
3680           tuple_coords,
3681           &blend->tuplecoords[( tupleIndex & 0xFFF ) * blend->num_axis],
3682           blend->num_axis * sizeof ( FT_Fixed ) );
3683 
3684       if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
3685       {
3686         for ( j = 0; j < blend->num_axis; j++ )
3687           im_start_coords[j] = FT_GET_SHORT() * 4;
3688         for ( j = 0; j < blend->num_axis; j++ )
3689           im_end_coords[j] = FT_GET_SHORT() * 4;
3690       }
3691 
3692       apply = ft_var_apply_tuple( blend,
3693                                   (FT_UShort)tupleIndex,
3694                                   tuple_coords,
3695                                   im_start_coords,
3696                                   im_end_coords );
3697 
3698       if ( apply == 0 )              /* tuple isn't active for our blend */
3699       {
3700         offsetToData += tupleDataSize;
3701         continue;
3702       }
3703 
3704       here = FT_Stream_FTell( stream );
3705 
3706       FT_Stream_SeekSet( stream, offsetToData );
3707 
3708       if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
3709       {
3710         localpoints = ft_var_readpackedpoints( stream,
3711                                                blend->gvar_size,
3712                                                &point_count );
3713         points      = localpoints;
3714       }
3715       else
3716       {
3717         points      = sharedpoints;
3718         point_count = spoint_count;
3719       }
3720 
3721       deltas_x = ft_var_readpackeddeltas( stream,
3722                                           blend->gvar_size,
3723                                           point_count == 0 ? n_points
3724                                                            : point_count );
3725       deltas_y = ft_var_readpackeddeltas( stream,
3726                                           blend->gvar_size,
3727                                           point_count == 0 ? n_points
3728                                                            : point_count );
3729 
3730       if ( !points || !deltas_y || !deltas_x )
3731         ; /* failure, ignore it */
3732 
3733       else if ( points == ALL_POINTS )
3734       {
3735 #ifdef FT_DEBUG_LEVEL_TRACE
3736         int  count = 0;
3737 #endif
3738 
3739 
3740         FT_TRACE7(( "    point deltas:\n" ));
3741 
3742         /* this means that there are deltas for every point in the glyph */
3743         for ( j = 0; j < n_points; j++ )
3744         {
3745           FT_Pos  delta_x = FT_MulFix( deltas_x[j], apply );
3746           FT_Pos  delta_y = FT_MulFix( deltas_y[j], apply );
3747 
3748 
3749           if ( j < n_points - 4 )
3750           {
3751             outline->points[j].x += delta_x;
3752             outline->points[j].y += delta_y;
3753           }
3754           else
3755           {
3756             /* To avoid double adjustment of advance width or height, */
3757             /* adjust phantom points only if there is no HVAR or VVAR */
3758             /* support, respectively.                                 */
3759             if ( j == ( n_points - 4 )        &&
3760                  !( face->variation_support &
3761                     TT_FACE_FLAG_VAR_LSB    ) )
3762               outline->points[j].x += delta_x;
3763 
3764             else if ( j == ( n_points - 3 )          &&
3765                       !( face->variation_support   &
3766                          TT_FACE_FLAG_VAR_HADVANCE ) )
3767               outline->points[j].x += delta_x;
3768 
3769             else if ( j == ( n_points - 2 )        &&
3770                       !( face->variation_support &
3771                          TT_FACE_FLAG_VAR_TSB    ) )
3772               outline->points[j].y += delta_y;
3773 
3774             else if ( j == ( n_points - 1 )          &&
3775                       !( face->variation_support   &
3776                          TT_FACE_FLAG_VAR_VADVANCE ) )
3777               outline->points[j].y += delta_y;
3778           }
3779 
3780 #ifdef FT_DEBUG_LEVEL_TRACE
3781           if ( delta_x || delta_y )
3782           {
3783             FT_TRACE7(( "      %d: (%d, %d) -> (%d, %d)\n",
3784                         j,
3785                         outline->points[j].x - delta_x,
3786                         outline->points[j].y - delta_y,
3787                         outline->points[j].x,
3788                         outline->points[j].y ));
3789             count++;
3790           }
3791 #endif
3792         }
3793 
3794 #ifdef FT_DEBUG_LEVEL_TRACE
3795         if ( !count )
3796           FT_TRACE7(( "      none\n" ));
3797 #endif
3798       }
3799 
3800       else
3801       {
3802 #ifdef FT_DEBUG_LEVEL_TRACE
3803         int  count = 0;
3804 #endif
3805 
3806 
3807         /* we have to interpolate the missing deltas similar to the */
3808         /* IUP bytecode instruction                                 */
3809         for ( j = 0; j < n_points; j++ )
3810         {
3811           has_delta[j]  = FALSE;
3812           points_out[j] = points_org[j];
3813         }
3814 
3815         for ( j = 0; j < point_count; j++ )
3816         {
3817           FT_UShort  idx = points[j];
3818 
3819 
3820           if ( idx >= n_points )
3821             continue;
3822 
3823           has_delta[idx] = TRUE;
3824 
3825           points_out[idx].x += FT_MulFix( deltas_x[j], apply );
3826           points_out[idx].y += FT_MulFix( deltas_y[j], apply );
3827         }
3828 
3829         /* no need to handle phantom points here,      */
3830         /* since solitary points can't be interpolated */
3831         tt_interpolate_deltas( outline,
3832                                points_out,
3833                                points_org,
3834                                has_delta );
3835 
3836         FT_TRACE7(( "    point deltas:\n" ));
3837 
3838         for ( j = 0; j < n_points; j++ )
3839         {
3840           FT_Pos  delta_x = points_out[j].x - points_org[j].x;
3841           FT_Pos  delta_y = points_out[j].y - points_org[j].y;
3842 
3843 
3844           if ( j < n_points - 4 )
3845           {
3846             outline->points[j].x += delta_x;
3847             outline->points[j].y += delta_y;
3848           }
3849           else
3850           {
3851             /* To avoid double adjustment of advance width or height, */
3852             /* adjust phantom points only if there is no HVAR or VVAR */
3853             /* support, respectively.                                 */
3854             if ( j == ( n_points - 4 )        &&
3855                  !( face->variation_support &
3856                     TT_FACE_FLAG_VAR_LSB    ) )
3857               outline->points[j].x += delta_x;
3858 
3859             else if ( j == ( n_points - 3 )          &&
3860                       !( face->variation_support   &
3861                          TT_FACE_FLAG_VAR_HADVANCE ) )
3862               outline->points[j].x += delta_x;
3863 
3864             else if ( j == ( n_points - 2 )        &&
3865                       !( face->variation_support &
3866                          TT_FACE_FLAG_VAR_TSB    ) )
3867               outline->points[j].y += delta_y;
3868 
3869             else if ( j == ( n_points - 1 )          &&
3870                       !( face->variation_support   &
3871                          TT_FACE_FLAG_VAR_VADVANCE ) )
3872               outline->points[j].y += delta_y;
3873           }
3874 
3875 #ifdef FT_DEBUG_LEVEL_TRACE
3876           if ( delta_x || delta_y )
3877           {
3878             FT_TRACE7(( "      %d: (%d, %d) -> (%d, %d)\n",
3879                         j,
3880                         outline->points[j].x - delta_x,
3881                         outline->points[j].y - delta_y,
3882                         outline->points[j].x,
3883                         outline->points[j].y ));
3884             count++;
3885           }
3886 #endif
3887         }
3888 
3889 #ifdef FT_DEBUG_LEVEL_TRACE
3890         if ( !count )
3891           FT_TRACE7(( "      none\n" ));
3892 #endif
3893       }
3894 
3895       if ( localpoints != ALL_POINTS )
3896         FT_FREE( localpoints );
3897       FT_FREE( deltas_x );
3898       FT_FREE( deltas_y );
3899 
3900       offsetToData += tupleDataSize;
3901 
3902       FT_Stream_SeekSet( stream, here );
3903     }
3904 
3905     FT_TRACE5(( "\n" ));
3906 
3907   Fail2:
3908     if ( sharedpoints != ALL_POINTS )
3909       FT_FREE( sharedpoints );
3910     FT_FREE( tuple_coords );
3911     FT_FREE( im_start_coords );
3912     FT_FREE( im_end_coords );
3913 
3914     FT_FRAME_EXIT();
3915 
3916   Fail1:
3917     FT_FREE( points_org );
3918     FT_FREE( points_out );
3919     FT_FREE( has_delta );
3920 
3921     return error;
3922   }
3923 
3924 
3925   /*************************************************************************/
3926   /*                                                                       */
3927   /* <Function>                                                            */
3928   /*    tt_get_var_blend                                                   */
3929   /*                                                                       */
3930   /* <Description>                                                         */
3931   /*    An extended internal version of `TT_Get_MM_Blend' that returns     */
3932   /*    pointers instead of copying data, without any initialization of    */
3933   /*    the MM machinery in case it isn't loaded yet.                      */
3934   /*                                                                       */
3935   FT_LOCAL_DEF( FT_Error )
3936   tt_get_var_blend( TT_Face      face,
3937                     FT_UInt     *num_coords,
3938                     FT_Fixed*   *coords,
3939                     FT_Fixed*   *normalizedcoords,
3940                     FT_MM_Var*  *mm_var )
3941   {
3942     if ( face->blend )
3943     {
3944       if ( num_coords )
3945         *num_coords       = face->blend->num_axis;
3946       if ( coords )
3947         *coords           = face->blend->coords;
3948       if ( normalizedcoords )
3949         *normalizedcoords = face->blend->normalizedcoords;
3950       if ( mm_var )
3951         *mm_var           = face->blend->mmvar;
3952     }
3953     else
3954     {
3955       if ( num_coords )
3956         *num_coords = 0;
3957       if ( coords )
3958         *coords     = NULL;
3959       if ( mm_var )
3960         *mm_var     = NULL;
3961     }
3962 
3963     return FT_Err_Ok;
3964   }
3965 
3966 
3967   static void
3968   ft_var_done_item_variation_store( TT_Face          face,
3969                                     GX_ItemVarStore  itemStore )
3970   {
3971     FT_Memory  memory = FT_FACE_MEMORY( face );
3972     FT_UInt    i;
3973 
3974 
3975     if ( itemStore->varData )
3976     {
3977       for ( i = 0; i < itemStore->dataCount; i++ )
3978       {
3979         FT_FREE( itemStore->varData[i].regionIndices );
3980         FT_FREE( itemStore->varData[i].deltaSet );
3981       }
3982 
3983       FT_FREE( itemStore->varData );
3984     }
3985 
3986     if ( itemStore->varRegionList )
3987     {
3988       for ( i = 0; i < itemStore->regionCount; i++ )
3989         FT_FREE( itemStore->varRegionList[i].axisList );
3990 
3991       FT_FREE( itemStore->varRegionList );
3992     }
3993   }
3994 
3995 
3996   /*************************************************************************/
3997   /*                                                                       */
3998   /* <Function>                                                            */
3999   /*    tt_done_blend                                                      */
4000   /*                                                                       */
4001   /* <Description>                                                         */
4002   /*    Free the blend internal data structure.                            */
4003   /*                                                                       */
4004   FT_LOCAL_DEF( void )
4005   tt_done_blend( TT_Face  face )
4006   {
4007     FT_Memory  memory = FT_FACE_MEMORY( face );
4008     GX_Blend   blend  = face->blend;
4009 
4010 
4011     if ( blend )
4012     {
4013       FT_UInt  i, num_axes;
4014 
4015 
4016       /* blend->num_axis might not be set up yet */
4017       num_axes = blend->mmvar->num_axis;
4018 
4019       FT_FREE( blend->coords );
4020       FT_FREE( blend->normalizedcoords );
4021       FT_FREE( blend->normalized_stylecoords );
4022       FT_FREE( blend->mmvar );
4023 
4024       if ( blend->avar_segment )
4025       {
4026         for ( i = 0; i < num_axes; i++ )
4027           FT_FREE( blend->avar_segment[i].correspondence );
4028         FT_FREE( blend->avar_segment );
4029       }
4030 
4031       if ( blend->hvar_table )
4032       {
4033         ft_var_done_item_variation_store( face,
4034                                           &blend->hvar_table->itemStore );
4035 
4036         FT_FREE( blend->hvar_table->widthMap.innerIndex );
4037         FT_FREE( blend->hvar_table->widthMap.outerIndex );
4038         FT_FREE( blend->hvar_table );
4039       }
4040 
4041       if ( blend->vvar_table )
4042       {
4043         ft_var_done_item_variation_store( face,
4044                                           &blend->vvar_table->itemStore );
4045 
4046         FT_FREE( blend->vvar_table->widthMap.innerIndex );
4047         FT_FREE( blend->vvar_table->widthMap.outerIndex );
4048         FT_FREE( blend->vvar_table );
4049       }
4050 
4051       if ( blend->mvar_table )
4052       {
4053         ft_var_done_item_variation_store( face,
4054                                           &blend->mvar_table->itemStore );
4055 
4056         FT_FREE( blend->mvar_table->values );
4057         FT_FREE( blend->mvar_table );
4058       }
4059 
4060       FT_FREE( blend->tuplecoords );
4061       FT_FREE( blend->glyphoffsets );
4062       FT_FREE( blend );
4063     }
4064   }
4065 
4066 #else /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
4067 
4068   /* ANSI C doesn't like empty source files */
4069   typedef int  _tt_gxvar_dummy;
4070 
4071 #endif /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
4072 
4073 
4074 /* END */