freetype-commit
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[freetype2] OpenType-1.8 d1908a1: Preliminary OpenType 1.8 support.


From: Werner LEMBERG
Subject: [freetype2] OpenType-1.8 d1908a1: Preliminary OpenType 1.8 support.
Date: Tue, 13 Sep 2016 23:22:16 +0000 (UTC)

branch: OpenType-1.8
commit d1908a107d46388bc5ddcd657fe739181f73a3ac
Author: Dave Arnold <address@hidden>
Commit: Werner Lemberg <address@hidden>

    Preliminary OpenType 1.8 support.
---
 README                                    |    9 +
 include/freetype/ftmm.h                   |   10 +
 include/freetype/internal/fttrace.h       |    1 +
 include/freetype/internal/services/svmm.h |   25 +-
 include/freetype/internal/tttypes.h       |    1 +
 include/freetype/tttags.h                 |    2 +
 src/base/ftmm.c                           |   55 +++
 src/cff/cf2fixed.h                        |    4 +-
 src/cff/cf2font.c                         |  192 ++++++++++-
 src/cff/cf2font.h                         |   10 +-
 src/cff/cf2ft.c                           |   41 ++-
 src/cff/cf2ft.h                           |    8 +
 src/cff/cf2intrp.c                        |   40 ++-
 src/cff/cf2stack.c                        |   28 ++
 src/cff/cf2stack.h                        |    9 +
 src/cff/cffdrivr.c                        |   32 +-
 src/cff/cffload.c                         |  176 +++++++++-
 src/cff/cffload.h                         |    3 +-
 src/cff/cffobjs.c                         |   23 +-
 src/cff/cffparse.c                        |   50 ++-
 src/cff/cffparse.h                        |    3 +-
 src/cff/cffpic.h                          |    1 +
 src/cff/cfftoken.h                        |    4 +
 src/cff/cfftypes.h                        |   39 +++
 src/sfnt/sfobjs.c                         |    9 +-
 src/sfnt/ttmtx.c                          |   10 +
 src/truetype/ttdriver.c                   |    4 +-
 src/truetype/ttgxvar.c                    |  537 ++++++++++++++++++++++++++++-
 src/truetype/ttgxvar.h                    |   82 ++++-
 29 files changed, 1364 insertions(+), 44 deletions(-)

diff --git a/README b/README
index 23c10ab..1a975e6 100644
--- a/README
+++ b/README
@@ -1,3 +1,12 @@
+  Branch of FreeType to support OpenType 1.8
+  ==========================================
+
+  This branch contains changes for supporting OpenType 1.8.
+  The changes will be merged back upstream in September 2016,
+  when the specification for OpenType 1.8 is final and has
+  been published.
+
+
   FreeType 2.6.5
   ==============
 
diff --git a/include/freetype/ftmm.h b/include/freetype/ftmm.h
index 6c05f0c..c447f1c 100644
--- a/include/freetype/ftmm.h
+++ b/include/freetype/ftmm.h
@@ -171,6 +171,7 @@ FT_BEGIN_HEADER
   {
     FT_Fixed*  coords;
     FT_UInt    strid;
+    FT_UInt    psid;
 
   } FT_Var_Named_Style;
 
@@ -329,6 +330,10 @@ FT_BEGIN_HEADER
                                  FT_UInt    num_coords,
                                  FT_Fixed*  coords );
 
+  FT_EXPORT( FT_Error )
+  FT_Get_Var_Design_Coordinates( FT_Face    face,
+                                 FT_UInt    num_coords,
+                                 FT_Fixed*  coords );
 
   /*************************************************************************/
   /*                                                                       */
@@ -375,6 +380,11 @@ FT_BEGIN_HEADER
 
   /* */
 
+  FT_EXPORT( FT_Error )
+  FT_Get_Var_Blend_Coordinates( FT_Face    face,
+                                FT_UInt    num_coords,
+                                FT_Fixed*  coords );
+
 
 FT_END_HEADER
 
diff --git a/include/freetype/internal/fttrace.h 
b/include/freetype/internal/fttrace.h
index efb3355..54ca595 100644
--- a/include/freetype/internal/fttrace.h
+++ b/include/freetype/internal/fttrace.h
@@ -90,6 +90,7 @@ FT_TRACE_DEF( cffparse )
 
 FT_TRACE_DEF( cf2blues )
 FT_TRACE_DEF( cf2hints )
+FT_TRACE_DEF( cf2font )
 FT_TRACE_DEF( cf2interp )
 
   /* Type 42 driver component */
diff --git a/include/freetype/internal/services/svmm.h 
b/include/freetype/internal/services/svmm.h
index b78a19f..6a1254d 100644
--- a/include/freetype/internal/services/svmm.h
+++ b/include/freetype/internal/services/svmm.h
@@ -58,6 +58,16 @@ FT_BEGIN_HEADER
                            FT_UInt   num_coords,
                            FT_Long*  coords );
 
+  typedef FT_Error
+  (*FT_Get_Var_Design_Func)( FT_Face    face,
+                             FT_UInt    num_coords,
+                             FT_Fixed*  coords );
+
+  typedef FT_Error
+  (*FT_Get_Var_Blend_Func)( FT_Face    face,
+                            FT_UInt    num_coords,
+                            FT_Fixed*  coords );
+
 
   FT_DEFINE_SERVICE( MultiMasters )
   {
@@ -66,6 +76,8 @@ FT_BEGIN_HEADER
     FT_Set_MM_Blend_Func    set_mm_blend;
     FT_Get_MM_Var_Func      get_mm_var;
     FT_Set_Var_Design_Func  set_var_design;
+    FT_Get_Var_Design_Func  get_var_design;
+    FT_Get_Var_Blend_Func   get_var_blend;
   };
 
 
@@ -76,10 +88,13 @@ FT_BEGIN_HEADER
                                            set_mm_design_,                  \
                                            set_mm_blend_,                   \
                                            get_mm_var_,                     \
-                                           set_var_design_ )                \
+                                           set_var_design_,                 \
+                                           get_var_design_,                 \
+                                          get_var_blend_ )                 \
   static const FT_Service_MultiMastersRec  class_ =                         \
   {                                                                         \
-    get_mm_, set_mm_design_, set_mm_blend_, get_mm_var_, set_var_design_    \
+    get_mm_, set_mm_design_, set_mm_blend_, get_mm_var_, set_var_design_,   \
+    get_var_design_, get_var_blend_                                         \
   };
 
 #else /* FT_CONFIG_OPTION_PIC */
@@ -89,7 +104,9 @@ FT_BEGIN_HEADER
                                            set_mm_design_,                  \
                                            set_mm_blend_,                   \
                                            get_mm_var_,                     \
-                                           set_var_design_ )                \
+                                           set_var_design_,                 \
+                                           get_var_design_,                 \
+                                          get_var_blend_ )                 \
   void                                                                      \
   FT_Init_Class_ ## class_( FT_Service_MultiMastersRec*  clazz )            \
   {                                                                         \
@@ -98,6 +115,8 @@ FT_BEGIN_HEADER
     clazz->set_mm_blend   = set_mm_blend_;                                  \
     clazz->get_mm_var     = get_mm_var_;                                    \
     clazz->set_var_design = set_var_design_;                                \
+    clazz->get_var_design = get_var_design_;                                \
+    clazz->get_var_blend  = get_var_blend_;                                 \
   }
 
 #endif /* FT_CONFIG_OPTION_PIC */
diff --git a/include/freetype/internal/tttypes.h 
b/include/freetype/internal/tttypes.h
index 4110d50..efade95 100644
--- a/include/freetype/internal/tttypes.h
+++ b/include/freetype/internal/tttypes.h
@@ -1346,6 +1346,7 @@ FT_BEGIN_HEADER
     FT_ULong              glyf_len;
 
 #ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+    FT_Bool               isCFF2;
     FT_Bool               doblend;
     GX_Blend              blend;
 #endif
diff --git a/include/freetype/tttags.h b/include/freetype/tttags.h
index f3c9aa5..49eecd2 100644
--- a/include/freetype/tttags.h
+++ b/include/freetype/tttags.h
@@ -43,6 +43,7 @@ FT_BEGIN_HEADER
 #define TTAG_CBDT  FT_MAKE_TAG( 'C', 'B', 'D', 'T' )
 #define TTAG_CBLC  FT_MAKE_TAG( 'C', 'B', 'L', 'C' )
 #define TTAG_CFF   FT_MAKE_TAG( 'C', 'F', 'F', ' ' )
+#define TTAG_CFF2  FT_MAKE_TAG( 'C', 'F', 'F', '2' )
 #define TTAG_CID   FT_MAKE_TAG( 'C', 'I', 'D', ' ' )
 #define TTAG_cmap  FT_MAKE_TAG( 'c', 'm', 'a', 'p' )
 #define TTAG_cvar  FT_MAKE_TAG( 'c', 'v', 'a', 'r' )
@@ -61,6 +62,7 @@ FT_BEGIN_HEADER
 #define TTAG_GPOS  FT_MAKE_TAG( 'G', 'P', 'O', 'S' )
 #define TTAG_GSUB  FT_MAKE_TAG( 'G', 'S', 'U', 'B' )
 #define TTAG_gvar  FT_MAKE_TAG( 'g', 'v', 'a', 'r' )
+#define TTAG_HVAR  FT_MAKE_TAG( 'H', 'V', 'A', 'R' )
 #define TTAG_hdmx  FT_MAKE_TAG( 'h', 'd', 'm', 'x' )
 #define TTAG_head  FT_MAKE_TAG( 'h', 'e', 'a', 'd' )
 #define TTAG_hhea  FT_MAKE_TAG( 'h', 'h', 'e', 'a' )
diff --git a/src/base/ftmm.c b/src/base/ftmm.c
index 6b759ca..b6bc3c5 100644
--- a/src/base/ftmm.c
+++ b/src/base/ftmm.c
@@ -175,6 +175,34 @@
   /* documentation is in ftmm.h */
 
   FT_EXPORT_DEF( FT_Error )
+  FT_Get_Var_Design_Coordinates( FT_Face    face,
+                                 FT_UInt    num_coords,
+                                 FT_Fixed*  coords )
+  {
+    FT_Error                 error;
+    FT_Service_MultiMasters  service;
+
+
+    /* check of `face' delayed to `ft_face_get_mm_service' */
+
+    if ( !coords )
+      return FT_THROW( Invalid_Argument );
+
+    error = ft_face_get_mm_service( face, &service );
+    if ( !error )
+    {
+      error = FT_ERR( Invalid_Argument );
+      if ( service->get_var_design )
+        error = service->get_var_design( face, num_coords, coords );
+    }
+
+    return error;
+  }
+
+
+  /* documentation is in ftmm.h */
+
+  FT_EXPORT_DEF( FT_Error )
   FT_Set_MM_Blend_Coordinates( FT_Face    face,
                                FT_UInt    num_coords,
                                FT_Fixed*  coords )
@@ -230,5 +258,32 @@
     return error;
   }
 
+  /* documentation is in ftmm.h */
+
+  FT_EXPORT_DEF( FT_Error )
+  FT_Get_Var_Blend_Coordinates( FT_Face    face,
+                                FT_UInt    num_coords,
+                                FT_Fixed*  coords )
+  {
+    FT_Error                 error;
+    FT_Service_MultiMasters  service;
+
+
+    /* check of `face' delayed to `ft_face_get_mm_service' */
+
+    if ( !coords )
+      return FT_THROW( Invalid_Argument );
+
+    error = ft_face_get_mm_service( face, &service );
+    if ( !error )
+    {
+      error = FT_ERR( Invalid_Argument );
+      if ( service->get_var_blend )
+         error = service->get_var_blend( face, num_coords, coords );
+    }
+
+    return error;
+  }
+
 
 /* END */
diff --git a/src/cff/cf2fixed.h b/src/cff/cf2fixed.h
index 74af377..2e4b503 100644
--- a/src/cff/cf2fixed.h
+++ b/src/cff/cf2fixed.h
@@ -51,8 +51,8 @@ FT_BEGIN_HEADER
 
 #define CF2_FIXED_MAX      ( (CF2_Fixed)0x7FFFFFFFL )
 #define CF2_FIXED_MIN      ( (CF2_Fixed)0x80000000L )
-#define CF2_FIXED_ONE      0x10000L
-#define CF2_FIXED_EPSILON  0x0001
+#define CF2_FIXED_ONE      ( (CF2_Fixed)0x10000L )
+#define CF2_FIXED_EPSILON  ( (CF2_Fixed)0x0001 )
 
   /* in C 89, left and right shift of negative numbers is  */
   /* implementation specific behaviour in the general case */
diff --git a/src/cff/cf2font.c b/src/cff/cf2font.c
index 83fd348..9fd1531 100644
--- a/src/cff/cf2font.c
+++ b/src/cff/cf2font.c
@@ -38,6 +38,7 @@
 
 #include <ft2build.h>
 #include FT_INTERNAL_CALC_H
+#include FT_INTERNAL_DEBUG_H
 
 #include "cf2ft.h"
 
@@ -46,6 +47,8 @@
 #include "cf2error.h"
 #include "cf2intrp.h"
 
+#undef  FT_COMPONENT
+#define FT_COMPONENT  trace_cf2font
 
   /* Compute a stem darkening amount in character space. */
   static void
@@ -233,25 +236,155 @@
     *darkenAmount += boldenAmount / 2;
   }
 
+  /* compute a blend vector from variation store index and normalized vector  
*/
+  /* return size of blend vector and allocated storage for it                 
*/
+  /* caller must free this                                                    
*/
+  /* lenNormalizedVector == 0 produces a default blend vector                 
*/
+  /* Note: normalizedVector uses FT_Fixed, not CF2_Fixed                      
*/
+  static void
+  cf2_buildBlendVector( CF2_Font font, CF2_UInt vsindex,
+                        CF2_UInt lenNormalizedVector, FT_Fixed * 
normalizedVector,
+                        CF2_UInt * lenBlendVector, CF2_Fixed ** blendVector )
+  {
+    FT_Error   error  = FT_Err_Ok;        /* for FT_REALLOC */
+    FT_Memory  memory = font->memory;     /* for FT_REALLOC */
+    CF2_UInt len;
+    CFF_VStore vs;
+    CFF_VarData* varData;
+    CF2_UInt master;
+
+    FT_UNUSED( lenNormalizedVector );
+    FT_UNUSED( vsindex );
+
+    FT_ASSERT( lenBlendVector && blendVector );
+    FT_ASSERT( lenNormalizedVector == 0 || normalizedVector );
+    FT_TRACE4(( "cf2_buildBlendVector\n" ));
+
+    vs = cf2_getVStore( font->decoder );
+
+    /* VStore and fvar must be consistent */
+    if ( lenNormalizedVector != 0 && lenNormalizedVector != vs->axisCount )
+    {
+      FT_TRACE4(( "cf2_buildBlendVector: Axis count mismatch\n" ));
+      CF2_SET_ERROR( &font->error, Invalid_File_Format );
+      goto Exit;
+    }
+    if ( vsindex >= vs->dataCount )
+    {
+      FT_TRACE4(( "cf2_buildBlendVector: vsindex out of range\n" ));
+      CF2_SET_ERROR( &font->error, Invalid_File_Format );
+      goto Exit;
+    }
+
+    /* select the item variation data structure */
+    varData = &vs->varData[vsindex];
+
+    /* prepare an output buffer for the blend vector */
+    len = varData->regionIdxCount + 1;    /* add 1 for default */
+    if ( FT_REALLOC( *blendVector, *lenBlendVector, len * sizeof( 
**blendVector )) )
+      return;
+    *lenBlendVector = len;
+
+    /* outer loop steps through master designs to be blended */
+    for ( master=0; master<len; master++ )
+    {
+      CF2_UInt j;
+      CF2_UInt idx;
+      CFF_VarRegion* varRegion;
+
+      /* default factor is always one */
+      if ( master == 0 )
+      {
+        *blendVector[master] = CF2_FIXED_ONE;
+        FT_TRACE4(( "blend vector len %d\n [ %f ", len, 
(double)(*blendVector)[master] / 65536 ));
+        continue;
+      }
+
+      /* VStore array does not include default master, so subtract one */
+      idx = varData->regionIndices[master-1];
+      varRegion = &vs->varRegionList[idx];
+
+      if ( idx >= vs->regionCount )
+      {
+        FT_TRACE4(( "cf2_buildBlendVector: region index out of range\n" ));
+        CF2_SET_ERROR( &font->error, Invalid_File_Format );
+        goto Exit;
+      }
+
+      /* Note: lenNormalizedVector could be zero      */
+      /* In that case, build default blend vector     */
+      if ( lenNormalizedVector != 0 )
+        (*blendVector)[master] = CF2_FIXED_ONE; /* default */
+
+      /* inner loop steps through axes in this region */
+      for ( j=0; j<lenNormalizedVector; j++ )
+      {
+        CFF_AxisCoords* axis = &varRegion->axisList[j];
+        CF2_Fixed axisScalar;
+
+        /* compute the scalar contribution of this axis */
+        /* ignore invalid ranges */
+        if ( axis->startCoord > axis->peakCoord || axis->peakCoord > 
axis->endCoord )
+          axisScalar = CF2_FIXED_ONE;
+        else if ( axis->startCoord < 0 && axis->endCoord > 0 && 
axis->peakCoord != 0 )
+          axisScalar = CF2_FIXED_ONE;
+        /* peak of 0 means ignore this axis */
+        else if ( axis->peakCoord == 0 )
+          axisScalar = CF2_FIXED_ONE;
+        /* ignore this region if coords are out of range */
+        else if ( normalizedVector[j] < axis->startCoord || 
normalizedVector[j] > axis->endCoord )
+          axisScalar = 0;
+        /* calculate a proportional factor */
+        else
+        {
+          if ( normalizedVector[j] == axis->peakCoord )
+            axisScalar = CF2_FIXED_ONE;
+          else if ( normalizedVector[j] < axis->peakCoord )
+            axisScalar = FT_DivFix( normalizedVector[j] - axis->startCoord,
+                                    axis->peakCoord - axis->startCoord );
+          else
+            axisScalar = FT_DivFix( axis->endCoord - normalizedVector[j],
+                                    axis->endCoord - axis->peakCoord );
+        }
+        /* take product of all the axis scalars */
+        (*blendVector)[master] = FT_MulFix( (*blendVector)[master], axisScalar 
);
+      }
+      FT_TRACE4(( ", %f ", (double)(*blendVector)[master] / 65536 ));
+    }
+    FT_TRACE4(( "]\n" ));
+
+  Exit:
+    return;
+ }
+
 
   /* set up values for the current FontDict and matrix */
+  /* called for each glyph to be rendered */
 
   /* caller's transform is adjusted for subpixel positioning */
   static void
   cf2_font_setup( CF2_Font           font,
                   const CF2_Matrix*  transform )
   {
+    FT_Error   error  = FT_Err_Ok;        /* for FT_REALLOC */
+    FT_Memory  memory = font->memory;     /* for FT_REALLOC */
+
     /* pointer to parsed font object */
     CFF_Decoder*  decoder = font->decoder;
 
     FT_Bool  needExtraSetup = FALSE;
 
+    CFF_VStoreRec* vstore;
+    FT_Bool  hasVariations = FALSE;
+
     /* character space units */
     CF2_Fixed  boldenX = font->syntheticEmboldeningAmountX;
     CF2_Fixed  boldenY = font->syntheticEmboldeningAmountY;
 
     CFF_SubFont  subFont;
     CF2_Fixed    ppem;
+    CF2_UInt     lenNormalizedV = 0;
+    FT_Fixed *   normalizedV = NULL;
 
 
     /* clear previous error */
@@ -266,6 +399,33 @@
       needExtraSetup    = TRUE;
     }
 
+    /* check for variation vectors */
+    vstore = cf2_getVStore( decoder );
+    hasVariations = ( vstore->dataCount != 0 );
+
+    if ( hasVariations )
+    {
+      if ( font->lenBlendVector == 0 )
+        needExtraSetup = TRUE;      /* a blend vector is required */
+
+      /* Note: lenNormalizedVector is zero until FT_Get_MM_Var() is called */
+      cf2_getNormalizedVector( decoder, &lenNormalizedV, &normalizedV );
+
+      /* determine if blend vector needs to be recomputed */
+      if ( font->lastVsindex != subFont->font_dict.vsindex ||
+        lenNormalizedV == 0 ||
+        font->lenNormalizedVector != lenNormalizedV ||
+        ( lenNormalizedV &&
+          memcmp( normalizedV,
+                  &font->lastNormalizedVector,
+                  lenNormalizedV * sizeof( *normalizedV ) ) != 0 ) )
+      {
+        font->lastVsindex = subFont->font_dict.vsindex;
+        /* vectors will be copied below, during ExtraSetup */
+        needExtraSetup = TRUE;
+      }
+    }
+
     /* if ppem has changed, we need to recompute some cached data         */
     /* note: because of CID font matrix concatenation, ppem and transform */
     /*       do not necessarily track.                                    */
@@ -423,7 +583,37 @@
 
       /* compute blue zones for this instance */
       cf2_blues_init( &font->blues, font );
-    }
+
+      /* copy normalized vector used to compute blend vector */
+      if ( hasVariations )
+      {
+        /* if user has set a normalized vector, use it */
+        /* otherwise, assume default */
+        if ( lenNormalizedV != 0 )
+        {
+          /* user has set a normalized vector */
+          if ( FT_REALLOC( font->lastNormalizedVector,
+                           font->lenNormalizedVector,
+                           lenNormalizedV * sizeof( *normalizedV )) )
+          {
+            CF2_SET_ERROR( &font->error, Out_Of_Memory );
+            return;
+          }
+          font->lenNormalizedVector = lenNormalizedV;
+          FT_MEM_COPY( font->lastNormalizedVector,
+                       normalizedV,
+                       lenNormalizedV * sizeof( *normalizedV ));
+        }
+
+        /* build blend vector for this instance */
+        cf2_buildBlendVector( font, font->lastVsindex,
+                              font->lenNormalizedVector,
+                              font->lastNormalizedVector,
+                              &font->lenBlendVector,
+                              &font->blendVector );
+      }
+
+    } /* needExtraSetup */
   }
 
 
diff --git a/src/cff/cf2font.h b/src/cff/cf2font.h
index bd05e69..a6fa310 100644
--- a/src/cff/cf2font.h
+++ b/src/cff/cf2font.h
@@ -47,7 +47,7 @@
 FT_BEGIN_HEADER
 
 
-#define CF2_OPERAND_STACK_SIZE  48
+#define CF2_OPERAND_STACK_SIZE  193/* TODO: this is temporary for CFF2    */
 #define CF2_MAX_SUBR            16 /* maximum subroutine nesting;         */
                                    /* only 10 are allowed but there exist */
                                    /* fonts like `HiraKakuProN-W3.ttf'    */
@@ -63,6 +63,7 @@ FT_BEGIN_HEADER
     FT_Memory  memory;
     FT_Error   error;     /* shared error for this instance */
 
+    FT_Bool             isCFF2;
     CF2_RenderingFlags  renderingFlags;
 
     /* variables that depend on Transform:  */
@@ -74,6 +75,13 @@ FT_BEGIN_HEADER
     CF2_Matrix  outerTransform;    /* post hinting; includes rotations */
     CF2_Fixed   ppem;              /* transform-dependent              */
 
+    /* variation data */
+    CF2_UInt    lastVsindex;         /* last vsindex used              */
+    CF2_UInt    lenNormalizedVector; /* normDV length (aka numAxes)    */
+    FT_Fixed *  lastNormalizedVector;/* last normDV used               */
+    CF2_UInt    lenBlendVector;      /* blendV length (aka numMasters) */
+    CF2_Fixed * blendVector;         /* current blendV (per glyph)     */
+
     CF2_Int  unitsPerEm;
 
     CF2_Fixed  syntheticEmboldeningAmountX;   /* character space units */
diff --git a/src/cff/cf2ft.c b/src/cff/cf2ft.c
index 55f3206..3f8334f 100644
--- a/src/cff/cf2ft.c
+++ b/src/cff/cf2ft.c
@@ -102,9 +102,10 @@
     if ( font )
     {
       FT_Memory  memory = font->memory;
-
-
       (void)memory;
+
+      FT_FREE( font->lastNormalizedVector );
+      FT_FREE( font->blendVector );
     }
   }
 
@@ -366,6 +367,9 @@
                                &hinted,
                                &scaled );
 
+      /* copy isCFF2 boolean from TT_Face to CF2_Font */
+      font->isCFF2 = builder->face->isCFF2;
+
       font->renderingFlags = 0;
       if ( hinted )
         font->renderingFlags |= CF2_FlagsHinted;
@@ -412,6 +416,39 @@
     return decoder->current_subfont;
   }
 
+  /* get pointer to VStore structure */
+  FT_LOCAL_DEF( CFF_VStore )
+  cf2_getVStore( CFF_Decoder* decoder )
+  {
+    FT_ASSERT( decoder && decoder->cff );
+
+    return &decoder->cff->vstore;
+  }
+
+  /* get normalized design vector for current render request */
+  /* returns pointer and length                              */
+  /* if blend struct is not initialized, return length zero  */
+  /* Note: use FT_Fixed not CF2_Fixed for the vector         */
+  FT_LOCAL_DEF( void )
+  cf2_getNormalizedVector( CFF_Decoder* decoder, CF2_UInt * len, FT_Fixed ** 
vec  )
+  {
+    GX_Blend blend;
+
+    FT_ASSERT( decoder && decoder->builder.face );
+    FT_ASSERT( vec && len );
+
+    blend = decoder->builder.face->blend;
+    if ( blend )
+    {
+      *vec = blend->normalizedcoords;
+      *len = blend->num_axis;
+    }
+    else
+    {
+      *vec = NULL;
+      *len = 0;
+    }
+  }
 
   /* get `y_ppem' from `CFF_Size' */
   FT_LOCAL_DEF( CF2_Fixed )
diff --git a/src/cff/cf2ft.h b/src/cff/cf2ft.h
index 8e55e84..7c1311a 100644
--- a/src/cff/cf2ft.h
+++ b/src/cff/cf2ft.h
@@ -64,6 +64,14 @@ FT_BEGIN_HEADER
   FT_LOCAL( CFF_SubFont )
   cf2_getSubfont( CFF_Decoder*  decoder );
 
+  FT_LOCAL( CFF_VStore )
+  cf2_getVStore( CFF_Decoder*  decoder );
+
+
+  FT_LOCAL_DEF( void )
+  cf2_getNormalizedVector( CFF_Decoder*  decoder,
+                           CF2_UInt *    len,
+                           FT_Fixed **   vec  );
 
   FT_LOCAL( CF2_Fixed )
   cf2_getPpemY( CFF_Decoder*  decoder );
diff --git a/src/cff/cf2intrp.c b/src/cff/cf2intrp.c
index 7d663dd..a011e28 100644
--- a/src/cff/cf2intrp.c
+++ b/src/cff/cf2intrp.c
@@ -216,7 +216,7 @@
     cf2_cmdRESERVED_13,  /* 13 */
     cf2_cmdENDCHAR,      /* 14 */
     cf2_cmdRESERVED_15,  /* 15 */
-    cf2_cmdRESERVED_16,  /* 16 */
+    cf2_cmdBLEND,        /* 16 */
     cf2_cmdRESERVED_17,  /* 17 */
     cf2_cmdHSTEMHM,      /* 18 */
     cf2_cmdHINTMASK,     /* 19 */
@@ -402,6 +402,34 @@
     *curY = vals[13];
   }
 
+  /* Blend numOperands on the stack,                  */
+  /* store results into the first numBlends values,   */
+  /* then pop remaining arguments.                    */
+  static void
+  cf2_doBlend( const CF2_Font  font,
+               CF2_Stack       opStack,
+               CF2_UInt        numBlends )
+  {
+    CF2_UInt delta;
+    CF2_UInt base;
+    CF2_UInt i, j;
+    CF2_UInt numOperands = (CF2_UInt)(numBlends * font->lenBlendVector);
+
+    base = cf2_stack_count( opStack ) - numOperands;
+    delta = base + numBlends;
+    for ( i = 0; i < numBlends; i++ )
+    {
+      const CF2_Fixed * weight = &font->blendVector[1];
+      CF2_Fixed sum = cf2_stack_getReal( opStack, i+base );    /* start with 
first term */
+      for ( j = 1; j < font->lenBlendVector; j++ )
+      {
+        sum += FT_MulFix( *weight++, cf2_stack_getReal( opStack, delta++ ));
+      }
+      cf2_stack_setReal( opStack, i+base, sum );               /* store 
blended result  */
+    }
+    /* leave only numBlends results on stack */
+    cf2_stack_pop( opStack, numOperands - numBlends );
+}
 
   /*
    * `error' is a shared error code used by many objects in this
@@ -585,12 +613,20 @@
       case cf2_cmdRESERVED_9:
       case cf2_cmdRESERVED_13:
       case cf2_cmdRESERVED_15:
-      case cf2_cmdRESERVED_16:
       case cf2_cmdRESERVED_17:
         /* we may get here if we have a prior error */
         FT_TRACE4(( " unknown op (%d)\n", op1 ));
         break;
 
+      case cf2_cmdBLEND:
+        {
+          FT_UInt numBlends = (FT_UInt)cf2_stack_popInt( opStack );
+          FT_TRACE4(( " blend\n"  ));
+          cf2_doBlend( font, opStack, numBlends );
+        }
+        continue;     /* do not clear the stack */
+
+
       case cf2_cmdHSTEMHM:
       case cf2_cmdHSTEM:
         FT_TRACE4(( op1 == cf2_cmdHSTEMHM ? " hstemhm\n" : " hstem\n" ));
diff --git a/src/cff/cf2stack.c b/src/cff/cf2stack.c
index 6fafd90..5001b68 100644
--- a/src/cff/cf2stack.c
+++ b/src/cff/cf2stack.c
@@ -194,6 +194,34 @@
     }
   }
 
+  /* provide random access to stack */
+  FT_LOCAL_DEF( void )
+  cf2_stack_setReal( CF2_Stack  stack,
+                     CF2_UInt   idx,
+                     CF2_Fixed  val )
+  {
+    if ( idx > cf2_stack_count( stack ) )
+    {
+      CF2_SET_ERROR( stack->error, Stack_Overflow );
+      return;
+    }
+
+    stack->buffer[idx].u.r = val;
+    stack->buffer[idx].type = CF2_NumberFixed;
+  }
+
+  /* discard (pop) num values from stack */
+  FT_LOCAL_DEF( void )
+  cf2_stack_pop( CF2_Stack  stack,
+                 CF2_UInt   num )
+  {
+    if ( num > cf2_stack_count( stack ) )
+    {
+      CF2_SET_ERROR( stack->error, Stack_Underflow );
+      return;
+    }
+    stack->top -= num;
+  }
 
   FT_LOCAL( void )
   cf2_stack_roll( CF2_Stack  stack,
diff --git a/src/cff/cf2stack.h b/src/cff/cf2stack.h
index e740a7a..acec074 100644
--- a/src/cff/cf2stack.h
+++ b/src/cff/cf2stack.h
@@ -94,6 +94,15 @@ FT_BEGIN_HEADER
                      CF2_UInt   idx );
 
   FT_LOCAL( void )
+  cf2_stack_setReal( CF2_Stack  stack,
+                     CF2_UInt   idx,
+                     CF2_Fixed  val );
+
+  FT_LOCAL( void )
+  cf2_stack_pop( CF2_Stack  stack,
+                 CF2_UInt   num );
+
+  FT_LOCAL( void )
   cf2_stack_roll( CF2_Stack  stack,
                   CF2_Int    count,
                   CF2_Int    idx );
diff --git a/src/cff/cffdrivr.c b/src/cff/cffdrivr.c
index 67bf09d..b03dacb 100644
--- a/src/cff/cffdrivr.c
+++ b/src/cff/cffdrivr.c
@@ -32,6 +32,12 @@
 #include "cffcmap.h"
 #include "cffparse.h"
 
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include FT_MULTIPLE_MASTERS_H
+#include FT_SERVICE_MULTIPLE_MASTERS_H
+#include "../truetype/ttgxvar.h"
+#endif
+
 #include "cfferrs.h"
 #include "cffpic.h"
 
@@ -291,6 +297,14 @@
     FT_Error    error;
 
 
+    /* TODO: for testing: cff2 does not have glyph names */
+    /* will need to use post table method */
+    if ( font->version_major == 2 )
+    {
+      FT_STRCPYN( buffer, "noname", buffer_max );
+      return FT_Err_Ok;
+    }
+
     if ( !font->psnames )
     {
       FT_ERROR(( "cff_get_glyph_name:"
@@ -871,15 +885,31 @@
   /*************************************************************************/
   /*************************************************************************/
 
+/* reuse some of the TT functions for the cff multi_master service */
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+  FT_DEFINE_SERVICE_MULTIMASTERSREC(
+    cff_service_multi_masters,
+    (FT_Get_MM_Func)         NULL,                   /* get_mm         */
+    (FT_Set_MM_Design_Func)  NULL,                   /* set_mm_design  */
+    (FT_Set_MM_Blend_Func)   TT_Set_MM_Blend,        /* set_mm_blend   */
+    (FT_Get_MM_Var_Func)     TT_Get_MM_Var,          /* get_mm_var     */
+    (FT_Set_Var_Design_Func) TT_Set_Var_Design,      /* set_var_design */
+    (FT_Get_Var_Design_Func) TT_Get_Var_Design,      /* get_var_design */
+    (FT_Get_Var_Blend_Func)  TT_Get_Var_Blend )      /* get_var_blend  */
+    
+#endif
+
+/* TODO: fix up ifdefs and we really need an 8-parameter 
FT_DEFINE_SERVICEDESCREC8 */
 #ifndef FT_CONFIG_OPTION_NO_GLYPH_NAMES
   FT_DEFINE_SERVICEDESCREC7(
     cff_services,
     FT_SERVICE_ID_FONT_FORMAT,          FT_FONT_FORMAT_CFF,
+    FT_SERVICE_ID_MULTI_MASTERS,        &CFF_SERVICE_MULTI_MASTERS_GET,
     FT_SERVICE_ID_POSTSCRIPT_INFO,      &CFF_SERVICE_PS_INFO_GET,
     FT_SERVICE_ID_POSTSCRIPT_FONT_NAME, &CFF_SERVICE_PS_NAME_GET,
     FT_SERVICE_ID_GLYPH_DICT,           &CFF_SERVICE_GLYPH_DICT_GET,
     FT_SERVICE_ID_TT_CMAP,              &CFF_SERVICE_GET_CMAP_INFO_GET,
-    FT_SERVICE_ID_CID,                  &CFF_SERVICE_CID_INFO_GET,
+    /*FT_SERVICE_ID_CID,                  &CFF_SERVICE_CID_INFO_GET,*/
     FT_SERVICE_ID_PROPERTIES,           &CFF_SERVICE_PROPERTIES_GET
   )
 #else
diff --git a/src/cff/cffload.c b/src/cff/cffload.c
index 3d1bda9..c357a1a 100644
--- a/src/cff/cffload.c
+++ b/src/cff/cffload.c
@@ -589,12 +589,15 @@
                       FT_UInt   element )
   {
     CFF_Index   idx = &font->name_index;
-    FT_Memory   memory = idx->stream->memory;
+    FT_Memory   memory;
     FT_Byte*    bytes;
     FT_ULong    byte_len;
     FT_Error    error;
     FT_String*  name = 0;
 
+    if ( !idx->stream )  /* CFF2 does not include a name index */
+      goto Exit;
+    memory = idx->stream->memory;
 
     error = cff_index_access_element( idx, element, &bytes, &byte_len );
     if ( error )
@@ -862,6 +865,32 @@
     charset->offset = 0;
   }
 
+  static void
+  cff_vstore_done( CFF_VStoreRec*   vstore,
+                   FT_Memory        memory )
+  {
+    FT_UInt i;
+
+    /* free regionList and axisLists */
+    if ( vstore->varRegionList )
+    {
+      for ( i=0; i<vstore->regionCount; i++ )
+      {
+        FT_FREE( vstore->varRegionList[i].axisList );
+      }
+    }
+    FT_FREE( vstore->varRegionList );
+
+    /* free varData and indices */
+    if ( vstore->varData )
+    {
+      for ( i=0; i<vstore->dataCount; i++ )
+      {
+        FT_FREE( vstore->varData[i].regionIndices );
+      }
+    }
+    FT_FREE( vstore->varData );
+  }
 
   static FT_Error
   cff_charset_load( CFF_Charset  charset,
@@ -1053,6 +1082,130 @@
   }
 
 
+  /* convert 2.14 to Fixed */
+  #define FT_fdot14ToFixed( x )             \
+          (((FT_Fixed)((FT_Int16)(x))) << 2 )
+
+  static FT_Error
+  cff_vstore_load(  CFF_VStoreRec*  vstore,
+                    FT_Stream       stream,
+                    FT_ULong        base_offset,
+                    FT_ULong        offset )
+  {
+    FT_Memory  memory = stream->memory;
+    FT_Error   error = FT_THROW( Invalid_File_Format );
+    FT_UInt    i,j;
+
+    /* no offset means no vstore to parse */
+    if ( offset )
+    {
+      FT_UInt   vsSize;     /* currently unused */
+      FT_UInt   vsOffset;
+      FT_UInt   format;
+      FT_ULong  regionListOffset;
+      FT_ULong  dataOffsetArrayOffset;
+      FT_ULong  varDataOffset;
+
+      /* we need to parse the table to determine its size */
+      if ( FT_STREAM_SEEK( base_offset + offset ) ||
+           FT_READ_USHORT( vsSize ) )
+        goto Exit;
+
+      /* actual variation store begins after the length */
+      vsOffset = FT_STREAM_POS();
+
+      /* check the header */
+      if ( FT_READ_USHORT( format ) )
+        goto Exit;
+      if ( format != 1 )
+      {
+        error = FT_THROW( Invalid_File_Format );
+        goto Exit;
+      }
+
+      /* read top level fields */
+      if ( FT_READ_ULONG( regionListOffset )   ||
+           FT_READ_USHORT( vstore->dataCount ) )
+        goto Exit;
+
+      /* save position of item variation data offsets */
+      /* we'll parse region list first, then come back */
+      dataOffsetArrayOffset = FT_STREAM_POS();
+
+      /* parse regionList and axisLists*/
+      if ( FT_STREAM_SEEK( vsOffset + regionListOffset ) ||
+           FT_READ_USHORT( vstore->axisCount )         ||
+           FT_READ_USHORT( vstore->regionCount ) )
+        goto Exit;
+
+      if ( FT_NEW_ARRAY( vstore->varRegionList, vstore->regionCount ) )
+        goto Exit;
+
+      for ( i=0; i<vstore->regionCount; i++ )
+      {
+        CFF_VarRegion* region = &vstore->varRegionList[i];
+        if ( FT_NEW_ARRAY( region->axisList, vstore->axisCount ) )
+          goto Exit;
+        for ( j=0; j<vstore->axisCount; j++ )
+        {
+          CFF_AxisCoords* axis = &region->axisList[j];
+          FT_Int16 start14, peak14, end14;
+          if ( FT_READ_SHORT( start14 ) ||
+               FT_READ_SHORT( peak14 )  ||
+               FT_READ_SHORT( end14 ) )
+            goto Exit;
+          axis->startCoord = FT_fdot14ToFixed( start14 );
+          axis->peakCoord =  FT_fdot14ToFixed( peak14 );
+          axis->endCoord =   FT_fdot14ToFixed( end14 );
+        }
+      }
+
+      /* re-position to parse varData and regionIndices */
+      if ( FT_STREAM_SEEK( dataOffsetArrayOffset ) )
+        goto Exit;
+
+      if ( FT_NEW_ARRAY( vstore->varData, vstore->dataCount ) )
+        goto Exit;
+
+      for ( i=0; i<vstore->dataCount; i++ )
+      {
+        FT_UInt itemCount, shortDeltaCount;
+        CFF_VarData* data = &vstore->varData[i];
+
+        if ( FT_READ_ULONG( varDataOffset ) )
+          goto Exit;
+
+        if ( FT_STREAM_SEEK( vsOffset + varDataOffset ) )
+          goto Exit;
+
+        /* ignore these two values because CFF2 has no delta sets */
+        if ( FT_READ_USHORT( itemCount ) ||
+             FT_READ_USHORT( shortDeltaCount ) )
+          goto Exit;
+
+        /* Note: just record values; consistency is checked later */
+        /* by cf2_buildBlendVector when it consumes vstore        */
+
+        if ( FT_READ_USHORT( data->regionIdxCount ) )
+          goto Exit;
+        if ( FT_NEW_ARRAY( data->regionIndices, data->regionIdxCount ) )
+          goto Exit;
+        for ( j=0; j<data->regionIdxCount; j++ )
+        {
+          if ( FT_READ_USHORT( data->regionIndices[j] ) )
+            goto Exit;
+        }
+      }
+
+    }
+    error = FT_Err_Ok;
+
+  Exit:
+    if ( error )
+      cff_vstore_done( vstore, memory );
+    return error;
+  }
+
   static void
   cff_encoding_done( CFF_Encoding  encoding )
   {
@@ -1442,7 +1595,8 @@
                  FT_Stream  stream,
                  FT_Int     face_index,
                  CFF_Font   font,
-                 FT_Bool    pure_cff )
+                 FT_Bool    pure_cff,
+                 FT_Bool    cff2 )
   {
     static const FT_Frame_Field  cff_header_fields[] =
     {
@@ -1478,7 +1632,7 @@
       goto Exit;
 
     /* check format */
-    if ( font->version_major   != 1 ||
+    if ( font->version_major != ( cff2 ? 2 : 1 ) ||
          font->header_size      < 4 ||
          font->absolute_offsize > 4 )
     {
@@ -1492,8 +1646,9 @@
       goto Exit;
 
     /* read the name, top dict, string and global subrs index */
-    if ( FT_SET_ERROR( cff_index_init( &font->name_index,
-                                       stream, 0 ) )                       ||
+    /* CFF2 does not contain a name index */
+    if ( ( !cff2 && FT_SET_ERROR( cff_index_init( &font->name_index,
+                                       stream, 0 ) ) )                     ||
          FT_SET_ERROR( cff_index_init( &font->font_dict_index,
                                        stream, 0 ) )                       ||
          FT_SET_ERROR( cff_index_init( &string_index,
@@ -1636,7 +1791,7 @@
       goto Exit;
 
     /* read the Charset and Encoding tables if available */
-    if ( font->num_glyphs > 0 )
+    if ( !cff2 && font->num_glyphs > 0 )
     {
       FT_Bool  invert = FT_BOOL( dict->cid_registry != 0xFFFFU && pure_cff );
 
@@ -1660,6 +1815,14 @@
       }
     }
 
+    /* read the Variation Store if available */
+    error = cff_vstore_load( &font->vstore,
+                             stream,
+                             base_offset,
+                             dict->vstore_offset );
+    if ( error )
+      goto Exit;
+
     /* get the font name (/CIDFontName for CID-keyed fonts, */
     /* /FontName otherwise)                                 */
     font->font_name = cff_index_get_name( font, subfont_index );
@@ -1696,6 +1859,7 @@
 
     cff_encoding_done( &font->encoding );
     cff_charset_done( &font->charset, font->stream );
+    cff_vstore_done( &font->vstore, memory );
 
     cff_subfont_done( memory, &font->top_font );
 
diff --git a/src/cff/cffload.h b/src/cff/cffload.h
index 1dd07ba..992e5b5 100644
--- a/src/cff/cffload.h
+++ b/src/cff/cffload.h
@@ -64,7 +64,8 @@ FT_BEGIN_HEADER
                  FT_Stream  stream,
                  FT_Int     face_index,
                  CFF_Font   font,
-                 FT_Bool    pure_cff );
+                 FT_Bool    pure_cff,
+                 FT_Bool    cff2 );
 
   FT_LOCAL( void )
   cff_font_done( CFF_Font  font );
diff --git a/src/cff/cffobjs.c b/src/cff/cffobjs.c
index 0f07696..b246f75 100644
--- a/src/cff/cffobjs.c
+++ b/src/cff/cffobjs.c
@@ -491,6 +491,7 @@
     FT_Service_PsCMaps  psnames;
     PSHinter_Service    pshinter;
     FT_Bool             pure_cff    = 1;
+    FT_Bool             cff2        = 0;
     FT_Bool             sfnt_format = 0;
     FT_Library          library     = cffface->driver->root.library;
 
@@ -554,9 +555,22 @@
       }
 
       /* now load the CFF part of the file */
-      error = face->goto_table( face, TTAG_CFF, stream, 0 );
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+      /* give priority to CFF2 */
+      error = face->goto_table( face, TTAG_CFF2, stream, 0 );
+      if ( !error )
+      {
+        cff2 = 1;
+        face->isCFF2 = cff2;
+      }
+      if ( FT_ERR_EQ( error, Table_Missing ) )
+#endif
+      {
+        error = face->goto_table( face, TTAG_CFF, stream, 0 );
+      }
       if ( error )
         goto Exit;
+
     }
     else
     {
@@ -579,7 +593,7 @@
         goto Exit;
 
       face->extra.data = cff;
-      error = cff_font_load( library, stream, face_index, cff, pure_cff );
+      error = cff_font_load( library, stream, face_index, cff, pure_cff, cff2 
);
       if ( error )
         goto Exit;
 
@@ -1079,6 +1093,11 @@
         FT_FREE( face->extra.data );
       }
     }
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+    tt_done_blend( memory, face->blend );
+    face->blend = NULL;
+#endif
+
   }
 
 
diff --git a/src/cff/cffparse.c b/src/cff/cffparse.c
index a4f986b..f007f85 100644
--- a/src/cff/cffparse.c
+++ b/src/cff/cffparse.c
@@ -782,6 +782,32 @@
     return error;
   }
 
+  /* TODO: replace this test code with doBlend */
+  static FT_Error
+  cff_parse_blend( CFF_Parser  parser )
+  {
+    FT_UInt          num_args = (FT_UInt)( parser->top - parser->stack );
+    FT_UInt          numBlends;
+    FT_Error         error;
+
+    error = FT_ERR( Stack_Underflow );
+    FT_TRACE1(( " cff_parse_blend\n" ));
+
+    if ( parser->top >= parser->stack + 1 ) /* at least one operand */
+    {
+      /* top of stack gives number of blends */
+      numBlends = (FT_UInt)cff_parse_num( parser->top - 1 );
+
+      if ( numBlends < num_args )
+      {
+        /* for testing, just reduce stack to first numBlends values */
+        parser->top = parser->stack + numBlends;
+
+        error = FT_Err_Ok;
+      }
+    }
+    return error;
+  }
 
 #define CFF_FIELD_NUM( code, name, id )             \
           CFF_FIELD( code, name, id, cff_kind_num )
@@ -817,6 +843,15 @@
             0, 0                             \
           },
 
+#define CFF_FIELD_BLEND( code, id )          \
+          {                                  \
+            cff_kind_blend,                  \
+            code | CFFCODE,                  \
+            0, 0,                            \
+            cff_parse_blend,                 \
+            0, 0                             \
+          },
+
 #define CFF_FIELD( code, name, id, kind ) \
           {                               \
             kind,                         \
@@ -860,6 +895,16 @@
             id                               \
           },
 
+#define CFF_FIELD_BLEND( code, id )          \
+          {                                  \
+            cff_kind_blend,                  \
+            code | CFFCODE,                  \
+            0, 0,                            \
+            cff_parse_blend,                 \
+            0, 0,                            \
+            id                               \
+          },
+
 #define CFF_FIELD( code, name, id, kind ) \
           {                               \
             kind,                         \
@@ -1386,7 +1431,7 @@
               }
               break;
 
-            default:  /* callback */
+            default:  /* callback or blend */
               error = field->reader( parser );
               if ( error )
                 goto Exit;
@@ -1400,7 +1445,8 @@
 
       Found:
         /* clear stack */
-        parser->top = parser->stack;
+        if ( field->kind != cff_kind_blend )
+            parser->top = parser->stack;
       }
       p++;
     }
diff --git a/src/cff/cffparse.h b/src/cff/cffparse.h
index a95970e..0134efb 100644
--- a/src/cff/cffparse.h
+++ b/src/cff/cffparse.h
@@ -28,7 +28,7 @@
 FT_BEGIN_HEADER
 
 
-#define CFF_MAX_STACK_DEPTH  96
+#define CFF_MAX_STACK_DEPTH  193
 
 #define CFF_CODE_TOPDICT  0x1000
 #define CFF_CODE_PRIVATE  0x2000
@@ -77,6 +77,7 @@ FT_BEGIN_HEADER
     cff_kind_bool,
     cff_kind_delta,
     cff_kind_callback,
+    cff_kind_blend,
 
     cff_kind_max  /* do not remove */
   };
diff --git a/src/cff/cffpic.h b/src/cff/cffpic.h
index bed6b35..4737b9e 100644
--- a/src/cff/cffpic.h
+++ b/src/cff/cffpic.h
@@ -32,6 +32,7 @@
 #define CFF_SERVICE_CID_INFO_GET         cff_service_cid_info
 #define CFF_SERVICE_PROPERTIES_GET       cff_service_properties
 #define CFF_SERVICES_GET                 cff_services
+#define CFF_SERVICE_MULTI_MASTERS_GET    cff_service_multi_masters
 #define CFF_CMAP_ENCODING_CLASS_REC_GET  cff_cmap_encoding_class_rec
 #define CFF_CMAP_UNICODE_CLASS_REC_GET   cff_cmap_unicode_class_rec
 #define CFF_FIELD_HANDLERS_GET           cff_field_handlers
diff --git a/src/cff/cfftoken.h b/src/cff/cfftoken.h
index 22637c7..77e93af 100644
--- a/src/cff/cfftoken.h
+++ b/src/cff/cfftoken.h
@@ -45,6 +45,9 @@
   CFF_FIELD_NUM     ( 16,    encoding_offset,     "Encoding" )
   CFF_FIELD_NUM     ( 17,    charstrings_offset,  "CharStrings" )
   CFF_FIELD_CALLBACK( 18,    private_dict,        "Private" )
+  CFF_FIELD_NUM     ( 22,    vsindex,             "vsindex" )
+  CFF_FIELD_NUM     ( 24,    vstore_offset,       "vstore" )
+  CFF_FIELD_NUM     ( 25,    maxstack,            "maxstack" )
   CFF_FIELD_NUM     ( 0x114, synthetic_base,      "SyntheticBase" )
   CFF_FIELD_STRING  ( 0x115, embedded_postscript, "PostScript" )
 
@@ -101,5 +104,6 @@
   CFF_FIELD_NUM       ( 20,    default_width,          "defaultWidthX" )
   CFF_FIELD_NUM       ( 21,    nominal_width,          "nominalWidthX" )
 
+  CFF_FIELD_BLEND     ( 31,                            "blend" )
 
 /* END */
diff --git a/src/cff/cfftypes.h b/src/cff/cfftypes.h
index 4426c7e..d8fc1e9 100644
--- a/src/cff/cfftypes.h
+++ b/src/cff/cfftypes.h
@@ -101,6 +101,38 @@ FT_BEGIN_HEADER
 
   } CFF_CharsetRec, *CFF_Charset;
 
+  typedef struct CFF_VarData_
+  {
+    /* FT_UInt      itemCount;         not used; always zero         */
+    /* FT_UInt      shortDeltaCount;   not used; always zero         */
+    FT_UInt         regionIdxCount; /* # regions in this var data    */
+    FT_UInt*        regionIndices;  /* array of regionCount indices  */
+                                    /* these index the varRegionList */
+  } CFF_VarData;
+
+  typedef struct CFF_AxisCoords_    /* contribution of one axis to a region   
*/
+  {
+    FT_Fixed        startCoord;
+    FT_Fixed        peakCoord;      /* zero peak means no effect (factor = 1) 
*/
+    FT_Fixed        endCoord;
+  } CFF_AxisCoords;
+
+  typedef struct CFF_VarRegion_
+  {
+    CFF_AxisCoords* axisList;       /* array of axisCount records */
+  } CFF_VarRegion;
+
+  typedef struct  CFF_VstoreRec_
+  {
+    FT_UInt         dataCount;
+    CFF_VarData*    varData;        /* array of dataCount records */
+                                    /* vsindex indexes this array */
+    FT_UShort       axisCount;
+    FT_UInt         regionCount;    /* total # regions defined    */
+    CFF_VarRegion*  varRegionList;
+
+  } CFF_VStoreRec, *CFF_VStore;
+
 
   typedef struct  CFF_FontRecDictRec_
   {
@@ -151,6 +183,11 @@ FT_BEGIN_HEADER
     FT_UShort  num_designs;
     FT_UShort  num_axes;
 
+    /* fields for CFF2 */
+    FT_UInt    vsindex;
+    FT_ULong   vstore_offset;
+    FT_UInt    maxstack;
+
   } CFF_FontRecDictRec, *CFF_FontRecDict;
 
 
@@ -280,6 +317,8 @@ FT_BEGIN_HEADER
     /* since version 2.4.12 */
     FT_Generic       cf2_instance;
 
+    CFF_VStoreRec    vstore;
+
   } CFF_FontRec, *CFF_Font;
 
 
diff --git a/src/sfnt/sfobjs.c b/src/sfnt/sfobjs.c
index 4413bbc..4ca8cb7 100644
--- a/src/sfnt/sfobjs.c
+++ b/src/sfnt/sfobjs.c
@@ -1085,10 +1085,12 @@
 #ifdef FT_CONFIG_OPTION_INCREMENTAL
     has_outline = FT_BOOL( face->root.internal->incremental_interface != 0 ||
                            tt_face_lookup_table( face, TTAG_glyf )    != 0 ||
-                           tt_face_lookup_table( face, TTAG_CFF )     != 0 );
+                           tt_face_lookup_table( face, TTAG_CFF )     != 0 ||
+                           tt_face_lookup_table( face, TTAG_CFF2 ));
 #else
     has_outline = FT_BOOL( tt_face_lookup_table( face, TTAG_glyf ) != 0 ||
-                           tt_face_lookup_table( face, TTAG_CFF )  != 0 );
+                           tt_face_lookup_table( face, TTAG_CFF )  != 0 ||
+                           tt_face_lookup_table( face, TTAG_CFF2 ));
 #endif
 
     is_apple_sbit = 0;
@@ -1331,6 +1333,9 @@
            tt_face_lookup_table( face, TTAG_fvar ) != 0 &&
            tt_face_lookup_table( face, TTAG_gvar ) != 0 )
         flags |= FT_FACE_FLAG_MULTIPLE_MASTERS;
+      if ( tt_face_lookup_table( face, TTAG_CFF2 ) != 0 &&
+           tt_face_lookup_table( face, TTAG_fvar ) != 0 )
+        flags |= FT_FACE_FLAG_MULTIPLE_MASTERS;
 #endif
 
       root->face_flags = flags;
diff --git a/src/sfnt/ttmtx.c b/src/sfnt/ttmtx.c
index 186f873..a544728 100644
--- a/src/sfnt/ttmtx.c
+++ b/src/sfnt/ttmtx.c
@@ -22,6 +22,10 @@
 #include FT_TRUETYPE_TAGS_H
 #include "ttmtx.h"
 
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include "../truetype/ttgxvar.h"
+#endif
+
 #include "sferrors.h"
 
 
@@ -274,6 +278,12 @@
       *abearing = 0;
       *aadvance = 0;
     }
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+    /* TODO: handle VVAR and LSB */
+    if ( !vertical )
+      tt_adjust_advance( face, gindex, aadvance );
+#endif
+
   }
 
 
diff --git a/src/truetype/ttdriver.c b/src/truetype/ttdriver.c
index 2659b9c..ce0be7f 100644
--- a/src/truetype/ttdriver.c
+++ b/src/truetype/ttdriver.c
@@ -449,7 +449,9 @@
     (FT_Set_MM_Design_Func) NULL,                   /* set_mm_design  */
     (FT_Set_MM_Blend_Func)  TT_Set_MM_Blend,        /* set_mm_blend   */
     (FT_Get_MM_Var_Func)    TT_Get_MM_Var,          /* get_mm_var     */
-    (FT_Set_Var_Design_Func)TT_Set_Var_Design )     /* set_var_design */
+    (FT_Set_Var_Design_Func)TT_Set_Var_Design,      /* set_var_design */
+    (FT_Get_Var_Design_Func)TT_Get_Var_Design,      /* get_var_design */
+    (FT_Get_Var_Blend_Func) TT_Get_Var_Blend )      /* get_var_blend  */
 #endif
 
 
diff --git a/src/truetype/ttgxvar.c b/src/truetype/ttgxvar.c
index 080e2dd..504022a 100644
--- a/src/truetype/ttgxvar.c
+++ b/src/truetype/ttgxvar.c
@@ -392,6 +392,418 @@
     FT_FRAME_EXIT();
   }
 
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Function>                                                            */
+  /*    ft_var_load_hvar                                                   */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    Parse the `HVAR' table if present.  It need not be, so we return   */
+  /*    nothing.                                                           */
+  /*    On success, blend->hvar_checked is TRUE                            */
+  /*    Some memory may remain allocated on error (hvar_checked FALSE)     */
+  /*    Memory is always freed in tt_done_blend.                           */
+  /*                                                                       */
+  /* <InOut>                                                               */
+  /*    face :: The font face.                                             */
+  /*                                                                       */
+  /*                                                                       */
+
+  /* some macros we need */
+  #define FT_fdot14ToFixed( x )             \
+          (((FT_Fixed)((FT_Int16)(x))) << 2 )
+
+  #define FT_FIXED_ONE ((FT_Fixed)0x10000)
+  #define FT_intToFixed( i )                \
+          ( (FT_Fixed)( (FT_UInt32)(i) << 16 ) )
+  #define FT_fixedToInt( x )                \
+          ( (FT_Short)( ( (FT_UInt32)(x) + 0x8000U ) >> 16 ) )
+
+  static void
+  ft_var_load_hvar( TT_Face  face )
+  {
+    FT_Stream       stream = FT_FACE_STREAM( face );
+    FT_Memory       memory = stream->memory;
+    GX_Blend        blend  = face->blend;
+    FT_Error        error;
+    FT_UShort       majorVersion;
+    FT_UShort       minorVersion;
+    FT_ULong        table_len;
+    FT_ULong        table_offset;
+    FT_ULong        store_offset;
+    FT_ULong        map_offset;
+
+    FT_TRACE2(( "HVAR " ));
+
+    /* if we allocated the table, assume we've already tried to parse it */
+    if ( face->blend->hvar_table )
+      return;
+
+    error = face->goto_table( face, TTAG_HVAR, stream, &table_len );
+    if ( error )
+    {
+      FT_TRACE2(( "is missing\n" ));
+      return;
+    }
+
+    table_offset = FT_STREAM_POS();
+
+    if ( FT_READ_USHORT( majorVersion ) ||
+         FT_READ_USHORT( minorVersion ) )
+      goto Exit;
+    if ( majorVersion != 1 )
+    {
+      FT_TRACE2(( "bad table version %d\n", majorVersion ));
+      error = FT_THROW( Invalid_File_Format );
+      goto Exit;
+    }
+
+    if ( FT_READ_ULONG( store_offset ) ||
+         FT_READ_ULONG( map_offset ) )
+      goto Exit;
+
+    /* parse item variation store */
+    {
+      FT_UShort format;
+      FT_ULong data_offset_array_offset;
+      FT_ULong data_offset;
+      FT_ULong region_offset;
+      GX_HVStore itemStore;
+      FT_UInt i, j, k;
+      FT_UInt shortDeltaCount;
+      GX_HVarTable hvarTable;
+      GX_HVarData hvarData;
+
+      if ( FT_STREAM_SEEK( table_offset + store_offset ) ||
+           FT_READ_USHORT( format ) )
+        goto Exit;
+      if ( format != 1 )
+      {
+        FT_TRACE2(( "bad store format %d\n", format ));
+        error = FT_THROW( Invalid_File_Format );
+        goto Exit;
+      }
+
+      if ( FT_NEW( blend->hvar_table ) )    /* allocate table at top level */
+        goto Exit;
+
+      hvarTable = blend->hvar_table;
+      itemStore = &hvarTable->itemStore;
+
+      if ( FT_READ_ULONG( region_offset ) ||
+           FT_READ_USHORT( itemStore->dataCount ) )
+        goto Exit;
+
+      /* save position of item variation data offsets */
+      /* we'll parse region list first, then come back */
+      data_offset_array_offset = FT_STREAM_POS();
+
+      /* parse array of region records (region list) */
+      if ( FT_STREAM_SEEK( table_offset + store_offset + region_offset ) )
+        goto Exit;
+
+      if ( FT_READ_USHORT( itemStore->axisCount ) ||
+           FT_READ_USHORT( itemStore->regionCount ) )
+        goto Exit;
+
+      if ( FT_NEW_ARRAY( itemStore->varRegionList, itemStore->regionCount ) )
+        goto Exit;
+
+      for ( i=0; i<itemStore->regionCount; i++ )
+      {
+        GX_AxisCoords axisCoords;
+
+        if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList, 
itemStore->axisCount ) )
+          goto Exit;
+
+        axisCoords = itemStore->varRegionList[i].axisList;
+
+        for ( j=0; j<itemStore->axisCount; j++ )
+        {
+          FT_Short start, peak, end;
+
+          if ( FT_READ_SHORT( start ) ||
+               FT_READ_SHORT( peak ) ||
+               FT_READ_SHORT( end ) )
+            goto Exit;
+          axisCoords[j].startCoord = FT_fdot14ToFixed( start );
+          axisCoords[j].peakCoord = FT_fdot14ToFixed( peak );
+          axisCoords[j].endCoord = FT_fdot14ToFixed( end );
+        }
+      }
+      /* end of region list parse */
+
+      /* parse array of item variation data subtables */
+      if ( FT_STREAM_SEEK( data_offset_array_offset ) )
+        goto Exit;
+
+      if ( FT_NEW_ARRAY( itemStore->varData, itemStore->dataCount ) )
+          goto Exit;
+
+      hvarData = itemStore->varData;
+      for ( i=0; i<itemStore->dataCount; i++ )
+      {
+        if ( FT_READ_ULONG( data_offset ) )
+          goto Exit;
+
+        if ( FT_STREAM_SEEK( table_offset + store_offset + data_offset ) ||
+             FT_READ_USHORT( hvarData->itemCount ) ||
+             FT_READ_USHORT( shortDeltaCount ) ||
+             FT_READ_USHORT( hvarData->regionCount ) )
+          goto Exit;
+
+        /* check some data consistency */
+        if ( shortDeltaCount > hvarData->regionCount )
+        {
+          FT_TRACE2(( "bad short count %d or region count %d\n",
+                      shortDeltaCount, hvarData->regionCount ));
+          error = FT_THROW( Invalid_File_Format );
+          goto Exit;
+        }
+        if ( hvarData->regionCount > itemStore->regionCount )
+        {
+          FT_TRACE2(( "inconsistent regionCount %d in varData[ %d ]\n",
+                      hvarData->regionCount, i ));
+          error = FT_THROW( Invalid_File_Format );
+          goto Exit;
+        }
+
+        /* parse region indices */
+        if ( FT_NEW_ARRAY( hvarData->regionIndices, hvarData->regionCount ) )
+          goto Exit;
+
+        for ( j=0; j<hvarData->regionCount; j++ )
+        {
+          if ( FT_READ_USHORT( hvarData->regionIndices[j] ) )
+            goto Exit;
+          if ( hvarData->regionIndices[j] >= hvarData->regionCount )
+          {
+            FT_TRACE2(( "bad region index %d\n", hvarData->regionIndices[j] ));
+            error = FT_THROW( Invalid_File_Format );
+            goto Exit;
+          }
+        }
+
+        /* parse delta set                                                   */
+        /* on input, deltas are ( shortDeltaCount + regionCount ) bytes each */
+        /* on output, deltas are expanded to regionCount shorts each         */
+        if ( FT_NEW_ARRAY( hvarData->deltaSet, hvarData->regionCount * 
hvarData->itemCount ) )
+          goto Exit;
+
+        /* the delta set is stored as a 2-dimensional array of shorts   */
+        /* sign-extend signed bytes to signed shorts                    */
+        for ( j=0; j<hvarData->itemCount * hvarData->regionCount; )
+        {
+          for ( k=0; k<shortDeltaCount; k++,j++ )
+          {
+            /* read the short deltas */
+            FT_Short delta;
+            if ( FT_READ_SHORT( delta ) )
+              goto Exit;
+            hvarData->deltaSet[j] = delta;
+          }
+          for ( ; k<hvarData->regionCount; k++,j++ )
+          {
+            /* read the (signed) byte deltas */
+            FT_Char delta;
+            if ( FT_READ_CHAR( delta ) )
+              goto Exit;
+            hvarData->deltaSet[j] = delta;
+          }
+        }
+      }
+    }
+    /* end parse item variation store */
+
+    {
+      /* parse width map */
+      GX_WidthMap widthMap;
+      FT_UShort format;
+      FT_UInt entrySize;
+      FT_UInt innerBitCount;
+      FT_UInt innerIndexMask;
+      FT_UInt outerBitCount;
+      FT_UInt i, j;
+
+      widthMap = &blend->hvar_table->widthMap;
+      if ( FT_READ_USHORT( format ) ||
+           FT_READ_USHORT( widthMap->mapCount ) )
+        goto Exit;
+
+      if ( format & 0xFFC0 )
+      {
+        FT_TRACE2(( "bad map format %d\n", format ));
+        error = FT_THROW( Invalid_File_Format );
+        goto Exit;
+      }
+      entrySize = ( ( format & 0x0030 ) >> 4 ) + 1;     /* bytes per entry, 
1,2,3 or 4 */
+      innerBitCount = ( format & 0x000F ) + 1;
+      innerIndexMask = ( 1 << innerBitCount ) - 1;
+      outerBitCount = 8 * entrySize - innerBitCount;
+
+      if ( FT_NEW_ARRAY( widthMap->innerIndex, widthMap->mapCount ) )
+        goto Exit;
+
+      if ( FT_NEW_ARRAY( widthMap->outerIndex, widthMap->mapCount ) )
+        goto Exit;
+
+      for ( i=0; i<widthMap->mapCount; i++ )
+      {
+        FT_UInt mapData = 0;
+        FT_UInt outerIndex, innerIndex;
+
+        /* read map data one unsigned byte at a time, big endian */
+        for ( j=0; j<entrySize; j++ )
+        {
+          FT_Byte data;
+
+          if ( FT_READ_BYTE( data ) )
+            goto Exit;
+          mapData = ( mapData << 8 ) | data;
+        }
+
+        outerIndex = mapData >> innerBitCount;
+        if ( outerIndex >= blend->hvar_table->itemStore.dataCount )
+        {
+          FT_TRACE2(( "outerIndex[ %d ] == %d out of range\n",
+                      i, outerIndex ));
+          error = FT_THROW( Invalid_File_Format );
+          goto Exit;
+        }
+        widthMap->outerIndex[i] = outerIndex;
+
+        innerIndex = mapData & innerIndexMask;
+
+        if ( innerIndex >= blend->hvar_table->itemStore.varData[ outerIndex 
].itemCount )
+        {
+          FT_TRACE2(( "innerIndex[ %d ] == %d out of range\n",
+                      i, innerIndex ));
+          error = FT_THROW( Invalid_File_Format );
+            goto Exit;
+        }
+        widthMap->innerIndex[i] = innerIndex;
+      }
+
+    }   /* end parse width map */
+
+    FT_TRACE2(( "loaded\n" ));
+    error = FT_Err_Ok;
+
+  Exit:
+    if ( error == FT_Err_Ok )
+      blend->hvar_checked = TRUE;
+  }
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Function>                                                            */
+  /*    tt_adjust_advance                                                  */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    Apply HVAR adjustment to advance width of gindex                   */
+  /*                                                                       */
+  /* <In>                                                                  */
+  /*    gindex :: The glyph                                                */
+  /* <InOut>                                                               */
+  /*    face :: The font face.                                             */
+  /*    aadvance :: points to width value                                  */
+  /*                                                                       */
+  FT_EXPORT( void )
+  tt_adjust_advance( TT_Face    face,
+                     FT_UInt    gindex,
+                     FT_UShort  *aadvance )
+  {
+    FT_UInt innerIndex, outerIndex;
+    GX_HVarData varData;
+    FT_UInt master, j;
+    FT_Fixed netAdjustment = 0;     /* accumulated adjustment */
+    FT_Fixed scaledDelta;
+    FT_Short* deltaSet;
+    FT_Fixed delta;
+
+    if ( !face->blend )
+      return;
+
+    if ( !face->blend->hvar_checked )
+    {
+      /* initialize hvar table */
+      ft_var_load_hvar( face );
+    }
+    if ( !face->blend->hvar_checked )
+      return;     /* HVAR parse failed */
+
+    if ( gindex >= face->blend->hvar_table->widthMap.mapCount )
+    {
+      FT_TRACE2(( "gindex %d out of range\n", gindex ));
+        goto Exit;
+    }
+
+    /* trust that HVAR parser has checked indices */
+    outerIndex = face->blend->hvar_table->widthMap.outerIndex[ gindex ];
+    innerIndex = face->blend->hvar_table->widthMap.innerIndex[ gindex ];
+    varData = &face->blend->hvar_table->itemStore.varData[ outerIndex ];
+    deltaSet = &varData->deltaSet[ 
face->blend->hvar_table->itemStore.regionCount * innerIndex ];
+
+    /* see pseudo code from Font Variations Overview */
+    /* outer loop steps through master designs to be blended */
+    for ( master=0; master<varData->regionCount; master++ )
+    {
+      FT_UInt regionIndex = varData->regionIndices[ master ];
+      GX_AxisCoords axis = face->blend->hvar_table->itemStore.varRegionList[ 
regionIndex ].axisList;
+      FT_Fixed scalar = FT_FIXED_ONE;
+
+      /* inner loop steps through axes in this region */
+      for ( j=0; j<face->blend->hvar_table->itemStore.axisCount; j++, axis++ )
+      {
+        FT_Fixed axisScalar;
+
+        /* compute the scalar contribution of this axis */
+        /* ignore invalid ranges */
+        if ( axis->startCoord > axis->peakCoord || axis->peakCoord > 
axis->endCoord )
+          axisScalar = FT_FIXED_ONE;
+        else if ( axis->startCoord < 0 && axis->endCoord > 0 && 
axis->peakCoord != 0 )
+          axisScalar = FT_FIXED_ONE;
+        /* peak of 0 means ignore this axis */
+        else if ( axis->peakCoord == 0 )
+          axisScalar = FT_FIXED_ONE;
+        /* ignore this region if coords are out of range */
+        else if ( face->blend->normalizedcoords[j] < axis->startCoord || 
face->blend->normalizedcoords[j] > axis->endCoord )
+          axisScalar = 0;
+        /* calculate a proportional factor */
+        else
+        {
+          if ( face->blend->normalizedcoords[j] == axis->peakCoord )
+            axisScalar = FT_FIXED_ONE;
+          else if ( face->blend->normalizedcoords[j] < axis->peakCoord )
+            axisScalar = FT_DivFix( face->blend->normalizedcoords[j] - 
axis->startCoord,
+                                    axis->peakCoord - axis->startCoord );
+          else
+            axisScalar = FT_DivFix( axis->endCoord - 
face->blend->normalizedcoords[j],
+                                    axis->endCoord - axis->peakCoord );
+        }
+        /* take product of all the axis scalars */
+        scalar = FT_MulFix( scalar, axisScalar );
+      } /* per-axis loop */
+      FT_TRACE4(( ", %f ", (double)scalar / 65536 ));
+
+      /* get the scaled delta for this region */
+      delta = FT_intToFixed( deltaSet[master] );
+      scaledDelta = FT_MulFix( scalar, delta );
+
+      /* accumulate the adjustments from each region */
+      netAdjustment = netAdjustment + scaledDelta;
+
+    }   /* per-region loop */
+    FT_TRACE4(( "]\n" ));
+
+    /* apply the accumulated adjustment to the default to derive the 
interpolated value */
+    /* TODO check rounding: *aadvance is short */
+    *aadvance += FT_fixedToInt( netAdjustment );
+
+Exit:
+    return;
+  }
+
 
   typedef struct  GX_GVar_Head_
   {
@@ -762,7 +1174,7 @@
   /* <Return>                                                              */
   /*    FreeType error code.  0 means success.                             */
   /*                                                                       */
-  FT_LOCAL_DEF( FT_Error )
+  FT_EXPORT( FT_Error )
   TT_Get_MM_Var( TT_Face      face,
                  FT_MM_Var*  *master )
   {
@@ -778,6 +1190,7 @@
     FT_Var_Axis*         a;
     FT_Var_Named_Style*  ns;
     GX_FVar_Head         fvar_head;
+    FT_Bool              usePsName;
 
     static const FT_Frame_Field  fvar_fields[] =
     {
@@ -824,9 +1237,14 @@
       if ( ( error = face->goto_table( face, TTAG_gvar,
                                        stream, &table_len ) ) != 0 )
       {
-        FT_TRACE1(( "\n"
-                    "TT_Get_MM_Var: `gvar' table is missing\n" ));
-        goto Exit;
+        /* CFF2 is an alternate to gvar here */
+        if ( ( error = face->goto_table( face, TTAG_CFF2,
+                                       stream, &table_len ) ) != 0 )
+        {
+          FT_TRACE1(( "\n"
+                      "TT_Get_MM_Var: `gvar' or `CFF2' table is missing\n" ));
+          goto Exit;
+        }
       }
 
       if ( ( error = face->goto_table( face, TTAG_fvar,
@@ -851,8 +1269,6 @@
            fvar_head.axisSize != 20                                       ||
            /* axisCount limit implied by 16-bit instanceSize */
            fvar_head.axisCount > 0x3FFE                                   ||
-           fvar_head.instanceSize != 4 + 4 * fvar_head.axisCount          ||
-           /* instanceCount limit implied by limited range of name IDs */
            fvar_head.instanceCount > 0x7EFF                               ||
            fvar_head.offsetToData + fvar_head.axisCount * 20U +
              fvar_head.instanceCount * fvar_head.instanceSize > table_len )
@@ -862,7 +1278,21 @@
         error = FT_THROW( Invalid_Table );
         goto Exit;
       }
-
+      if ( fvar_head.instanceSize == 4 + 4 * fvar_head.axisCount )
+      {
+        usePsName = FALSE;
+      }
+      else if ( fvar_head.instanceSize == 6 + 4 * fvar_head.axisCount )
+      {
+        usePsName = TRUE;
+      }
+      else
+      {
+        FT_TRACE1(( "\n"
+                    "TT_Get_MM_Var: invalid `fvar' header\n" ));
+        error = FT_THROW( Invalid_Table );
+        goto Exit;
+      }
       FT_TRACE2(( "loaded\n" ));
 
       FT_TRACE5(( "number of GX style axes: %d\n", fvar_head.axisCount ));
@@ -952,8 +1382,17 @@
       ns = mmvar->namedstyle;
       for ( i = 0; i < fvar_head.instanceCount; i++, ns++ )
       {
-        if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) )
-          goto Exit;
+        /* PostScript names add 2 bytes to the instance record size */
+        if ( usePsName )
+        {
+          if ( FT_FRAME_ENTER( 6L + 4L * fvar_head.axisCount ) )
+            goto Exit;
+        }
+        else
+        {
+          if ( FT_FRAME_ENTER( 4L + 4L * fvar_head.axisCount ) )
+            goto Exit;
+        }
 
         ns->strid       =    FT_GET_USHORT();
         (void) /* flags = */ FT_GET_USHORT();
@@ -961,6 +1400,9 @@
         for ( j = 0; j < fvar_head.axisCount; j++ )
           ns->coords[j] = FT_GET_LONG();
 
+        if ( usePsName )
+          ns->psid = FT_GET_USHORT();
+
         FT_FRAME_EXIT();
       }
     }
@@ -1042,7 +1484,7 @@
   /* <Return>                                                              */
   /*    FreeType error code.  0 means success.                             */
   /*                                                                       */
-  FT_LOCAL_DEF( FT_Error )
+  FT_EXPORT( FT_Error )
   TT_Set_MM_Blend( TT_Face    face,
                    FT_UInt    num_coords,
                    FT_Fixed*  coords )
@@ -1097,7 +1539,7 @@
 
     FT_TRACE5(( "\n" ));
 
-    if ( blend->glyphoffsets == NULL )
+    if ( !face->isCFF2 && blend->glyphoffsets == NULL )
       if ( ( error = ft_var_load_gvar( face ) ) != 0 )
         goto Exit;
 
@@ -1202,7 +1644,7 @@
   /* <Return>                                                              */
   /*    FreeType error code.  0 means success.                             */
   /*                                                                       */
-  FT_LOCAL_DEF( FT_Error )
+  FT_EXPORT( FT_Error )
   TT_Set_Var_Design( TT_Face    face,
                      FT_UInt    num_coords,
                      FT_Fixed*  coords )
@@ -1310,6 +1752,50 @@
   }
 
 
+  FT_EXPORT( FT_Error )
+  TT_Get_Var_Blend( TT_Face    face,
+                    FT_UInt    num_coords,
+                    FT_Fixed*  coords )
+  {
+    FT_Error    error  = FT_Err_Ok;
+    GX_Blend    blend;
+    FT_UInt     i;
+
+    face->doblend = FALSE;
+
+    if ( face->blend == NULL )
+    {
+      if ( ( error = TT_Get_MM_Var( face, NULL ) ) != 0 )
+        return error;
+    }
+
+    blend = face->blend;
+
+    if ( num_coords > blend->num_axis )
+    {
+      FT_TRACE2(( "TT_Get_MM_Blend: only using first %d of %d coordinates\n",
+                  blend->num_axis, num_coords ));
+      num_coords = blend->num_axis;
+    }
+
+    for (i = 0; i < num_coords; ++i)
+    {
+      coords[i] = blend->normalizedcoords[i];
+    }
+
+    return FT_Err_Ok;
+  }
+
+  FT_EXPORT( FT_Error )
+  TT_Get_Var_Design( TT_Face    face,
+                     FT_UInt    num_coords,
+                     FT_Fixed*  coords )
+  {
+    /* TODO: Implement this function. */
+    return FT_THROW( Unimplemented_Feature );
+  }
+
+
   /*************************************************************************/
   /*************************************************************************/
   /*****                                                               *****/
@@ -2153,7 +2639,7 @@
   /* <Description>                                                         */
   /*    Free the blend internal data structure.                            */
   /*                                                                       */
-  FT_LOCAL_DEF( void )
+  FT_EXPORT( void )
   tt_done_blend( FT_Memory  memory,
                  GX_Blend   blend )
   {
@@ -2172,6 +2658,31 @@
         FT_FREE( blend->avar_segment );
       }
 
+      if ( blend->hvar_table != NULL )
+      {
+        FT_UInt i;
+        if ( blend->hvar_table->itemStore.varData )
+        {
+          for ( i=0; i<blend->hvar_table->itemStore.dataCount; i++ )
+          {
+            FT_FREE( blend->hvar_table->itemStore.varData[i].regionIndices );
+            FT_FREE( blend->hvar_table->itemStore.varData[i].deltaSet );
+          }
+          FT_FREE( blend->hvar_table->itemStore.varData );
+        }
+        if ( blend->hvar_table->itemStore.varRegionList )
+        {
+          for ( i=0; i<blend->hvar_table->itemStore.regionCount; i++ )
+          {
+            FT_FREE( blend->hvar_table->itemStore.varRegionList[i].axisList );
+          }
+          FT_FREE( blend->hvar_table->itemStore.varRegionList );
+        }
+        FT_FREE( blend->hvar_table->widthMap.innerIndex );
+        FT_FREE( blend->hvar_table->widthMap.outerIndex );
+        FT_FREE( blend->hvar_table );
+      }
+
       FT_FREE( blend->tuplecoords );
       FT_FREE( blend->glyphoffsets );
       FT_FREE( blend );
diff --git a/src/truetype/ttgxvar.h b/src/truetype/ttgxvar.h
index aa8f6ea..956000c 100644
--- a/src/truetype/ttgxvar.h
+++ b/src/truetype/ttgxvar.h
@@ -64,6 +64,64 @@ FT_BEGIN_HEADER
   /*************************************************************************/
   /*                                                                       */
   /* <Struct>                                                              */
+  /*    GX_HVarRec                                                         */
+  /*                                                                       */
+  /* <Description>                                                         */
+  /*    Data from the `HVAR' table.                                        */
+  /*                                                                       */
+  /* See similar variation store structures in cfftypes.h                  */
+  /*                                                                       */
+  typedef struct GX_HVarData_
+  {
+    FT_UInt         itemCount;          /* # delta sets per item           */
+    FT_UInt         regionCount;        /* # regions in this var data      */
+    FT_UInt*        regionIndices;      /* array of regionCount indices    */
+                                        /* these index the varRegionList   */
+    FT_Short*       deltaSet;           /* array of itemCount deltas       */
+                                        /* use innerIndex for this array */
+  } GX_HVarDataRec, *GX_HVarData;
+
+  typedef struct GX_AxisCoords_     /* contribution of one axis to a region */
+  {
+    FT_Fixed        startCoord;
+    FT_Fixed        peakCoord;      /* zero means no effect (factor = 1) */
+    FT_Fixed        endCoord;
+  } GX_AxisCoordsRec, *GX_AxisCoords;
+
+  typedef struct GX_HVarRegion_
+  {
+    GX_AxisCoords   axisList;        /* array of axisCount records */
+  } GX_HVarRegionRec, *GX_HVarRegion;
+
+  typedef struct  GX_HVStoreRec_    /* HVAR item variation store  */
+  {
+    FT_UInt         dataCount;
+    GX_HVarData     varData;        /* array of dataCount records */
+                                    /* use outerIndex for this array */
+    FT_UShort       axisCount;
+    FT_UInt         regionCount;    /* total # regions defined    */
+    GX_HVarRegion   varRegionList;
+
+  } GX_HVStoreRec, *GX_HVStore;
+
+  typedef struct GX_WidthMapRec_
+  {
+      FT_UInt       mapCount;
+      FT_UInt*      outerIndex;     /* indices to item var data */
+      FT_UInt*      innerIndex;     /* indices to delta set */
+  } GX_WidthMapRec, *GX_WidthMap;
+
+  typedef struct  GX_HVarRec_
+  {
+    GX_HVStoreRec      itemStore;       /* Item Variation Store  */
+    GX_WidthMapRec     widthMap;      /* Advance Width Mapping */
+    /* GX_LSBMap       LsbMap;         Not implemented       */
+    /* GX_RSBMap       RsbMap;         Not implemented       */
+  } GX_HVarTableRec, *GX_HVarTable;
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* <Struct>                                                              */
   /*    GX_BlendRec                                                        */
   /*                                                                       */
   /* <Description>                                                         */
@@ -89,6 +147,9 @@ FT_BEGIN_HEADER
     FT_Bool         avar_checked;
     GX_AVarSegment  avar_segment;
 
+    FT_Bool         hvar_checked;
+    GX_HVarTable    hvar_table;
+
     FT_UInt         tuplecount;      /* shared tuples in `gvar'           */
     FT_Fixed*       tuplecoords;     /* tuplecoords[tuplecount][num_axis] */
 
@@ -143,20 +204,29 @@ FT_BEGIN_HEADER
 #define TTAG_slnt  FT_MAKE_TAG( 's', 'l', 'n', 't' )
 
 
-  FT_LOCAL( FT_Error )
+  FT_EXPORT( FT_Error )
   TT_Set_MM_Blend( TT_Face    face,
                    FT_UInt    num_coords,
                    FT_Fixed*  coords );
 
-  FT_LOCAL( FT_Error )
+  FT_EXPORT( FT_Error )
   TT_Set_Var_Design( TT_Face    face,
                      FT_UInt    num_coords,
                      FT_Fixed*  coords );
 
-  FT_LOCAL( FT_Error )
+  FT_EXPORT( FT_Error )
   TT_Get_MM_Var( TT_Face      face,
                  FT_MM_Var*  *master );
 
+  FT_EXPORT( FT_Error )
+  TT_Get_Var_Design( TT_Face    face,
+                     FT_UInt    num_coords,
+                     FT_Fixed*  coords );
+
+  FT_EXPORT( FT_Error )
+  TT_Get_Var_Blend( TT_Face    face,
+                    FT_UInt    num_coords,
+                    FT_Fixed*  coords );
 
   FT_LOCAL( FT_Error )
   tt_face_vary_cvt( TT_Face    face,
@@ -169,8 +239,12 @@ FT_BEGIN_HEADER
                               FT_Outline*  outline,
                               FT_UInt      n_points );
 
+  FT_EXPORT( void )
+  tt_adjust_advance( TT_Face    face,
+                     FT_UInt    gindex,
+                     FT_UShort  *aadvance );
 
-  FT_LOCAL( void )
+  FT_EXPORT( void )
   tt_done_blend( FT_Memory  memory,
                  GX_Blend   blend );
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]