freetype-commit
[Top][All Lists]
Advanced

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

[freetype2] gsoc-anurag-2023 3926f77ef: Begin 'preload' font driver (bas


From: Werner Lemberg
Subject: [freetype2] gsoc-anurag-2023 3926f77ef: Begin 'preload' font driver (based on truetype)
Date: Tue, 8 Aug 2023 16:17:20 -0400 (EDT)

branch: gsoc-anurag-2023
commit 3926f77efdef679d06c5018ede966c9bffcf62ed
Author: Anurag Thakur <anurag105csec21@bpitindia.edu.in>
Commit: Anurag Thakur <anurag105csec21@bpitindia.edu.in>

    Begin 'preload' font driver (based on truetype)
---
 include/freetype/config/ftmodule.h |    1 +
 modules.cfg                        |    6 +
 src/preload/module.mk              |   23 +
 src/preload/preload.c              |   29 +
 src/preload/rules.mk               |   75 +
 src/preload/ttdriver.c             |  691 ++++
 src/preload/ttdriver.h             |   35 +
 src/preload/tterrors.h             |   42 +
 src/preload/ttgload.c              | 2743 +++++++++++++
 src/preload/ttgload.h              |   61 +
 src/preload/ttgxvar.c              | 4661 ++++++++++++++++++++++
 src/preload/ttgxvar.h              |  453 +++
 src/preload/ttinterp.c             | 7753 ++++++++++++++++++++++++++++++++++++
 src/preload/ttinterp.h             |  465 +++
 src/preload/ttobjs.c               | 1539 +++++++
 src/preload/ttobjs.h               |  426 ++
 src/preload/ttpload.c              |  665 ++++
 src/preload/ttpload.h              |   74 +
 18 files changed, 19742 insertions(+)

diff --git a/include/freetype/config/ftmodule.h 
b/include/freetype/config/ftmodule.h
index 3b692aa28..86b5d062c 100644
--- a/include/freetype/config/ftmodule.h
+++ b/include/freetype/config/ftmodule.h
@@ -11,6 +11,7 @@
  */
 
 FT_USE_MODULE( FT_Module_Class, autofit_module_class )
+FT_USE_MODULE( FT_Driver_ClassRec, preload_driver_class )
 FT_USE_MODULE( FT_Driver_ClassRec, tt_driver_class )
 FT_USE_MODULE( FT_Driver_ClassRec, t1_driver_class )
 FT_USE_MODULE( FT_Driver_ClassRec, cff_driver_class )
diff --git a/modules.cfg b/modules.cfg
index cb49d79c9..8ffee3780 100644
--- a/modules.cfg
+++ b/modules.cfg
@@ -34,6 +34,12 @@
 # This driver needs the `sfnt' module.
 FONT_MODULES += truetype
 
+
+# TrueType preload font driver.
+#
+# This driver needs the `sfnt' module.
+FONT_MODULES += preload
+
 # PostScript Type 1 font driver.
 #
 # This driver needs the `psaux', `pshinter', and `psnames' modules.
diff --git a/src/preload/module.mk b/src/preload/module.mk
new file mode 100644
index 000000000..5b31e44d4
--- /dev/null
+++ b/src/preload/module.mk
@@ -0,0 +1,23 @@
+#
+# FreeType 2 TrueType module definition
+#
+
+
+# Copyright (C) 1996-2023 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT.  By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+FTMODULE_H_COMMANDS += PRELOAD_DRIVER
+
+define PRELOAD_DRIVER
+$(OPEN_DRIVER) FT_Driver_ClassRec, preload_driver_class $(CLOSE_DRIVER)
+$(ECHO_DRIVER)preload  $(ECHO_DRIVER_DESC)Windows/Mac font files with 
extension *.ttf or *.ttc$(ECHO_DRIVER_DONE)
+endef
+
+# EOF
diff --git a/src/preload/preload.c b/src/preload/preload.c
new file mode 100644
index 000000000..fcc0ea334
--- /dev/null
+++ b/src/preload/preload.c
@@ -0,0 +1,29 @@
+/****************************************************************************
+ *
+ * truetype.c
+ *
+ *   FreeType TrueType driver component (body only).
+ *
+ * Copyright (C) 1996-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+#define FT_MAKE_OPTION_SINGLE_OBJECT
+
+#include "ttdriver.c"   /* driver interface    */
+#include "ttgload.c"    /* glyph loader        */
+#include "ttgxvar.c"    /* gx distortable font */
+#include "ttinterp.c"
+#include "ttobjs.c"     /* object manager      */
+#include "ttpload.c"    /* tables loader       */
+
+
+/* END */
diff --git a/src/preload/rules.mk b/src/preload/rules.mk
new file mode 100644
index 000000000..db9345752
--- /dev/null
+++ b/src/preload/rules.mk
@@ -0,0 +1,75 @@
+#
+# FreeType 2 TrueType driver configuration rules
+#
+
+
+# Copyright (C) 1996-2023 by
+# David Turner, Robert Wilhelm, and Werner Lemberg.
+#
+# This file is part of the FreeType project, and may only be used, modified,
+# and distributed under the terms of the FreeType project license,
+# LICENSE.TXT.  By continuing to use, modify, or distribute this file you
+# indicate that you have read the license and understand and accept it
+# fully.
+
+
+# TrueType driver directory
+#
+TT_DIR := $(SRC_DIR)/preload
+
+
+# compilation flags for the driver
+#
+TT_COMPILE := $(CC) $(ANSIFLAGS)                           \
+                    $I$(subst /,$(COMPILER_SEP),$(TT_DIR)) \
+                    $(INCLUDE_FLAGS)                       \
+                    $(FT_CFLAGS)
+
+
+# TrueType driver sources (i.e., C files)
+#
+TT_DRV_SRC := $(TT_DIR)/ttdriver.c \
+              $(TT_DIR)/ttgload.c  \
+              $(TT_DIR)/ttgxvar.c  \
+              $(TT_DIR)/ttinterp.c \
+              $(TT_DIR)/ttobjs.c   \
+              $(TT_DIR)/ttpload.c
+
+# TrueType driver headers
+#
+TT_DRV_H := $(TT_DRV_SRC:%.c=%.h) \
+            $(TT_DIR)/tterrors.h
+
+
+# TrueType driver object(s)
+#
+#   TT_DRV_OBJ_M is used during `multi' builds
+#   TT_DRV_OBJ_S is used during `single' builds
+#
+TT_DRV_OBJ_M := $(TT_DRV_SRC:$(TT_DIR)/%.c=$(OBJ_DIR)/%.$O)
+TT_DRV_OBJ_S := $(OBJ_DIR)/preload.$O
+
+# TrueType driver source file for single build
+#
+TT_DRV_SRC_S := $(TT_DIR)/preload.c
+
+
+# TrueType driver - single object
+#
+$(TT_DRV_OBJ_S): $(TT_DRV_SRC_S) $(TT_DRV_SRC) $(FREETYPE_H) $(TT_DRV_H)
+       $(TT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $(TT_DRV_SRC_S))
+
+
+# driver - multiple objects
+#
+$(OBJ_DIR)/%.$O: $(TT_DIR)/%.c $(FREETYPE_H) $(TT_DRV_H)
+       $(TT_COMPILE) $T$(subst /,$(COMPILER_SEP),$@ $<)
+
+
+# update main driver object lists
+#
+DRV_OBJS_S += $(TT_DRV_OBJ_S)
+DRV_OBJS_M += $(TT_DRV_OBJ_M)
+
+
+# EOF
diff --git a/src/preload/ttdriver.c b/src/preload/ttdriver.c
new file mode 100644
index 000000000..d1496fec7
--- /dev/null
+++ b/src/preload/ttdriver.c
@@ -0,0 +1,691 @@
+/****************************************************************************
+ *
+ * ttdriver.c
+ *
+ *   TrueType font driver implementation (body).
+ *
+ * Copyright (C) 1996-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/sfnt.h>
+#include <freetype/internal/services/svfntfmt.h>
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include <freetype/ftmm.h>
+#include <freetype/internal/services/svmm.h>
+#include <freetype/internal/services/svmetric.h>
+#endif
+
+#include <freetype/internal/services/svtteng.h>
+#include <freetype/internal/services/svttglyf.h>
+#include <freetype/internal/services/svprop.h>
+#include <freetype/ftdriver.h>
+
+#include "ttdriver.h"
+#include "ttgload.h"
+#include "ttpload.h"
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include "ttgxvar.h"
+#endif
+
+#include "tterrors.h"
+
+
+  /**************************************************************************
+   *
+   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
+   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
+   * messages during execution.
+   */
+#undef  FT_COMPONENT
+#define FT_COMPONENT  ttdriver
+
+
+  /*
+   * PROPERTY SERVICE
+   *
+   */
+  FT_CALLBACK_DEF( FT_Error )
+  tt_property_set( FT_Module    module,         /* TT_Driver */
+                   const char*  property_name,
+                   const void*  value,
+                   FT_Bool      value_is_string )
+  {
+    FT_Error   error  = FT_Err_Ok;
+    TT_Driver  driver = (TT_Driver)module;
+
+#ifndef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+    FT_UNUSED( value_is_string );
+#endif
+
+
+    if ( !ft_strcmp( property_name, "interpreter-version" ) )
+    {
+      FT_UInt  interpreter_version;
+
+
+#ifdef FT_CONFIG_OPTION_ENVIRONMENT_PROPERTIES
+      if ( value_is_string )
+      {
+        const char*  s = (const char*)value;
+
+
+        interpreter_version = (FT_UInt)ft_strtol( s, NULL, 10 );
+      }
+      else
+#endif
+      {
+        FT_UInt*  iv = (FT_UInt*)value;
+
+
+        interpreter_version = *iv;
+      }
+
+      switch ( interpreter_version )
+      {
+      case TT_INTERPRETER_VERSION_35:
+        driver->interpreter_version = TT_INTERPRETER_VERSION_35;
+        break;
+
+      case TT_INTERPRETER_VERSION_38:
+      case TT_INTERPRETER_VERSION_40:
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+        driver->interpreter_version = TT_INTERPRETER_VERSION_40;
+      break;
+#endif
+
+      default:
+        error = FT_ERR( Unimplemented_Feature );
+      }
+
+      return error;
+    }
+
+    FT_TRACE2(( "tt_property_set: missing property `%s'\n",
+                property_name ));
+    return FT_THROW( Missing_Property );
+  }
+
+
+  FT_CALLBACK_DEF( FT_Error )
+  tt_property_get( FT_Module    module,         /* TT_Driver */
+                   const char*  property_name,
+                   void*        value )
+  {
+    FT_Error   error  = FT_Err_Ok;
+    TT_Driver  driver = (TT_Driver)module;
+
+    FT_UInt  interpreter_version = driver->interpreter_version;
+
+
+    if ( !ft_strcmp( property_name, "interpreter-version" ) )
+    {
+      FT_UInt*  val = (FT_UInt*)value;
+
+
+      *val = interpreter_version;
+
+      return error;
+    }
+
+    FT_TRACE2(( "tt_property_get: missing property `%s'\n",
+                property_name ));
+    return FT_THROW( Missing_Property );
+  }
+
+
+  FT_DEFINE_SERVICE_PROPERTIESREC(
+    tt_service_properties,
+
+    tt_property_set,  /* FT_Properties_SetFunc set_property */
+    tt_property_get   /* FT_Properties_GetFunc get_property */
+  )
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*************************************************************************/
+  /****                                                                 ****/
+  /****                                                                 ****/
+  /****                          F A C E S                              ****/
+  /****                                                                 ****/
+  /****                                                                 ****/
+  /*************************************************************************/
+  /*************************************************************************/
+  /*************************************************************************/
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_get_kerning
+   *
+   * @Description:
+   *   A driver method used to return the kerning vector between two
+   *   glyphs of the same face.
+   *
+   * @Input:
+   *   face ::
+   *     A handle to the source face object.
+   *
+   *   left_glyph ::
+   *     The index of the left glyph in the kern pair.
+   *
+   *   right_glyph ::
+   *     The index of the right glyph in the kern pair.
+   *
+   * @Output:
+   *   kerning ::
+   *     The kerning vector.  This is in font units for
+   *     scalable formats, and in pixels for fixed-sizes
+   *     formats.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   *
+   * @Note:
+   *   Only horizontal layouts (left-to-right & right-to-left) are
+   *   supported by this function.  Other layouts, or more sophisticated
+   *   kernings, are out of scope of this method (the basic driver
+   *   interface is meant to be simple).
+   *
+   *   They can be implemented by format-specific interfaces.
+   */
+  FT_CALLBACK_DEF( FT_Error )
+  tt_get_kerning( FT_Face     face,        /* TT_Face */
+                  FT_UInt     left_glyph,
+                  FT_UInt     right_glyph,
+                  FT_Vector*  kerning )
+  {
+    TT_Face       ttface = (TT_Face)face;
+    SFNT_Service  sfnt   = (SFNT_Service)ttface->sfnt;
+
+
+    kerning->x = 0;
+    kerning->y = 0;
+
+    if ( sfnt )
+      kerning->x = sfnt->get_kerning( ttface, left_glyph, right_glyph );
+
+    return 0;
+  }
+
+
+  FT_CALLBACK_DEF( FT_Error )
+  tt_get_advances( FT_Face    face,      /* TT_Face */
+                   FT_UInt    start,
+                   FT_UInt    count,
+                   FT_Int32   flags,
+                   FT_Fixed  *advances )
+  {
+    FT_UInt  nn;
+    TT_Face  ttface = (TT_Face)face;
+
+
+    /* XXX: TODO: check for sbits */
+
+    if ( flags & FT_LOAD_VERTICAL_LAYOUT )
+    {
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+      /* no fast retrieval for blended MM fonts without VVAR table */
+      if ( ( FT_IS_NAMED_INSTANCE( face ) || FT_IS_VARIATION( face ) ) &&
+           !( ttface->variation_support & TT_FACE_FLAG_VAR_VADVANCE )  )
+        return FT_THROW( Unimplemented_Feature );
+#endif
+
+      for ( nn = 0; nn < count; nn++ )
+      {
+        FT_Short   tsb;
+        FT_UShort  ah;
+
+
+        /* since we don't need `tsb', we use zero for `yMax' parameter */
+        TT_Get_VMetrics( ttface, start + nn, 0, &tsb, &ah );
+        advances[nn] = ah;
+      }
+    }
+    else
+    {
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+      /* no fast retrieval for blended MM fonts without HVAR table */
+      if ( ( FT_IS_NAMED_INSTANCE( face ) || FT_IS_VARIATION( face ) ) &&
+           !( ttface->variation_support & TT_FACE_FLAG_VAR_HADVANCE )  )
+        return FT_THROW( Unimplemented_Feature );
+#endif
+
+      for ( nn = 0; nn < count; nn++ )
+      {
+        FT_Short   lsb;
+        FT_UShort  aw;
+
+
+        TT_Get_HMetrics( ttface, start + nn, &lsb, &aw );
+        advances[nn] = aw;
+      }
+    }
+
+    return FT_Err_Ok;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*************************************************************************/
+  /****                                                                 ****/
+  /****                                                                 ****/
+  /****                           S I Z E S                             ****/
+  /****                                                                 ****/
+  /****                                                                 ****/
+  /*************************************************************************/
+  /*************************************************************************/
+  /*************************************************************************/
+
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+  FT_CALLBACK_DEF( FT_Error )
+  tt_size_select( FT_Size   size,
+                  FT_ULong  strike_index )
+  {
+    TT_Face   ttface = (TT_Face)size->face;
+    TT_Size   ttsize = (TT_Size)size;
+    FT_Error  error  = FT_Err_Ok;
+
+
+    ttsize->strike_index = strike_index;
+
+    if ( FT_IS_SCALABLE( size->face ) )
+    {
+      /* use the scaled metrics, even when tt_size_reset fails */
+      FT_Select_Metrics( size->face, strike_index );
+
+      tt_size_reset( ttsize ); /* ignore return value */
+    }
+    else
+    {
+      SFNT_Service      sfnt         = (SFNT_Service)ttface->sfnt;
+      FT_Size_Metrics*  size_metrics = &size->metrics;
+
+
+      error = sfnt->load_strike_metrics( ttface,
+                                         strike_index,
+                                         size_metrics );
+      if ( error )
+        ttsize->strike_index = 0xFFFFFFFFUL;
+    }
+
+    return error;
+  }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+
+  FT_CALLBACK_DEF( FT_Error )
+  tt_size_request( FT_Size          size,
+                   FT_Size_Request  req )
+  {
+    TT_Size   ttsize = (TT_Size)size;
+    FT_Error  error  = FT_Err_Ok;
+
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+    if ( FT_HAS_FIXED_SIZES( size->face ) )
+    {
+      TT_Face       ttface = (TT_Face)size->face;
+      SFNT_Service  sfnt   = (SFNT_Service)ttface->sfnt;
+      FT_ULong      strike_index;
+
+
+      error = sfnt->set_sbit_strike( ttface, req, &strike_index );
+
+      if ( error )
+        ttsize->strike_index = 0xFFFFFFFFUL;
+      else
+        return tt_size_select( size, strike_index );
+    }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+    {
+      FT_Error  err = FT_Request_Metrics( size->face, req );
+
+
+      if ( err )
+      {
+        error = err;
+        goto Exit;
+      }
+    }
+
+    if ( FT_IS_SCALABLE( size->face ) )
+    {
+      error = tt_size_reset( ttsize );
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+      /* for the `MPS' bytecode instruction we need the point size */
+      if ( !error )
+      {
+        FT_UInt  resolution =
+                   ttsize->metrics->x_ppem > ttsize->metrics->y_ppem
+                     ? req->horiResolution
+                     : req->vertResolution;
+
+
+        /* if we don't have a resolution value, assume 72dpi */
+        if ( req->type == FT_SIZE_REQUEST_TYPE_SCALES ||
+             !resolution                              )
+          resolution = 72;
+
+        ttsize->point_size = FT_MulDiv( ttsize->ttmetrics.ppem,
+                                        64 * 72,
+                                        resolution );
+      }
+#endif
+    }
+
+  Exit:
+    return error;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_glyph_load
+   *
+   * @Description:
+   *   A driver method used to load a glyph within a given glyph slot.
+   *
+   * @Input:
+   *   slot ::
+   *     A handle to the target slot object where the glyph
+   *     will be loaded.
+   *
+   *   size ::
+   *     A handle to the source face size at which the glyph
+   *     must be scaled, loaded, etc.
+   *
+   *   glyph_index ::
+   *     The index of the glyph in the font file.
+   *
+   *   load_flags ::
+   *     A flag indicating what to load for this glyph.  The
+   *     FT_LOAD_XXX constants can be used to control the
+   *     glyph loading process (e.g., whether the outline
+   *     should be scaled, whether to load bitmaps or not,
+   *     whether to hint the outline, etc).
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_CALLBACK_DEF( FT_Error )
+  tt_glyph_load( FT_GlyphSlot  slot,        /* TT_GlyphSlot */
+                 FT_Size       size,        /* TT_Size      */
+                 FT_UInt       glyph_index,
+                 FT_Int32      load_flags )
+  {
+    TT_GlyphSlot  ttslot = (TT_GlyphSlot)slot;
+    TT_Size       ttsize = (TT_Size)size;
+    FT_Face       face   = ttslot->face;
+    FT_Error      error;
+
+
+    if ( !slot )
+      return FT_THROW( Invalid_Slot_Handle );
+
+    if ( !size )
+      return FT_THROW( Invalid_Size_Handle );
+
+    if ( !face )
+      return FT_THROW( Invalid_Face_Handle );
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+    if ( glyph_index >= (FT_UInt)face->num_glyphs &&
+         !face->internal->incremental_interface   )
+#else
+    if ( glyph_index >= (FT_UInt)face->num_glyphs )
+#endif
+      return FT_THROW( Invalid_Argument );
+
+    if ( load_flags & FT_LOAD_NO_HINTING )
+    {
+      /* both FT_LOAD_NO_HINTING and FT_LOAD_NO_AUTOHINT   */
+      /* are necessary to disable hinting for tricky fonts */
+
+      if ( FT_IS_TRICKY( face ) )
+        load_flags &= ~FT_LOAD_NO_HINTING;
+
+      if ( load_flags & FT_LOAD_NO_AUTOHINT )
+        load_flags |= FT_LOAD_NO_HINTING;
+    }
+
+    if ( load_flags & ( FT_LOAD_NO_RECURSE | FT_LOAD_NO_SCALE ) )
+    {
+      load_flags |= FT_LOAD_NO_BITMAP | FT_LOAD_NO_SCALE;
+
+      if ( !FT_IS_TRICKY( face ) )
+        load_flags |= FT_LOAD_NO_HINTING;
+    }
+
+    /* use hinted metrics only if we load a glyph with hinting */
+    ttsize->metrics = ( load_flags & FT_LOAD_NO_HINTING )
+                        ? &size->metrics
+                        : &ttsize->hinted_metrics;
+
+    /* now fill in the glyph slot with outline/bitmap/layered */
+    error = TT_Load_Glyph( ttsize, ttslot, glyph_index, load_flags );
+
+    /* force drop-out mode to 2 - irrelevant now */
+    /* slot->outline.dropout_mode = 2; */
+
+    return error;
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*************************************************************************/
+  /****                                                                 ****/
+  /****                                                                 ****/
+  /****                D R I V E R  I N T E R F A C E                   ****/
+  /****                                                                 ****/
+  /****                                                                 ****/
+  /*************************************************************************/
+  /*************************************************************************/
+  /*************************************************************************/
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+  FT_DEFINE_SERVICE_MULTIMASTERSREC(
+    tt_service_gx_multi_masters,
+
+    NULL,                  /* FT_Get_MM_Func              get_mm               
      */
+    NULL,                  /* FT_Set_MM_Design_Func       set_mm_design        
      */
+    TT_Set_MM_Blend,       /* FT_Set_MM_Blend_Func        set_mm_blend         
      */
+    TT_Get_MM_Blend,       /* FT_Get_MM_Blend_Func        get_mm_blend         
      */
+    TT_Get_MM_Var,         /* FT_Get_MM_Var_Func          get_mm_var           
      */
+    TT_Set_Var_Design,     /* FT_Set_Var_Design_Func      set_var_design       
      */
+    TT_Get_Var_Design,     /* FT_Get_Var_Design_Func      get_var_design       
      */
+    TT_Set_Named_Instance, /* FT_Set_Named_Instance_Func  set_named_instance   
      */
+    TT_Get_Default_Named_Instance,
+                    /* FT_Get_Default_Named_Instance_Func 
get_default_named_instance */
+    NULL,                  /* FT_Set_MM_WeightVector_Func set_mm_weightvector  
      */
+    NULL,                  /* FT_Get_MM_WeightVector_Func get_mm_weightvector  
      */
+
+    tt_construct_ps_name,  /* FT_Construct_PS_Name_Func   construct_ps_name    
      */
+    tt_var_load_delta_set_index_mapping,
+                    /* FT_Var_Load_Delta_Set_Idx_Map_Func 
load_delta_set_idx_map     */
+    tt_var_load_item_variation_store,
+                    /* FT_Var_Load_Item_Var_Store_Func    
load_item_variation_store  */
+    tt_var_get_item_delta, /* FT_Var_Get_Item_Delta_Func  get_item_delta       
      */
+    tt_var_done_item_variation_store,
+                    /* FT_Var_Done_Item_Var_Store_Func    
done_item_variation_store  */
+    tt_var_done_delta_set_index_map,
+                    /* FT_Var_Done_Delta_Set_Idx_Map_Func 
done_delta_set_index_map   */
+    tt_get_var_blend,      /* FT_Get_Var_Blend_Func       get_var_blend        
      */
+    tt_done_blend          /* FT_Done_Blend_Func          done_blend           
      */
+  )
+
+  FT_DEFINE_SERVICE_METRICSVARIATIONSREC(
+    tt_service_metrics_variations,
+
+    tt_hadvance_adjust,   /* FT_HAdvance_Adjust_Func hadvance_adjust */
+    NULL,                 /* FT_LSB_Adjust_Func      lsb_adjust      */
+    NULL,                 /* FT_RSB_Adjust_Func      rsb_adjust      */
+
+    tt_vadvance_adjust,   /* FT_VAdvance_Adjust_Func vadvance_adjust */
+    NULL,                 /* FT_TSB_Adjust_Func      tsb_adjust      */
+    NULL,                 /* FT_BSB_Adjust_Func      bsb_adjust      */
+    NULL,                 /* FT_VOrg_Adjust_Func     vorg_adjust     */
+
+    tt_apply_mvar,        /* FT_Metrics_Adjust_Func  metrics_adjust  */
+    tt_size_reset_height  /* FT_Size_Reset_Func      size_reset      */
+  )
+
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+
+  static const FT_Service_TrueTypeEngineRec  tt_service_truetype_engine =
+  {
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+    FT_TRUETYPE_ENGINE_TYPE_PATENTED
+
+#else /* !TT_USE_BYTECODE_INTERPRETER */
+
+    FT_TRUETYPE_ENGINE_TYPE_NONE
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+  };
+
+
+  FT_DEFINE_SERVICE_TTGLYFREC(
+    tt_service_truetype_glyf,
+
+    (TT_Glyf_GetLocationFunc)tt_face_get_location      /* get_location */
+  )
+
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+  FT_DEFINE_SERVICEDESCREC6(
+    tt_services,
+
+    FT_SERVICE_ID_FONT_FORMAT,        FT_FONT_FORMAT_TRUETYPE,
+    FT_SERVICE_ID_MULTI_MASTERS,      &tt_service_gx_multi_masters,
+    FT_SERVICE_ID_METRICS_VARIATIONS, &tt_service_metrics_variations,
+    FT_SERVICE_ID_TRUETYPE_ENGINE,    &tt_service_truetype_engine,
+    FT_SERVICE_ID_TT_GLYF,            &tt_service_truetype_glyf,
+    FT_SERVICE_ID_PROPERTIES,         &tt_service_properties )
+#else
+  FT_DEFINE_SERVICEDESCREC4(
+    tt_services,
+
+    FT_SERVICE_ID_FONT_FORMAT,     FT_FONT_FORMAT_TRUETYPE,
+    FT_SERVICE_ID_TRUETYPE_ENGINE, &tt_service_truetype_engine,
+    FT_SERVICE_ID_TT_GLYF,         &tt_service_truetype_glyf,
+    FT_SERVICE_ID_PROPERTIES,      &tt_service_properties )
+#endif
+
+
+  FT_CALLBACK_DEF( FT_Module_Interface )
+  tt_get_interface( FT_Module    driver,    /* TT_Driver */
+                    const char*  tt_interface )
+  {
+    FT_Library           library;
+    FT_Module_Interface  result;
+    FT_Module            sfntd;
+    SFNT_Service         sfnt;
+
+
+    result = ft_service_list_lookup( tt_services, tt_interface );
+    if ( result )
+      return result;
+
+    if ( !driver )
+      return NULL;
+    library = driver->library;
+    if ( !library )
+      return NULL;
+
+    /* only return the default interface from the SFNT module */
+    sfntd = FT_Get_Module( library, "sfnt" );
+    if ( sfntd )
+    {
+      sfnt = (SFNT_Service)( sfntd->clazz->module_interface );
+      if ( sfnt )
+        return sfnt->get_interface( driver, tt_interface );
+    }
+
+    return 0;
+  }
+
+
+  /* The FT_DriverInterface structure is defined in ftdriver.h. */
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+#define TT_HINTER_FLAG  FT_MODULE_DRIVER_HAS_HINTER
+#else
+#define TT_HINTER_FLAG  0
+#endif
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+#define TT_SIZE_SELECT  tt_size_select
+#else
+#define TT_SIZE_SELECT  0
+#endif
+
+  FT_DEFINE_DRIVER(
+    tt_driver_class,
+
+      FT_MODULE_FONT_DRIVER     |
+      FT_MODULE_DRIVER_SCALABLE |
+      TT_HINTER_FLAG,
+
+      sizeof ( TT_DriverRec ),
+
+      "truetype",      /* driver name                           */
+      0x10000L,        /* driver version == 1.0                 */
+      0x20000L,        /* driver requires FreeType 2.0 or above */
+
+      NULL,    /* module-specific interface */
+
+      tt_driver_init,           /* FT_Module_Constructor  module_init   */
+      tt_driver_done,           /* FT_Module_Destructor   module_done   */
+      tt_get_interface,         /* FT_Module_Requester    get_interface */
+
+    sizeof ( TT_FaceRec ),
+    sizeof ( TT_SizeRec ),
+    sizeof ( FT_GlyphSlotRec ),
+
+    tt_face_init,               /* FT_Face_InitFunc  init_face */
+    tt_face_done,               /* FT_Face_DoneFunc  done_face */
+    tt_size_init,               /* FT_Size_InitFunc  init_size */
+    tt_size_done,               /* FT_Size_DoneFunc  done_size */
+    tt_slot_init,               /* FT_Slot_InitFunc  init_slot */
+    NULL,                       /* FT_Slot_DoneFunc  done_slot */
+
+    tt_glyph_load,              /* FT_Slot_LoadFunc  load_glyph */
+
+    tt_get_kerning,             /* FT_Face_GetKerningFunc   get_kerning  */
+    NULL,                       /* FT_Face_AttachFunc       attach_file  */
+    tt_get_advances,            /* FT_Face_GetAdvancesFunc  get_advances */
+
+    tt_size_request,            /* FT_Size_RequestFunc  request_size */
+    TT_SIZE_SELECT              /* FT_Size_SelectFunc   select_size  */
+  )
+
+
+/* END */
diff --git a/src/preload/ttdriver.h b/src/preload/ttdriver.h
new file mode 100644
index 000000000..757a66f42
--- /dev/null
+++ b/src/preload/ttdriver.h
@@ -0,0 +1,35 @@
+/****************************************************************************
+ *
+ * ttdriver.h
+ *
+ *   High-level TrueType driver interface (specification).
+ *
+ * Copyright (C) 1996-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+#ifndef TTDRIVER_H_
+#define TTDRIVER_H_
+
+
+#include <freetype/internal/ftdrv.h>
+
+
+FT_BEGIN_HEADER
+
+  FT_DECLARE_DRIVER( tt_driver_class )
+
+FT_END_HEADER
+
+#endif /* TTDRIVER_H_ */
+
+
+/* END */
diff --git a/src/preload/tterrors.h b/src/preload/tterrors.h
new file mode 100644
index 000000000..008ee9985
--- /dev/null
+++ b/src/preload/tterrors.h
@@ -0,0 +1,42 @@
+/****************************************************************************
+ *
+ * tterrors.h
+ *
+ *   TrueType error codes (specification only).
+ *
+ * Copyright (C) 2001-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+  /**************************************************************************
+   *
+   * This file is used to define the TrueType error enumeration
+   * constants.
+   *
+   */
+
+#ifndef TTERRORS_H_
+#define TTERRORS_H_
+
+#include <freetype/ftmoderr.h>
+
+#undef FTERRORS_H_
+
+#undef  FT_ERR_PREFIX
+#define FT_ERR_PREFIX  TT_Err_
+#define FT_ERR_BASE    FT_Mod_Err_TrueType
+
+#include <freetype/fterrors.h>
+
+#endif /* TTERRORS_H_ */
+
+
+/* END */
diff --git a/src/preload/ttgload.c b/src/preload/ttgload.c
new file mode 100644
index 000000000..dc427e8a1
--- /dev/null
+++ b/src/preload/ttgload.c
@@ -0,0 +1,2743 @@
+/****************************************************************************
+ *
+ * ttgload.c
+ *
+ *   TrueType Glyph Loader (body).
+ *
+ * Copyright (C) 1996-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+#include <ft2build.h>
+#include <freetype/internal/ftdebug.h>
+#include FT_CONFIG_CONFIG_H
+#include <freetype/internal/ftcalc.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/sfnt.h>
+#include <freetype/tttags.h>
+#include <freetype/ftoutln.h>
+#include <freetype/ftdriver.h>
+#include <freetype/ftlist.h>
+
+#include "ttgload.h"
+#include "ttpload.h"
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include "ttgxvar.h"
+#endif
+
+#include "tterrors.h"
+
+
+  /**************************************************************************
+   *
+   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
+   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
+   * messages during execution.
+   */
+#undef  FT_COMPONENT
+#define FT_COMPONENT  ttgload
+
+
+  /**************************************************************************
+   *
+   * Simple glyph flags.
+   */
+#define ON_CURVE_POINT  0x01  /* same value as FT_CURVE_TAG_ON            */
+#define X_SHORT_VECTOR  0x02
+#define Y_SHORT_VECTOR  0x04
+#define REPEAT_FLAG     0x08
+#define X_POSITIVE      0x10  /* two meanings depending on X_SHORT_VECTOR */
+#define SAME_X          0x10
+#define Y_POSITIVE      0x20  /* two meanings depending on Y_SHORT_VECTOR */
+#define SAME_Y          0x20
+#define OVERLAP_SIMPLE  0x40  /* retained as FT_OUTLINE_OVERLAP           */
+
+
+  /**************************************************************************
+   *
+   * Composite glyph flags.
+   */
+#define ARGS_ARE_WORDS             0x0001
+#define ARGS_ARE_XY_VALUES         0x0002
+#define ROUND_XY_TO_GRID           0x0004
+#define WE_HAVE_A_SCALE            0x0008
+/* reserved                        0x0010 */
+#define MORE_COMPONENTS            0x0020
+#define WE_HAVE_AN_XY_SCALE        0x0040
+#define WE_HAVE_A_2X2              0x0080
+#define WE_HAVE_INSTR              0x0100
+#define USE_MY_METRICS             0x0200
+#define OVERLAP_COMPOUND           0x0400  /* retained as FT_OUTLINE_OVERLAP */
+#define SCALED_COMPONENT_OFFSET    0x0800
+#define UNSCALED_COMPONENT_OFFSET  0x1000
+
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#define IS_DEFAULT_INSTANCE( _face )             \
+          ( !( FT_IS_NAMED_INSTANCE( _face ) ||  \
+               FT_IS_VARIATION( _face )      ) )
+#else
+#define IS_DEFAULT_INSTANCE( _face )  1
+#endif
+
+
+  /**************************************************************************
+   *
+   * Return the horizontal metrics in font units for a given glyph.
+   */
+  FT_LOCAL_DEF( void )
+  TT_Get_HMetrics( TT_Face     face,
+                   FT_UInt     idx,
+                   FT_Short*   lsb,
+                   FT_UShort*  aw )
+  {
+    ( (SFNT_Service)face->sfnt )->get_metrics( face, 0, idx, lsb, aw );
+
+    FT_TRACE5(( "  advance width (font units): %d\n", *aw ));
+    FT_TRACE5(( "  left side bearing (font units): %d\n", *lsb ));
+  }
+
+
+  /**************************************************************************
+   *
+   * Return the vertical metrics in font units for a given glyph.
+   * See function `tt_loader_set_pp' below for explanations.
+   */
+  FT_LOCAL_DEF( void )
+  TT_Get_VMetrics( TT_Face     face,
+                   FT_UInt     idx,
+                   FT_Pos      yMax,
+                   FT_Short*   tsb,
+                   FT_UShort*  ah )
+  {
+    if ( face->vertical_info )
+      ( (SFNT_Service)face->sfnt )->get_metrics( face, 1, idx, tsb, ah );
+
+    else if ( face->os2.version != 0xFFFFU )
+    {
+      *tsb = (FT_Short)( face->os2.sTypoAscender - yMax );
+      *ah  = (FT_UShort)FT_ABS( face->os2.sTypoAscender -
+                                face->os2.sTypoDescender );
+    }
+
+    else
+    {
+      *tsb = (FT_Short)( face->horizontal.Ascender - yMax );
+      *ah  = (FT_UShort)FT_ABS( face->horizontal.Ascender -
+                                face->horizontal.Descender );
+    }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+    if ( !face->vertical_info )
+      FT_TRACE5(( "  [vertical metrics missing, computing values]\n" ));
+#endif
+
+    FT_TRACE5(( "  advance height (font units): %d\n", *ah ));
+    FT_TRACE5(( "  top side bearing (font units): %d\n", *tsb ));
+  }
+
+
+  static FT_Error
+  tt_get_metrics( TT_Loader  loader,
+                  FT_UInt    glyph_index )
+  {
+    TT_Face    face   = loader->face;
+
+    FT_Error   error;
+    FT_Stream  stream = loader->stream;
+
+    FT_Short   left_bearing = 0, top_bearing = 0;
+    FT_UShort  advance_width = 0, advance_height = 0;
+
+    /* we must preserve the stream position          */
+    /* (which gets altered by the metrics functions) */
+    FT_ULong  pos = FT_STREAM_POS();
+
+
+    TT_Get_HMetrics( face, glyph_index,
+                     &left_bearing,
+                     &advance_width );
+    TT_Get_VMetrics( face, glyph_index,
+                     loader->bbox.yMax,
+                     &top_bearing,
+                     &advance_height );
+
+    if ( FT_STREAM_SEEK( pos ) )
+      return error;
+
+    loader->left_bearing = left_bearing;
+    loader->advance      = advance_width;
+    loader->top_bearing  = top_bearing;
+    loader->vadvance     = advance_height;
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+    /* With the incremental interface, these values are set by  */
+    /* a call to `tt_get_metrics_incremental'.                  */
+    if ( face->root.internal->incremental_interface == NULL )
+#endif
+    {
+      if ( !loader->linear_def )
+      {
+        loader->linear_def = 1;
+        loader->linear     = advance_width;
+      }
+    }
+
+    return FT_Err_Ok;
+  }
+
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+  static void
+  tt_get_metrics_incremental( TT_Loader  loader,
+                              FT_UInt    glyph_index )
+  {
+    TT_Face  face = loader->face;
+
+    FT_Short   left_bearing = 0, top_bearing = 0;
+    FT_UShort  advance_width = 0, advance_height = 0;
+
+
+    /* If this is an incrementally loaded font check whether there are */
+    /* overriding metrics for this glyph.                              */
+    if ( face->root.internal->incremental_interface                           
&&
+         face->root.internal->incremental_interface->funcs->get_glyph_metrics )
+    {
+      FT_Incremental_MetricsRec  incr_metrics;
+      FT_Error                   error;
+
+
+      incr_metrics.bearing_x = loader->left_bearing;
+      incr_metrics.bearing_y = 0;
+      incr_metrics.advance   = loader->advance;
+      incr_metrics.advance_v = 0;
+
+      error = 
face->root.internal->incremental_interface->funcs->get_glyph_metrics(
+                face->root.internal->incremental_interface->object,
+                glyph_index, FALSE, &incr_metrics );
+      if ( error )
+        goto Exit;
+
+      left_bearing  = (FT_Short)incr_metrics.bearing_x;
+      advance_width = (FT_UShort)incr_metrics.advance;
+
+#if 0
+
+      /* GWW: Do I do the same for vertical metrics? */
+      incr_metrics.bearing_x = 0;
+      incr_metrics.bearing_y = loader->top_bearing;
+      incr_metrics.advance   = loader->vadvance;
+
+      error = 
face->root.internal->incremental_interface->funcs->get_glyph_metrics(
+                face->root.internal->incremental_interface->object,
+                glyph_index, TRUE, &incr_metrics );
+      if ( error )
+        goto Exit;
+
+      top_bearing    = (FT_Short)incr_metrics.bearing_y;
+      advance_height = (FT_UShort)incr_metrics.advance;
+
+#endif /* 0 */
+
+      loader->left_bearing = left_bearing;
+      loader->advance      = advance_width;
+      loader->top_bearing  = top_bearing;
+      loader->vadvance     = advance_height;
+
+      if ( !loader->linear_def )
+      {
+        loader->linear_def = 1;
+        loader->linear     = advance_width;
+      }
+    }
+
+  Exit:
+    return;
+  }
+
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+
+
+  /**************************************************************************
+   *
+   * The following functions are used by default with TrueType fonts.
+   * However, they can be replaced by alternatives if we need to support
+   * TrueType-compressed formats (like MicroType) in the future.
+   *
+   */
+
+  FT_CALLBACK_DEF( FT_Error )
+  TT_Access_Glyph_Frame( TT_Loader  loader,
+                         FT_UInt    glyph_index,
+                         FT_ULong   offset,
+                         FT_UInt    byte_count )
+  {
+    FT_Error   error;
+    FT_Stream  stream = loader->stream;
+
+    FT_UNUSED( glyph_index );
+
+
+    /* the following line sets the `error' variable through macros! */
+    if ( FT_STREAM_SEEK( offset ) || FT_FRAME_ENTER( byte_count ) )
+      return error;
+
+    loader->cursor = stream->cursor;
+    loader->limit  = stream->limit;
+
+    return FT_Err_Ok;
+  }
+
+
+  FT_CALLBACK_DEF( void )
+  TT_Forget_Glyph_Frame( TT_Loader  loader )
+  {
+    FT_Stream  stream = loader->stream;
+
+
+    FT_FRAME_EXIT();
+  }
+
+
+  FT_CALLBACK_DEF( FT_Error )
+  TT_Load_Glyph_Header( TT_Loader  loader )
+  {
+    FT_Byte*  p     = loader->cursor;
+    FT_Byte*  limit = loader->limit;
+
+
+    if ( p + 10 > limit )
+      return FT_THROW( Invalid_Outline );
+
+    loader->n_contours = FT_NEXT_SHORT( p );
+
+    loader->bbox.xMin = FT_NEXT_SHORT( p );
+    loader->bbox.yMin = FT_NEXT_SHORT( p );
+    loader->bbox.xMax = FT_NEXT_SHORT( p );
+    loader->bbox.yMax = FT_NEXT_SHORT( p );
+
+    FT_TRACE5(( "  # of contours: %d\n", loader->n_contours ));
+    FT_TRACE5(( "  xMin: %4ld  xMax: %4ld\n", loader->bbox.xMin,
+                                            loader->bbox.xMax ));
+    FT_TRACE5(( "  yMin: %4ld  yMax: %4ld\n", loader->bbox.yMin,
+                                            loader->bbox.yMax ));
+    loader->cursor = p;
+
+    return FT_Err_Ok;
+  }
+
+
+  FT_CALLBACK_DEF( FT_Error )
+  TT_Load_Simple_Glyph( TT_Loader  load )
+  {
+    FT_Error        error;
+    FT_Byte*        p          = load->cursor;
+    FT_Byte*        limit      = load->limit;
+    FT_GlyphLoader  gloader    = load->gloader;
+    FT_Outline*     outline    = &gloader->current.outline;
+    FT_Int          n_contours = load->n_contours;
+    FT_Int          n_points;
+    FT_UShort       n_ins;
+
+    FT_Byte         *flag, *flag_limit;
+    FT_Byte         c, count;
+    FT_Vector       *vec, *vec_limit;
+    FT_Pos          x, y;
+    FT_Short        *cont, *cont_limit, last;
+
+
+    /* check that we can add the contours to the glyph */
+    error = FT_GLYPHLOADER_CHECK_POINTS( gloader, 0, n_contours );
+    if ( error )
+      goto Fail;
+
+    /* check space for contours array + instructions count */
+    if ( n_contours >= 0xFFF || p + 2 * n_contours + 2 > limit )
+      goto Invalid_Outline;
+
+    /* reading the contours' endpoints & number of points */
+    cont       = outline->contours;
+    cont_limit = cont + n_contours;
+
+    last = -1;
+    for ( ; cont < cont_limit; cont++ )
+    {
+      *cont = FT_NEXT_SHORT( p );
+
+      if ( *cont <= last )
+        goto Invalid_Outline;
+
+      last = *cont;
+    }
+
+    n_points = last + 1;
+
+    FT_TRACE5(( "  # of points: %d\n", n_points ));
+
+    /* note that we will add four phantom points later */
+    error = FT_GLYPHLOADER_CHECK_POINTS( gloader, n_points + 4, 0 );
+    if ( error )
+      goto Fail;
+
+    /* space checked above */
+    n_ins = FT_NEXT_USHORT( p );
+
+    FT_TRACE5(( "  Instructions size: %u\n", n_ins ));
+
+    /* check instructions size */
+    if ( p + n_ins > limit )
+    {
+      FT_TRACE1(( "TT_Load_Simple_Glyph: excessive instruction count\n" ));
+      error = FT_THROW( Too_Many_Hints );
+      goto Fail;
+    }
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+    if ( IS_HINTED( load->load_flags ) )
+    {
+      TT_ExecContext  exec = load->exec;
+      FT_Memory       memory = exec->memory;
+
+
+      if ( exec->glyphSize )
+        FT_FREE( exec->glyphIns );
+      exec->glyphSize = 0;
+
+      /* we don't trust `maxSizeOfInstructions' in the `maxp' table */
+      /* and thus allocate the bytecode array size by ourselves     */
+      if ( n_ins )
+      {
+        if ( FT_QNEW_ARRAY( exec->glyphIns, n_ins ) )
+          return error;
+
+        FT_MEM_COPY( exec->glyphIns, p, (FT_Long)n_ins );
+
+        exec->glyphSize  = n_ins;
+      }
+    }
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+    p += n_ins;
+
+    /* reading the point tags */
+    flag       = (FT_Byte*)outline->tags;
+    flag_limit = flag + n_points;
+
+    FT_ASSERT( flag );
+
+    while ( flag < flag_limit )
+    {
+      if ( p + 1 > limit )
+        goto Invalid_Outline;
+
+      *flag++ = c = FT_NEXT_BYTE( p );
+      if ( c & REPEAT_FLAG )
+      {
+        if ( p + 1 > limit )
+          goto Invalid_Outline;
+
+        count = FT_NEXT_BYTE( p );
+        if ( flag + (FT_Int)count > flag_limit )
+          goto Invalid_Outline;
+
+        for ( ; count > 0; count-- )
+          *flag++ = c;
+      }
+    }
+
+    /* retain the overlap flag */
+    if ( n_points && outline->tags[0] & OVERLAP_SIMPLE )
+      gloader->base.outline.flags |= FT_OUTLINE_OVERLAP;
+
+    /* reading the X coordinates */
+
+    vec       = outline->points;
+    vec_limit = vec + n_points;
+    flag      = (FT_Byte*)outline->tags;
+    x         = 0;
+
+    for ( ; vec < vec_limit; vec++, flag++ )
+    {
+      FT_Pos   delta = 0;
+      FT_Byte  f     = *flag;
+
+
+      if ( f & X_SHORT_VECTOR )
+      {
+        if ( p + 1 > limit )
+          goto Invalid_Outline;
+
+        delta = (FT_Pos)FT_NEXT_BYTE( p );
+        if ( !( f & X_POSITIVE ) )
+          delta = -delta;
+      }
+      else if ( !( f & SAME_X ) )
+      {
+        if ( p + 2 > limit )
+          goto Invalid_Outline;
+
+        delta = (FT_Pos)FT_NEXT_SHORT( p );
+      }
+
+      x     += delta;
+      vec->x = x;
+    }
+
+    /* reading the Y coordinates */
+
+    vec       = outline->points;
+    vec_limit = vec + n_points;
+    flag      = (FT_Byte*)outline->tags;
+    y         = 0;
+
+    for ( ; vec < vec_limit; vec++, flag++ )
+    {
+      FT_Pos   delta = 0;
+      FT_Byte  f     = *flag;
+
+
+      if ( f & Y_SHORT_VECTOR )
+      {
+        if ( p + 1 > limit )
+          goto Invalid_Outline;
+
+        delta = (FT_Pos)FT_NEXT_BYTE( p );
+        if ( !( f & Y_POSITIVE ) )
+          delta = -delta;
+      }
+      else if ( !( f & SAME_Y ) )
+      {
+        if ( p + 2 > limit )
+          goto Invalid_Outline;
+
+        delta = (FT_Pos)FT_NEXT_SHORT( p );
+      }
+
+      y     += delta;
+      vec->y = y;
+
+      /* the cast is for stupid compilers */
+      *flag  = (FT_Byte)( f & ON_CURVE_POINT );
+    }
+
+    outline->n_points   = (FT_Short)n_points;
+    outline->n_contours = (FT_Short)n_contours;
+
+    load->cursor = p;
+
+  Fail:
+    return error;
+
+  Invalid_Outline:
+    error = FT_THROW( Invalid_Outline );
+    goto Fail;
+  }
+
+
+  FT_CALLBACK_DEF( FT_Error )
+  TT_Load_Composite_Glyph( TT_Loader  loader )
+  {
+    FT_Error        error;
+    FT_Byte*        p          = loader->cursor;
+    FT_Byte*        limit      = loader->limit;
+    FT_GlyphLoader  gloader    = loader->gloader;
+    FT_Long         num_glyphs = loader->face->root.num_glyphs;
+    FT_SubGlyph     subglyph;
+    FT_UInt         num_subglyphs;
+
+
+    num_subglyphs = 0;
+
+    do
+    {
+      FT_Fixed  xx, xy, yy, yx;
+      FT_UInt   count;
+
+
+      /* check that we can load a new subglyph */
+      error = FT_GlyphLoader_CheckSubGlyphs( gloader, num_subglyphs + 1 );
+      if ( error )
+        goto Fail;
+
+      /* check space */
+      if ( p + 4 > limit )
+        goto Invalid_Composite;
+
+      subglyph = gloader->current.subglyphs + num_subglyphs;
+
+      subglyph->arg1 = subglyph->arg2 = 0;
+
+      subglyph->flags = FT_NEXT_USHORT( p );
+      subglyph->index = FT_NEXT_USHORT( p );
+
+      /* we reject composites that have components */
+      /* with invalid glyph indices                */
+      if ( subglyph->index >= num_glyphs )
+        goto Invalid_Composite;
+
+      /* check space */
+      count = 2;
+      if ( subglyph->flags & ARGS_ARE_WORDS )
+        count += 2;
+      if ( subglyph->flags & WE_HAVE_A_SCALE )
+        count += 2;
+      else if ( subglyph->flags & WE_HAVE_AN_XY_SCALE )
+        count += 4;
+      else if ( subglyph->flags & WE_HAVE_A_2X2 )
+        count += 8;
+
+      if ( p + count > limit )
+        goto Invalid_Composite;
+
+      /* read arguments */
+      if ( subglyph->flags & ARGS_ARE_XY_VALUES )
+      {
+        if ( subglyph->flags & ARGS_ARE_WORDS )
+        {
+          subglyph->arg1 = FT_NEXT_SHORT( p );
+          subglyph->arg2 = FT_NEXT_SHORT( p );
+        }
+        else
+        {
+          subglyph->arg1 = FT_NEXT_CHAR( p );
+          subglyph->arg2 = FT_NEXT_CHAR( p );
+        }
+      }
+      else
+      {
+        if ( subglyph->flags & ARGS_ARE_WORDS )
+        {
+          subglyph->arg1 = (FT_Int)FT_NEXT_USHORT( p );
+          subglyph->arg2 = (FT_Int)FT_NEXT_USHORT( p );
+        }
+        else
+        {
+          subglyph->arg1 = (FT_Int)FT_NEXT_BYTE( p );
+          subglyph->arg2 = (FT_Int)FT_NEXT_BYTE( p );
+        }
+      }
+
+      /* read transform */
+      xx = yy = 0x10000L;
+      xy = yx = 0;
+
+      if ( subglyph->flags & WE_HAVE_A_SCALE )
+      {
+        xx = (FT_Fixed)FT_NEXT_SHORT( p ) * 4;
+        yy = xx;
+      }
+      else if ( subglyph->flags & WE_HAVE_AN_XY_SCALE )
+      {
+        xx = (FT_Fixed)FT_NEXT_SHORT( p ) * 4;
+        yy = (FT_Fixed)FT_NEXT_SHORT( p ) * 4;
+      }
+      else if ( subglyph->flags & WE_HAVE_A_2X2 )
+      {
+        xx = (FT_Fixed)FT_NEXT_SHORT( p ) * 4;
+        yx = (FT_Fixed)FT_NEXT_SHORT( p ) * 4;
+        xy = (FT_Fixed)FT_NEXT_SHORT( p ) * 4;
+        yy = (FT_Fixed)FT_NEXT_SHORT( p ) * 4;
+      }
+
+      subglyph->transform.xx = xx;
+      subglyph->transform.xy = xy;
+      subglyph->transform.yx = yx;
+      subglyph->transform.yy = yy;
+
+      num_subglyphs++;
+
+    } while ( subglyph->flags & MORE_COMPONENTS );
+
+    gloader->current.num_subglyphs = num_subglyphs;
+    FT_TRACE5(( "  %d component%s\n",
+                num_subglyphs,
+                num_subglyphs > 1 ? "s" : "" ));
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+    {
+      FT_UInt  i;
+
+
+      subglyph = gloader->current.subglyphs;
+
+      for ( i = 0; i < num_subglyphs; i++ )
+      {
+        if ( num_subglyphs > 1 )
+          FT_TRACE7(( "    subglyph %d:\n", i ));
+
+        FT_TRACE7(( "      glyph index: %d\n", subglyph->index ));
+
+        if ( subglyph->flags & ARGS_ARE_XY_VALUES )
+          FT_TRACE7(( "      offset: x=%d, y=%d\n",
+                      subglyph->arg1,
+                      subglyph->arg2 ));
+        else
+          FT_TRACE7(( "      matching points: base=%d, component=%d\n",
+                      subglyph->arg1,
+                      subglyph->arg2 ));
+
+        if ( subglyph->flags & WE_HAVE_A_SCALE )
+          FT_TRACE7(( "      scaling: %f\n",
+                      (double)subglyph->transform.xx / 65536 ));
+        else if ( subglyph->flags & WE_HAVE_AN_XY_SCALE )
+          FT_TRACE7(( "      scaling: x=%f, y=%f\n",
+                      (double)subglyph->transform.xx / 65536,
+                      (double)subglyph->transform.yy / 65536 ));
+        else if ( subglyph->flags & WE_HAVE_A_2X2 )
+        {
+          FT_TRACE7(( "      scaling: xx=%f, yx=%f\n",
+                      (double)subglyph->transform.xx / 65536,
+                      (double)subglyph->transform.yx / 65536 ));
+          FT_TRACE7(( "               xy=%f, yy=%f\n",
+                      (double)subglyph->transform.xy / 65536,
+                      (double)subglyph->transform.yy / 65536 ));
+        }
+
+        subglyph++;
+      }
+    }
+#endif /* FT_DEBUG_LEVEL_TRACE */
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+    {
+      FT_Stream  stream = loader->stream;
+
+
+      /* we must undo the FT_FRAME_ENTER in order to point */
+      /* to the composite instructions, if we find some.   */
+      /* We will process them later.                       */
+      /*                                                   */
+      loader->ins_pos = (FT_ULong)( FT_STREAM_POS() +
+                                    p - limit );
+    }
+
+#endif
+
+    loader->cursor = p;
+
+  Fail:
+    return error;
+
+  Invalid_Composite:
+    error = FT_THROW( Invalid_Composite );
+    goto Fail;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  TT_Init_Glyph_Loading( TT_Face  face )
+  {
+    face->access_glyph_frame   = TT_Access_Glyph_Frame;
+    face->read_glyph_header    = TT_Load_Glyph_Header;
+    face->read_simple_glyph    = TT_Load_Simple_Glyph;
+    face->read_composite_glyph = TT_Load_Composite_Glyph;
+    face->forget_glyph_frame   = TT_Forget_Glyph_Frame;
+  }
+
+
+  static void
+  tt_prepare_zone( TT_GlyphZone  zone,
+                   FT_GlyphLoad  load,
+                   FT_UInt       start_point,
+                   FT_UInt       start_contour )
+  {
+    zone->n_points    = (FT_UShort)load->outline.n_points + 4 -
+                          (FT_UShort)start_point;
+    zone->n_contours  = load->outline.n_contours -
+                          (FT_Short)start_contour;
+    zone->org         = load->extra_points + start_point;
+    zone->cur         = load->outline.points + start_point;
+    zone->orus        = load->extra_points2 + start_point;
+    zone->tags        = (FT_Byte*)load->outline.tags + start_point;
+    zone->contours    = (FT_UShort*)load->outline.contours + start_contour;
+    zone->first_point = (FT_UShort)start_point;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Hint_Glyph
+   *
+   * @Description:
+   *   Hint the glyph using the zone prepared by the caller.  Note that
+   *   the zone is supposed to include four phantom points.
+   */
+  static FT_Error
+  TT_Hint_Glyph( TT_Loader  loader,
+                 FT_Bool    is_composite )
+  {
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    TT_Face    face   = loader->face;
+    TT_Driver  driver = (TT_Driver)FT_FACE_DRIVER( face );
+#endif
+
+    TT_GlyphZone  zone = &loader->zone;
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+    TT_ExecContext  exec  = loader->exec;
+    FT_Long         n_ins = exec->glyphSize;
+#else
+    FT_UNUSED( is_composite );
+#endif
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+    /* save original point positions in `org' array */
+    if ( n_ins > 0 )
+      FT_ARRAY_COPY( zone->org, zone->cur, zone->n_points );
+
+    /* Reset graphics state. */
+    exec->GS = loader->size->GS;
+
+    /* XXX: UNDOCUMENTED! Hinting instructions of a composite glyph */
+    /*      completely refer to the (already) hinted subglyphs.     */
+    if ( is_composite )
+    {
+      exec->metrics.x_scale = 1 << 16;
+      exec->metrics.y_scale = 1 << 16;
+
+      FT_ARRAY_COPY( zone->orus, zone->cur, zone->n_points );
+    }
+    else
+    {
+      exec->metrics.x_scale = loader->size->metrics->x_scale;
+      exec->metrics.y_scale = loader->size->metrics->y_scale;
+    }
+#endif
+
+    /* round phantom points */
+    zone->cur[zone->n_points - 4].x =
+      FT_PIX_ROUND( zone->cur[zone->n_points - 4].x );
+    zone->cur[zone->n_points - 3].x =
+      FT_PIX_ROUND( zone->cur[zone->n_points - 3].x );
+    zone->cur[zone->n_points - 2].y =
+      FT_PIX_ROUND( zone->cur[zone->n_points - 2].y );
+    zone->cur[zone->n_points - 1].y =
+      FT_PIX_ROUND( zone->cur[zone->n_points - 1].y );
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+    if ( n_ins > 0 )
+    {
+      FT_Error  error;
+
+
+      TT_Set_CodeRange( exec, tt_coderange_glyph, exec->glyphIns, n_ins );
+
+      exec->is_composite = is_composite;
+      exec->pts          = *zone;
+
+      error = TT_Run_Context( exec );
+      if ( error && exec->pedantic_hinting )
+        return error;
+
+      /* store drop-out mode in bits 5-7; set bit 2 also as a marker */
+      loader->gloader->current.outline.tags[0] |=
+        ( exec->GS.scan_type << 5 ) | FT_CURVE_TAG_HAS_SCANMODE;
+    }
+
+#endif
+
+    /* Save possibly modified glyph phantom points unless in v40 backward  */
+    /* compatibility mode, where no movement on the x axis means no reason */
+    /* to change bearings or advance widths.                               */
+
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 &&
+         exec->backward_compatibility )
+      return FT_Err_Ok;
+#endif
+
+    loader->pp1 = zone->cur[zone->n_points - 4];
+    loader->pp2 = zone->cur[zone->n_points - 3];
+    loader->pp3 = zone->cur[zone->n_points - 2];
+    loader->pp4 = zone->cur[zone->n_points - 1];
+
+    return FT_Err_Ok;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Process_Simple_Glyph
+   *
+   * @Description:
+   *   Once a simple glyph has been loaded, it needs to be processed.
+   *   Usually, this means scaling and hinting through bytecode
+   *   interpretation.
+   */
+  static FT_Error
+  TT_Process_Simple_Glyph( TT_Loader  loader )
+  {
+    FT_Error        error    = FT_Err_Ok;
+    FT_GlyphLoader  gloader  = loader->gloader;
+    FT_Outline*     outline  = &gloader->current.outline;
+    FT_Int          n_points = outline->n_points;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+    FT_Memory   memory    = loader->face->root.memory;
+    FT_Vector*  unrounded = NULL;
+#endif
+
+
+    /* set phantom points */
+    outline->points[n_points    ] = loader->pp1;
+    outline->points[n_points + 1] = loader->pp2;
+    outline->points[n_points + 2] = loader->pp3;
+    outline->points[n_points + 3] = loader->pp4;
+
+    n_points += 4;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+    if ( !IS_DEFAULT_INSTANCE( FT_FACE( loader->face ) ) )
+    {
+      if ( FT_QNEW_ARRAY( unrounded, n_points ) )
+        goto Exit;
+
+      /* Deltas apply to the unscaled data. */
+      error = TT_Vary_Apply_Glyph_Deltas( loader,
+                                          outline,
+                                          unrounded );
+      if ( error )
+        goto Exit;
+    }
+
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+    if ( IS_HINTED( loader->load_flags ) )
+    {
+      tt_prepare_zone( &loader->zone, &gloader->current, 0, 0 );
+
+      FT_ARRAY_COPY( loader->zone.orus, loader->zone.cur,
+                     loader->zone.n_points );
+    }
+
+    {
+      FT_Vector*  vec   = outline->points;
+      FT_Vector*  limit = outline->points + n_points;
+
+      FT_Fixed  x_scale = 0; /* pacify compiler */
+      FT_Fixed  y_scale = 0;
+
+      FT_Bool  do_scale = FALSE;
+
+
+      {
+        /* scale the glyph */
+        if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
+        {
+          x_scale = loader->size->metrics->x_scale;
+          y_scale = loader->size->metrics->y_scale;
+
+          do_scale = TRUE;
+        }
+      }
+
+      if ( do_scale )
+      {
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+        if ( !IS_DEFAULT_INSTANCE( FT_FACE( loader->face ) ) )
+        {
+          FT_Vector*  u = unrounded;
+
+
+          for ( ; vec < limit; vec++, u++ )
+          {
+            vec->x = ADD_LONG( FT_MulFix( u->x, x_scale ), 32 ) >> 6;
+            vec->y = ADD_LONG( FT_MulFix( u->y, y_scale ), 32 ) >> 6;
+          }
+        }
+        else
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+        {
+          for ( ; vec < limit; vec++ )
+          {
+            vec->x = FT_MulFix( vec->x, x_scale );
+            vec->y = FT_MulFix( vec->y, y_scale );
+          }
+        }
+      }
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+      /* if we have a HVAR table, `pp1' and/or `pp2' */
+      /* are already adjusted but unscaled           */
+      if ( ( loader->face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) &&
+           IS_HINTED( loader->load_flags )                                 )
+      {
+        loader->pp1.x = FT_MulFix( loader->pp1.x, x_scale );
+        loader->pp2.x = FT_MulFix( loader->pp2.x, x_scale );
+        /* pp1.y and pp2.y are always zero */
+      }
+      else
+#endif
+      {
+        loader->pp1 = outline->points[n_points - 4];
+        loader->pp2 = outline->points[n_points - 3];
+      }
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+      /* if we have a VVAR table, `pp3' and/or `pp4' */
+      /* are already adjusted but unscaled           */
+      if ( ( loader->face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) &&
+           IS_HINTED( loader->load_flags )                                 )
+      {
+        loader->pp3.x = FT_MulFix( loader->pp3.x, x_scale );
+        loader->pp3.y = FT_MulFix( loader->pp3.y, y_scale );
+        loader->pp4.x = FT_MulFix( loader->pp4.x, x_scale );
+        loader->pp4.y = FT_MulFix( loader->pp4.y, y_scale );
+      }
+      else
+#endif
+      {
+        loader->pp3 = outline->points[n_points - 2];
+        loader->pp4 = outline->points[n_points - 1];
+      }
+    }
+
+    if ( IS_HINTED( loader->load_flags ) )
+      error = TT_Hint_Glyph( loader, 0 );
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+  Exit:
+    FT_FREE( unrounded );
+#endif
+
+    return error;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Process_Composite_Component
+   *
+   * @Description:
+   *   Once a composite component has been loaded, it needs to be
+   *   processed.  Usually, this means transforming and translating.
+   */
+  static FT_Error
+  TT_Process_Composite_Component( TT_Loader    loader,
+                                  FT_SubGlyph  subglyph,
+                                  FT_UInt      start_point,
+                                  FT_UInt      num_base_points )
+  {
+    FT_GlyphLoader  gloader = loader->gloader;
+    FT_Outline      current;
+    FT_Bool         have_scale;
+    FT_Pos          x, y;
+
+
+    current.points   = gloader->base.outline.points +
+                         num_base_points;
+    current.n_points = gloader->base.outline.n_points -
+                         (short)num_base_points;
+
+    have_scale = FT_BOOL( subglyph->flags & ( WE_HAVE_A_SCALE     |
+                                              WE_HAVE_AN_XY_SCALE |
+                                              WE_HAVE_A_2X2       ) );
+
+    /* perform the transform required for this subglyph */
+    if ( have_scale )
+      FT_Outline_Transform( &current, &subglyph->transform );
+
+    /* get offset */
+    if ( !( subglyph->flags & ARGS_ARE_XY_VALUES ) )
+    {
+      FT_UInt     num_points = (FT_UInt)gloader->base.outline.n_points;
+      FT_UInt     k = (FT_UInt)subglyph->arg1;
+      FT_UInt     l = (FT_UInt)subglyph->arg2;
+      FT_Vector*  p1;
+      FT_Vector*  p2;
+
+
+      /* match l-th point of the newly loaded component to the k-th point */
+      /* of the previously loaded components.                             */
+
+      /* change to the point numbers used by our outline */
+      k += start_point;
+      l += num_base_points;
+      if ( k >= num_base_points ||
+           l >= num_points      )
+        return FT_THROW( Invalid_Composite );
+
+      p1 = gloader->base.outline.points + k;
+      p2 = gloader->base.outline.points + l;
+
+      x = SUB_LONG( p1->x, p2->x );
+      y = SUB_LONG( p1->y, p2->y );
+    }
+    else
+    {
+      x = subglyph->arg1;
+      y = subglyph->arg2;
+
+      if ( !x && !y )
+        return FT_Err_Ok;
+
+      /* Use a default value dependent on                                  */
+      /* TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED.  This is useful for old */
+      /* TT fonts which don't set the xxx_COMPONENT_OFFSET bit.            */
+
+      if ( have_scale &&
+#ifdef TT_CONFIG_OPTION_COMPONENT_OFFSET_SCALED
+           !( subglyph->flags & UNSCALED_COMPONENT_OFFSET ) )
+#else
+            ( subglyph->flags & SCALED_COMPONENT_OFFSET ) )
+#endif
+      {
+
+#if 0
+
+        /********************************************************************
+         *
+         * This algorithm is what Apple documents.  But it doesn't work.
+         */
+        int  a = subglyph->transform.xx > 0 ?  subglyph->transform.xx
+                                            : -subglyph->transform.xx;
+        int  b = subglyph->transform.yx > 0 ?  subglyph->transform.yx
+                                            : -subglyph->transform.yx;
+        int  c = subglyph->transform.xy > 0 ?  subglyph->transform.xy
+                                            : -subglyph->transform.xy;
+        int  d = subglyph->transform.yy > 0 ?  subglyph->transform.yy
+                                            : -subglyph->transform.yy;
+        int  m = a > b ? a : b;
+        int  n = c > d ? c : d;
+
+
+        if ( a - b <= 33 && a - b >= -33 )
+          m *= 2;
+        if ( c - d <= 33 && c - d >= -33 )
+          n *= 2;
+        x = FT_MulFix( x, m );
+        y = FT_MulFix( y, n );
+
+#else /* 1 */
+
+        /********************************************************************
+         *
+         * This algorithm is a guess and works much better than the above.
+         */
+        FT_Fixed  mac_xscale = FT_Hypot( subglyph->transform.xx,
+                                         subglyph->transform.xy );
+        FT_Fixed  mac_yscale = FT_Hypot( subglyph->transform.yy,
+                                         subglyph->transform.yx );
+
+
+        x = FT_MulFix( x, mac_xscale );
+        y = FT_MulFix( y, mac_yscale );
+
+#endif /* 1 */
+
+      }
+
+      if ( !( loader->load_flags & FT_LOAD_NO_SCALE ) )
+      {
+        FT_Fixed  x_scale = loader->size->metrics->x_scale;
+        FT_Fixed  y_scale = loader->size->metrics->y_scale;
+
+
+        x = FT_MulFix( x, x_scale );
+        y = FT_MulFix( y, y_scale );
+
+        if ( subglyph->flags & ROUND_XY_TO_GRID )
+        {
+          TT_Face    face   = loader->face;
+          TT_Driver  driver = (TT_Driver)FT_FACE_DRIVER( face );
+
+
+          if ( IS_HINTED( loader->load_flags ) )
+          {
+            /*
+             * We round the horizontal offset only if there is hinting along
+             * the x axis; this corresponds to integer advance width values.
+             *
+             * Theoretically, a glyph's bytecode can toggle ClearType's
+             * `backward compatibility' mode, which would allow modification
+             * of the advance width.  In reality, however, applications
+             * neither allow nor expect modified advance widths if subpixel
+             * rendering is active.
+             *
+             */
+            if ( driver->interpreter_version == TT_INTERPRETER_VERSION_35 )
+              x = FT_PIX_ROUND( x );
+
+            y = FT_PIX_ROUND( y );
+          }
+        }
+      }
+    }
+
+    if ( x || y )
+      FT_Outline_Translate( &current, x, y );
+
+    return FT_Err_Ok;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Process_Composite_Glyph
+   *
+   * @Description:
+   *   This is slightly different from TT_Process_Simple_Glyph, in that
+   *   its sole purpose is to hint the glyph.  Thus this function is
+   *   only available when bytecode interpreter is enabled.
+   */
+  static FT_Error
+  TT_Process_Composite_Glyph( TT_Loader  loader,
+                              FT_UInt    start_point,
+                              FT_UInt    start_contour )
+  {
+    FT_Error     error;
+    FT_Outline*  outline = &loader->gloader->base.outline;
+    FT_Stream    stream = loader->stream;
+    FT_UShort    n_ins;
+    FT_UInt      i;
+
+
+    /* make room for phantom points */
+    error = FT_GLYPHLOADER_CHECK_POINTS( loader->gloader,
+                                         outline->n_points + 4,
+                                         0 );
+    if ( error )
+      return error;
+
+    outline->points[outline->n_points    ] = loader->pp1;
+    outline->points[outline->n_points + 1] = loader->pp2;
+    outline->points[outline->n_points + 2] = loader->pp3;
+    outline->points[outline->n_points + 3] = loader->pp4;
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+    {
+      TT_ExecContext  exec = loader->exec;
+      FT_Memory       memory = exec->memory;
+
+
+      if ( exec->glyphSize )
+        FT_FREE( exec->glyphIns );
+      exec->glyphSize = 0;
+
+      /* TT_Load_Composite_Glyph only gives us the offset of instructions */
+      /* so we read them here                                             */
+      if ( FT_STREAM_SEEK( loader->ins_pos ) ||
+           FT_READ_USHORT( n_ins )           )
+        return error;
+
+      FT_TRACE5(( "  Instructions size = %hu\n", n_ins ));
+
+      if ( !n_ins )
+        return FT_Err_Ok;
+
+      /* don't trust `maxSizeOfInstructions'; */
+      /* only do a rough safety check         */
+      if ( n_ins > loader->byte_len )
+      {
+        FT_TRACE1(( "TT_Process_Composite_Glyph:"
+                    " too many instructions (%hu) for glyph with length %u\n",
+                    n_ins, loader->byte_len ));
+        return FT_THROW( Too_Many_Hints );
+      }
+
+      if ( FT_QNEW_ARRAY( exec->glyphIns, n_ins )  ||
+           FT_STREAM_READ( exec->glyphIns, n_ins ) )
+        return error;
+
+      exec->glyphSize = n_ins;
+    }
+
+#endif
+
+    tt_prepare_zone( &loader->zone, &loader->gloader->base,
+                     start_point, start_contour );
+
+    /* Some points are likely touched during execution of  */
+    /* instructions on components.  So let's untouch them. */
+    for ( i = 0; i < loader->zone.n_points - 4U; i++ )
+      loader->zone.tags[i] &= ~FT_CURVE_TAG_TOUCH_BOTH;
+
+    return TT_Hint_Glyph( loader, 1 );
+  }
+
+
+  /*
+   * Calculate the phantom points
+   *
+   * Defining the right side bearing (rsb) as
+   *
+   *   rsb = aw - (lsb + xmax - xmin)
+   *
+   * (with `aw' the advance width, `lsb' the left side bearing, and `xmin'
+   * and `xmax' the glyph's minimum and maximum x value), the OpenType
+   * specification defines the initial position of horizontal phantom points
+   * as
+   *
+   *   pp1 = (round(xmin - lsb), 0)      ,
+   *   pp2 = (round(pp1 + aw), 0)        .
+   *
+   * Note that the rounding to the grid (in the device space) is not
+   * documented currently in the specification.
+   *
+   * However, the specification lacks the precise definition of vertical
+   * phantom points.  Greg Hitchcock provided the following explanation.
+   *
+   * - a `vmtx' table is present
+   *
+   *   For any glyph, the minimum and maximum y values (`ymin' and `ymax')
+   *   are given in the `glyf' table, the top side bearing (tsb) and advance
+   *   height (ah) are given in the `vmtx' table.  The bottom side bearing
+   *   (bsb) is then calculated as
+   *
+   *     bsb = ah - (tsb + ymax - ymin)       ,
+   *
+   *   and the initial position of vertical phantom points is
+   *
+   *     pp3 = (x, round(ymax + tsb))       ,
+   *     pp4 = (x, round(pp3 - ah))         .
+   *
+   *   See below for value `x'.
+   *
+   * - no `vmtx' table in the font
+   *
+   *   If there is an `OS/2' table, we set
+   *
+   *     DefaultAscender = sTypoAscender       ,
+   *     DefaultDescender = sTypoDescender     ,
+   *
+   *   otherwise we use data from the `hhea' table:
+   *
+   *     DefaultAscender = Ascender         ,
+   *     DefaultDescender = Descender       .
+   *
+   *   With these two variables we can now set
+   *
+   *     ah = DefaultAscender - sDefaultDescender    ,
+   *     tsb = DefaultAscender - yMax                ,
+   *
+   *   and proceed as if a `vmtx' table was present.
+   *
+   * Usually we have
+   *
+   *   x = aw / 2      ,                                                (1)
+   *
+   * but there is one compatibility case where it can be set to
+   *
+   *   x = -DefaultDescender -
+   *         ((DefaultAscender - DefaultDescender - aw) / 2)     .      (2)
+   *
+   * and another one with
+   *
+   *   x = 0     .                                                      (3)
+   *
+   * In Windows, the history of those values is quite complicated,
+   * depending on the hinting engine (that is, the graphics framework).
+   *
+   *   framework        from                 to       formula
+   *  ----------------------------------------------------------
+   *    GDI       Windows 98               current      (1)
+   *              (Windows 2000 for NT)
+   *    GDI+      Windows XP               Windows 7    (2)
+   *    GDI+      Windows 8                current      (3)
+   *    DWrite    Windows 7                current      (3)
+   *
+   * For simplicity, FreeType uses (1) for grayscale subpixel hinting and
+   * (3) for everything else.
+   *
+   */
+  static void
+  tt_loader_set_pp( TT_Loader  loader )
+  {
+    loader->pp1.x = loader->bbox.xMin - loader->left_bearing;
+    loader->pp1.y = 0;
+    loader->pp2.x = loader->pp1.x + loader->advance;
+    loader->pp2.y = 0;
+
+    loader->pp3.x = 0;
+    loader->pp3.y = loader->bbox.yMax + loader->top_bearing;
+    loader->pp4.x = 0;
+    loader->pp4.y = loader->pp3.y - loader->vadvance;
+
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    {
+      TT_Driver driver = (TT_Driver)FT_FACE_DRIVER( loader->face );
+
+
+      if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 &&
+           loader->exec                                             &&
+           loader->exec->subpixel_hinting_lean                      &&
+           loader->exec->grayscale_cleartype                        )
+      {
+        loader->pp3.x = loader->advance / 2;
+        loader->pp4.x = loader->advance / 2;
+      }
+    }
+#endif
+  }
+
+
+  /* a utility function to retrieve i-th node from given FT_List */
+  static FT_ListNode
+  ft_list_get_node_at( FT_List  list,
+                       FT_UInt  idx )
+  {
+    FT_ListNode  cur;
+
+
+    if ( !list )
+      return NULL;
+
+    for ( cur = list->head; cur; cur = cur->next )
+    {
+      if ( !idx )
+        return cur;
+
+      idx--;
+    }
+
+    return NULL;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   load_truetype_glyph
+   *
+   * @Description:
+   *   Loads a given truetype glyph.  Handles composites and uses a
+   *   TT_Loader object.
+   */
+  static FT_Error
+  load_truetype_glyph( TT_Loader  loader,
+                       FT_UInt    glyph_index,
+                       FT_UInt    recurse_count,
+                       FT_Bool    header_only )
+  {
+    FT_Error        error   = FT_Err_Ok;
+    FT_Fixed        x_scale, y_scale;
+    FT_ULong        offset;
+    TT_Face         face    = loader->face;
+    FT_GlyphLoader  gloader = loader->gloader;
+
+    FT_Bool  opened_frame = 0;
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+    FT_StreamRec    inc_stream;
+    FT_Data         glyph_data;
+    FT_Bool         glyph_data_loaded = 0;
+#endif
+
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+    if ( recurse_count )
+      FT_TRACE5(( "  nesting level: %d\n", recurse_count ));
+#endif
+
+    /* some fonts have an incorrect value of `maxComponentDepth' */
+    if ( recurse_count > face->max_profile.maxComponentDepth )
+    {
+      FT_TRACE1(( "load_truetype_glyph: maxComponentDepth set to %d\n",
+                  recurse_count ));
+      face->max_profile.maxComponentDepth = (FT_UShort)recurse_count;
+    }
+
+#ifndef FT_CONFIG_OPTION_INCREMENTAL
+    /* check glyph index */
+    if ( glyph_index >= (FT_UInt)face->root.num_glyphs )
+    {
+      error = FT_THROW( Invalid_Glyph_Index );
+      goto Exit;
+    }
+#endif
+
+    loader->glyph_index = glyph_index;
+
+    if ( loader->load_flags & FT_LOAD_NO_SCALE )
+    {
+      x_scale = 0x10000L;
+      y_scale = 0x10000L;
+    }
+    else
+    {
+      x_scale = loader->size->metrics->x_scale;
+      y_scale = loader->size->metrics->y_scale;
+    }
+
+    /* Set `offset' to the start of the glyph relative to the start of */
+    /* the `glyf' table, and `byte_len' to the length of the glyph in  */
+    /* bytes.                                                          */
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+    /* If we are loading glyph data via the incremental interface, set */
+    /* the loader stream to a memory stream reading the data returned  */
+    /* by the interface.                                               */
+    if ( face->root.internal->incremental_interface )
+    {
+      error = 
face->root.internal->incremental_interface->funcs->get_glyph_data(
+                face->root.internal->incremental_interface->object,
+                glyph_index, &glyph_data );
+      if ( error )
+        goto Exit;
+
+      glyph_data_loaded = 1;
+      offset            = 0;
+      loader->byte_len  = glyph_data.length;
+
+      FT_ZERO( &inc_stream );
+      FT_Stream_OpenMemory( &inc_stream,
+                            glyph_data.pointer,
+                            glyph_data.length );
+
+      loader->stream = &inc_stream;
+    }
+    else
+
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+    {
+      FT_ULong  len;
+
+
+      offset = tt_face_get_location( FT_FACE( face ), glyph_index, &len );
+
+      loader->byte_len = (FT_UInt)len;
+    }
+
+    if ( loader->byte_len > 0 )
+    {
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+      /* for the incremental interface, `glyf_offset' is always zero */
+      if ( !face->glyf_offset                          &&
+           !face->root.internal->incremental_interface )
+#else
+      if ( !face->glyf_offset )
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+      {
+        FT_TRACE2(( "no `glyf' table but non-zero `loca' entry\n" ));
+        error = FT_THROW( Invalid_Table );
+        goto Exit;
+      }
+
+      error = face->access_glyph_frame( loader, glyph_index,
+                                        face->glyf_offset + offset,
+                                        loader->byte_len );
+      if ( error )
+        goto Exit;
+
+      /* read glyph header first */
+      error = face->read_glyph_header( loader );
+
+      face->forget_glyph_frame( loader );
+
+      if ( error )
+        goto Exit;
+    }
+
+    /* a space glyph */
+    if ( loader->byte_len == 0 || loader->n_contours == 0 )
+    {
+      loader->bbox.xMin = 0;
+      loader->bbox.xMax = 0;
+      loader->bbox.yMin = 0;
+      loader->bbox.yMax = 0;
+    }
+
+    /* the metrics must be computed after loading the glyph header */
+    /* since we need the glyph's `yMax' value in case the vertical */
+    /* metrics must be emulated                                    */
+    error = tt_get_metrics( loader, glyph_index );
+    if ( error )
+      goto Exit;
+
+    if ( header_only )
+      goto Exit;
+
+    if ( loader->byte_len == 0 || loader->n_contours == 0 )
+    {
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+      tt_get_metrics_incremental( loader, glyph_index );
+#endif
+      tt_loader_set_pp( loader );
+
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+      if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ||
+           FT_IS_VARIATION( FT_FACE( face ) )      )
+      {
+        /* a small outline structure with four elements for */
+        /* communication with `TT_Vary_Apply_Glyph_Deltas'  */
+        FT_Vector   points[4];
+        FT_Outline  outline;
+
+        /* unrounded values */
+        FT_Vector  unrounded[4] = { {0, 0}, {0, 0}, {0, 0}, {0, 0} };
+
+
+        points[0] = loader->pp1;
+        points[1] = loader->pp2;
+        points[2] = loader->pp3;
+        points[3] = loader->pp4;
+
+        outline.n_points   = 0;
+        outline.n_contours = 0;
+        outline.points     = points;
+        outline.tags       = NULL;
+        outline.contours   = NULL;
+
+        /* this must be done before scaling */
+        error = TT_Vary_Apply_Glyph_Deltas( loader,
+                                            &outline,
+                                            unrounded );
+        if ( error )
+          goto Exit;
+      }
+
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+      /* scale phantom points, if necessary; */
+      /* they get rounded in `TT_Hint_Glyph' */
+      if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
+      {
+        loader->pp1.x = FT_MulFix( loader->pp1.x, x_scale );
+        loader->pp2.x = FT_MulFix( loader->pp2.x, x_scale );
+        /* pp1.y and pp2.y are always zero */
+
+        loader->pp3.x = FT_MulFix( loader->pp3.x, x_scale );
+        loader->pp3.y = FT_MulFix( loader->pp3.y, y_scale );
+        loader->pp4.x = FT_MulFix( loader->pp4.x, x_scale );
+        loader->pp4.y = FT_MulFix( loader->pp4.y, y_scale );
+      }
+
+      error = FT_Err_Ok;
+      goto Exit;
+    }
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+    tt_get_metrics_incremental( loader, glyph_index );
+#endif
+    tt_loader_set_pp( loader );
+
+
+    /***********************************************************************/
+    /***********************************************************************/
+    /***********************************************************************/
+
+    /* we now open a frame again, right after the glyph header */
+    /* (which consists of 10 bytes)                            */
+    error = face->access_glyph_frame( loader, glyph_index,
+                                      face->glyf_offset + offset + 10,
+                                      loader->byte_len - 10 );
+    if ( error )
+      goto Exit;
+
+    opened_frame = 1;
+
+    /* if it is a simple glyph, load it */
+
+    if ( loader->n_contours > 0 )
+    {
+      error = face->read_simple_glyph( loader );
+      if ( error )
+        goto Exit;
+
+      /* all data have been read */
+      face->forget_glyph_frame( loader );
+      opened_frame = 0;
+
+      error = TT_Process_Simple_Glyph( loader );
+      if ( error )
+        goto Exit;
+
+      FT_GlyphLoader_Add( gloader );
+    }
+
+    /***********************************************************************/
+    /***********************************************************************/
+    /***********************************************************************/
+
+    /* otherwise, load a composite! */
+    else if ( loader->n_contours < 0 )
+    {
+      FT_Memory  memory = face->root.memory;
+
+      FT_UInt   start_point;
+      FT_UInt   start_contour;
+      FT_ULong  ins_pos;  /* position of composite instructions, if any */
+
+      FT_ListNode  node, node2;
+
+
+      /* normalize the `n_contours' value */
+      loader->n_contours = -1;
+
+      /*
+       * We store the glyph index directly in the `node->data' pointer,
+       * following the glib solution (cf. macro `GUINT_TO_POINTER') with a
+       * double cast to make this portable.  Note, however, that this needs
+       * pointers with a width of at least 32 bits.
+       */
+
+      /* clear the nodes filled by sibling chains */
+      node = ft_list_get_node_at( &loader->composites, recurse_count );
+      for ( node2 = node; node2; node2 = node2->next )
+        node2->data = (void*)-1;
+
+      /* check whether we already have a composite glyph with this index */
+      if ( FT_List_Find( &loader->composites,
+                         FT_UINT_TO_POINTER( glyph_index ) ) )
+      {
+        FT_TRACE1(( "TT_Load_Composite_Glyph:"
+                    " infinite recursion detected\n" ));
+        error = FT_THROW( Invalid_Composite );
+        goto Exit;
+      }
+
+      else if ( node )
+        node->data = FT_UINT_TO_POINTER( glyph_index );
+
+      else
+      {
+        if ( FT_QNEW( node ) )
+          goto Exit;
+        node->data = FT_UINT_TO_POINTER( glyph_index );
+        FT_List_Add( &loader->composites, node );
+      }
+
+      start_point   = (FT_UInt)gloader->base.outline.n_points;
+      start_contour = (FT_UInt)gloader->base.outline.n_contours;
+
+      /* for each subglyph, read composite header */
+      error = face->read_composite_glyph( loader );
+      if ( error )
+        goto Exit;
+
+      /* store the offset of instructions */
+      ins_pos = loader->ins_pos;
+
+      /* all data we need are read */
+      face->forget_glyph_frame( loader );
+      opened_frame = 0;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+      if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) ||
+           FT_IS_VARIATION( FT_FACE( face ) )      )
+      {
+        short        i, limit;
+        FT_SubGlyph  subglyph;
+
+        FT_Outline  outline = { 0, 0, NULL, NULL, NULL, 0 };
+        FT_Vector*  unrounded = NULL;
+
+
+        limit = (short)gloader->current.num_subglyphs;
+
+        /* construct an outline structure for              */
+        /* communication with `TT_Vary_Apply_Glyph_Deltas' */
+        if ( FT_QNEW_ARRAY( outline.points, limit + 4 ) ||
+             FT_QNEW_ARRAY( outline.tags, limit )       ||
+             FT_QNEW_ARRAY( outline.contours, limit )   ||
+             FT_QNEW_ARRAY( unrounded, limit + 4 )      )
+          goto Exit1;
+
+        outline.n_contours = outline.n_points = limit;
+
+        subglyph = gloader->current.subglyphs;
+
+        for ( i = 0; i < limit; i++, subglyph++ )
+        {
+          /* applying deltas for anchor points doesn't make sense, */
+          /* but we don't have to specially check this since       */
+          /* unused delta values are zero anyways                  */
+          outline.points[i].x = subglyph->arg1;
+          outline.points[i].y = subglyph->arg2;
+          outline.tags[i]     = ON_CURVE_POINT;
+          outline.contours[i] = i;
+        }
+
+        outline.points[i++] = loader->pp1;
+        outline.points[i++] = loader->pp2;
+        outline.points[i++] = loader->pp3;
+        outline.points[i  ] = loader->pp4;
+
+        /* this call provides additional offsets */
+        /* for each component's translation      */
+        if ( FT_SET_ERROR( TT_Vary_Apply_Glyph_Deltas( loader,
+                                                       &outline,
+                                                       unrounded ) ) )
+          goto Exit1;
+
+        subglyph = gloader->current.subglyphs;
+
+        for ( i = 0; i < limit; i++, subglyph++ )
+        {
+          if ( subglyph->flags & ARGS_ARE_XY_VALUES )
+          {
+            subglyph->arg1 = (FT_Int16)outline.points[i].x;
+            subglyph->arg2 = (FT_Int16)outline.points[i].y;
+          }
+        }
+
+      Exit1:
+        FT_FREE( outline.points );
+        FT_FREE( outline.tags );
+        FT_FREE( outline.contours );
+        FT_FREE( unrounded );
+
+        if ( error )
+          goto Exit;
+      }
+
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+      /* scale phantom points, if necessary; */
+      /* they get rounded in `TT_Hint_Glyph' */
+      if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
+      {
+        loader->pp1.x = FT_MulFix( loader->pp1.x, x_scale );
+        loader->pp2.x = FT_MulFix( loader->pp2.x, x_scale );
+        /* pp1.y and pp2.y are always zero */
+
+        loader->pp3.x = FT_MulFix( loader->pp3.x, x_scale );
+        loader->pp3.y = FT_MulFix( loader->pp3.y, y_scale );
+        loader->pp4.x = FT_MulFix( loader->pp4.x, x_scale );
+        loader->pp4.y = FT_MulFix( loader->pp4.y, y_scale );
+      }
+
+      /* if the flag FT_LOAD_NO_RECURSE is set, we return the subglyph */
+      /* `as is' in the glyph slot (the client application will be     */
+      /* responsible for interpreting these data)...                   */
+      if ( loader->load_flags & FT_LOAD_NO_RECURSE )
+      {
+        FT_GlyphLoader_Add( gloader );
+        loader->glyph->format = FT_GLYPH_FORMAT_COMPOSITE;
+
+        goto Exit;
+      }
+
+      /*********************************************************************/
+      /*********************************************************************/
+      /*********************************************************************/
+
+      {
+        FT_UInt      n, num_base_points;
+        FT_SubGlyph  subglyph       = NULL;
+
+        FT_UInt      num_points     = start_point;
+        FT_UInt      num_subglyphs  = gloader->current.num_subglyphs;
+        FT_UInt      num_base_subgs = gloader->base.num_subglyphs;
+
+        FT_Stream    old_stream     = loader->stream;
+        FT_UInt      old_byte_len   = loader->byte_len;
+
+
+        FT_GlyphLoader_Add( gloader );
+
+        /* read each subglyph independently */
+        for ( n = 0; n < num_subglyphs; n++ )
+        {
+          FT_Vector  pp[4];
+
+          FT_Int  linear_hadvance;
+          FT_Int  linear_vadvance;
+
+
+          /* Each time we call `load_truetype_glyph' in this loop, the */
+          /* value of `gloader.base.subglyphs' can change due to table */
+          /* reallocations.  We thus need to recompute the subglyph    */
+          /* pointer on each iteration.                                */
+          subglyph = gloader->base.subglyphs + num_base_subgs + n;
+
+          pp[0] = loader->pp1;
+          pp[1] = loader->pp2;
+          pp[2] = loader->pp3;
+          pp[3] = loader->pp4;
+
+          linear_hadvance = loader->linear;
+          linear_vadvance = loader->vadvance;
+
+          num_base_points = (FT_UInt)gloader->base.outline.n_points;
+
+          error = load_truetype_glyph( loader,
+                                       (FT_UInt)subglyph->index,
+                                       recurse_count + 1,
+                                       FALSE );
+          if ( error )
+            goto Exit;
+
+          /* restore subglyph pointer */
+          subglyph = gloader->base.subglyphs + num_base_subgs + n;
+
+          /* restore phantom points if necessary */
+          if ( !( subglyph->flags & USE_MY_METRICS ) )
+          {
+            loader->pp1 = pp[0];
+            loader->pp2 = pp[1];
+            loader->pp3 = pp[2];
+            loader->pp4 = pp[3];
+
+            loader->linear   = linear_hadvance;
+            loader->vadvance = linear_vadvance;
+          }
+
+          num_points = (FT_UInt)gloader->base.outline.n_points;
+
+          if ( num_points == num_base_points )
+            continue;
+
+          /* gloader->base.outline consists of three parts:           */
+          /*                                                          */
+          /* 0 ----> start_point ----> num_base_points ----> n_points */
+          /*    (1)               (2)                   (3)           */
+          /*                                                          */
+          /* (1) points that exist from the beginning                 */
+          /* (2) component points that have been loaded so far        */
+          /* (3) points of the newly loaded component                 */
+          error = TT_Process_Composite_Component( loader,
+                                                  subglyph,
+                                                  start_point,
+                                                  num_base_points );
+          if ( error )
+            goto Exit;
+        }
+
+        loader->stream   = old_stream;
+        loader->byte_len = old_byte_len;
+
+        /* process the glyph */
+        loader->ins_pos = ins_pos;
+        if ( IS_HINTED( loader->load_flags ) &&
+#ifdef TT_USE_BYTECODE_INTERPRETER
+             subglyph                        &&
+             subglyph->flags & WE_HAVE_INSTR &&
+#endif
+             num_points > start_point )
+        {
+          error = TT_Process_Composite_Glyph( loader,
+                                              start_point,
+                                              start_contour );
+          if ( error )
+            goto Exit;
+        }
+      }
+
+      /* retain the overlap flag */
+      if ( gloader->base.num_subglyphs                         &&
+           gloader->base.subglyphs[0].flags & OVERLAP_COMPOUND )
+        gloader->base.outline.flags |= FT_OUTLINE_OVERLAP;
+    }
+
+    /***********************************************************************/
+    /***********************************************************************/
+    /***********************************************************************/
+
+  Exit:
+
+    if ( opened_frame )
+      face->forget_glyph_frame( loader );
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+
+    if ( glyph_data_loaded )
+      face->root.internal->incremental_interface->funcs->free_glyph_data(
+        face->root.internal->incremental_interface->object,
+        &glyph_data );
+
+#endif
+
+    return error;
+  }
+
+
+  static FT_Error
+  compute_glyph_metrics( TT_Loader  loader,
+                         FT_UInt    glyph_index )
+  {
+    TT_Face       face  = loader->face;
+    TT_Size       size  = loader->size;
+    TT_GlyphSlot  glyph = loader->glyph;
+    FT_BBox       bbox;
+    FT_Fixed      y_scale;
+
+
+    y_scale = 0x10000L;
+    if ( ( loader->load_flags & FT_LOAD_NO_SCALE ) == 0 )
+      y_scale = size->metrics->y_scale;
+
+    if ( glyph->format != FT_GLYPH_FORMAT_COMPOSITE )
+      FT_Outline_Get_CBox( &glyph->outline, &bbox );
+    else
+      bbox = loader->bbox;
+
+    /* get the device-independent horizontal advance; it is scaled later */
+    /* by the base layer.                                                */
+    glyph->linearHoriAdvance = loader->linear;
+
+    glyph->metrics.horiBearingX = bbox.xMin;
+    glyph->metrics.horiBearingY = bbox.yMax;
+    if ( loader->widthp )
+      glyph->metrics.horiAdvance = loader->widthp[glyph_index] * 64;
+    else
+      glyph->metrics.horiAdvance = SUB_LONG( loader->pp2.x, loader->pp1.x );
+
+    /* set glyph dimensions */
+    glyph->metrics.width  = SUB_LONG( bbox.xMax, bbox.xMin );
+    glyph->metrics.height = SUB_LONG( bbox.yMax, bbox.yMin );
+
+    /* Now take care of vertical metrics.  In the case where there is */
+    /* no vertical information within the font (relatively common),   */
+    /* create some metrics manually                                   */
+    {
+      FT_Pos  top;      /* scaled vertical top side bearing  */
+      FT_Pos  advance;  /* scaled vertical advance height    */
+
+
+      /* Get the unscaled top bearing and advance height. */
+      if ( face->vertical_info                   &&
+           face->vertical.number_Of_VMetrics > 0 )
+      {
+        top = (FT_Short)FT_DivFix( SUB_LONG( loader->pp3.y, bbox.yMax ),
+                                   y_scale );
+
+        if ( loader->pp3.y <= loader->pp4.y )
+          advance = 0;
+        else
+          advance = (FT_UShort)FT_DivFix( SUB_LONG( loader->pp3.y,
+                                                    loader->pp4.y ),
+                                          y_scale );
+      }
+      else
+      {
+        FT_Pos  height;
+
+
+        /* XXX Compute top side bearing and advance height in  */
+        /*     Get_VMetrics instead of here.                   */
+
+        /* NOTE: The OS/2 values are the only `portable' ones, */
+        /*       which is why we use them, if there is an OS/2 */
+        /*       table in the font.  Otherwise, we use the     */
+        /*       values defined in the horizontal header.      */
+
+        height = (FT_Short)FT_DivFix( SUB_LONG( bbox.yMax,
+                                                bbox.yMin ),
+                                      y_scale );
+        if ( face->os2.version != 0xFFFFU )
+          advance = (FT_Pos)( face->os2.sTypoAscender -
+                              face->os2.sTypoDescender );
+        else
+          advance = (FT_Pos)( face->horizontal.Ascender -
+                              face->horizontal.Descender );
+
+        top = ( advance - height ) / 2;
+      }
+
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+      {
+        FT_Incremental_InterfaceRec*  incr;
+        FT_Incremental_MetricsRec     incr_metrics;
+        FT_Error                      error;
+
+
+        incr = face->root.internal->incremental_interface;
+
+        /* If this is an incrementally loaded font see if there are */
+        /* overriding metrics for this glyph.                       */
+        if ( incr && incr->funcs->get_glyph_metrics )
+        {
+          incr_metrics.bearing_x = 0;
+          incr_metrics.bearing_y = top;
+          incr_metrics.advance   = advance;
+
+          error = incr->funcs->get_glyph_metrics( incr->object,
+                                                  glyph_index,
+                                                  TRUE,
+                                                  &incr_metrics );
+          if ( error )
+            return error;
+
+          top     = incr_metrics.bearing_y;
+          advance = incr_metrics.advance;
+        }
+      }
+
+      /* GWW: Do vertical metrics get loaded incrementally too? */
+
+#endif /* FT_CONFIG_OPTION_INCREMENTAL */
+
+      glyph->linearVertAdvance = advance;
+
+      /* scale the metrics */
+      if ( !( loader->load_flags & FT_LOAD_NO_SCALE ) )
+      {
+        top     = FT_MulFix( top,     y_scale );
+        advance = FT_MulFix( advance, y_scale );
+      }
+
+      /* XXX: for now, we have no better algorithm for the lsb, but it */
+      /*      should work fine.                                        */
+      /*                                                               */
+      glyph->metrics.vertBearingX = SUB_LONG( glyph->metrics.horiBearingX,
+                                              glyph->metrics.horiAdvance / 2 );
+      glyph->metrics.vertBearingY = top;
+      glyph->metrics.vertAdvance  = advance;
+    }
+
+    return FT_Err_Ok;
+  }
+
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+  static FT_Error
+  load_sbit_image( TT_Size       size,
+                   TT_GlyphSlot  glyph,
+                   FT_UInt       glyph_index,
+                   FT_Int32      load_flags )
+  {
+    TT_Face             face   = (TT_Face)glyph->face;
+    SFNT_Service        sfnt   = (SFNT_Service)face->sfnt;
+    FT_Stream           stream = face->root.stream;
+    FT_Error            error;
+    TT_SBit_MetricsRec  sbit_metrics;
+
+
+    error = sfnt->load_sbit_image( face,
+                                   size->strike_index,
+                                   glyph_index,
+                                   (FT_UInt)load_flags,
+                                   stream,
+                                   &glyph->bitmap,
+                                   &sbit_metrics );
+    if ( !error )
+    {
+      glyph->outline.n_points   = 0;
+      glyph->outline.n_contours = 0;
+
+      glyph->metrics.width  = (FT_Pos)sbit_metrics.width  * 64;
+      glyph->metrics.height = (FT_Pos)sbit_metrics.height * 64;
+
+      glyph->metrics.horiBearingX = (FT_Pos)sbit_metrics.horiBearingX * 64;
+      glyph->metrics.horiBearingY = (FT_Pos)sbit_metrics.horiBearingY * 64;
+      glyph->metrics.horiAdvance  = (FT_Pos)sbit_metrics.horiAdvance  * 64;
+
+      glyph->metrics.vertBearingX = (FT_Pos)sbit_metrics.vertBearingX * 64;
+      glyph->metrics.vertBearingY = (FT_Pos)sbit_metrics.vertBearingY * 64;
+      glyph->metrics.vertAdvance  = (FT_Pos)sbit_metrics.vertAdvance  * 64;
+
+      glyph->format = FT_GLYPH_FORMAT_BITMAP;
+
+      if ( load_flags & FT_LOAD_VERTICAL_LAYOUT )
+      {
+        glyph->bitmap_left = sbit_metrics.vertBearingX;
+        glyph->bitmap_top  = sbit_metrics.vertBearingY;
+      }
+      else
+      {
+        glyph->bitmap_left = sbit_metrics.horiBearingX;
+        glyph->bitmap_top  = sbit_metrics.horiBearingY;
+      }
+    }
+
+    return error;
+  }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+
+  static FT_Error
+  tt_loader_init( TT_Loader     loader,
+                  TT_Size       size,
+                  TT_GlyphSlot  glyph,
+                  FT_Int32      load_flags,
+                  FT_Bool       glyf_table_only )
+  {
+    TT_Face    face   = (TT_Face)glyph->face;
+    FT_Stream  stream = face->root.stream;
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+    FT_Error   error;
+    FT_Bool    pedantic = FT_BOOL( load_flags & FT_LOAD_PEDANTIC );
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    TT_Driver  driver   = (TT_Driver)FT_FACE_DRIVER( glyph->face );
+#endif
+#endif
+
+
+    FT_ZERO( loader );
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+    /* load execution context */
+    if ( IS_HINTED( load_flags ) && !glyf_table_only )
+    {
+      TT_ExecContext  exec;
+      FT_Bool         grayscale = TRUE;
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+      FT_Bool         subpixel_hinting_lean;
+      FT_Bool         grayscale_cleartype;
+#endif
+
+      FT_Bool  reexecute = FALSE;
+
+
+      if ( size->bytecode_ready < 0 || size->cvt_ready < 0 )
+      {
+        error = tt_size_ready_bytecode( size, pedantic );
+        if ( error )
+          return error;
+      }
+      else if ( size->bytecode_ready )
+        return size->bytecode_ready;
+      else if ( size->cvt_ready )
+        return size->cvt_ready;
+
+      /* query new execution context */
+      exec = size->context;
+      if ( !exec )
+        return FT_THROW( Could_Not_Find_Context );
+
+      grayscale = FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) !=
+                             FT_RENDER_MODE_MONO             );
+
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+      if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 )
+      {
+        subpixel_hinting_lean =
+          FT_BOOL( FT_LOAD_TARGET_MODE( load_flags ) !=
+                   FT_RENDER_MODE_MONO               );
+        grayscale_cleartype =
+          FT_BOOL( subpixel_hinting_lean         &&
+                   !( ( load_flags         &
+                        FT_LOAD_TARGET_LCD )   ||
+                      ( load_flags           &
+                        FT_LOAD_TARGET_LCD_V ) ) );
+        exec->vertical_lcd_lean =
+          FT_BOOL( subpixel_hinting_lean    &&
+                   ( load_flags           &
+                     FT_LOAD_TARGET_LCD_V ) );
+        grayscale = FT_BOOL( grayscale && !subpixel_hinting_lean );
+      }
+      else
+      {
+        subpixel_hinting_lean   = FALSE;
+        grayscale_cleartype     = FALSE;
+        exec->vertical_lcd_lean = FALSE;
+      }
+#endif
+
+      error = TT_Load_Context( exec, face, size );
+      if ( error )
+        return error;
+
+      {
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+        if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 )
+        {
+          /* a change from mono to subpixel rendering (and vice versa) */
+          /* requires a re-execution of the CVT program                */
+          if ( subpixel_hinting_lean != exec->subpixel_hinting_lean )
+          {
+            FT_TRACE4(( "tt_loader_init: subpixel hinting change,"
+                        " re-executing `prep' table\n" ));
+
+            exec->subpixel_hinting_lean = subpixel_hinting_lean;
+            reexecute                   = TRUE;
+          }
+
+          /* a change from colored to grayscale subpixel rendering (and */
+          /* vice versa) requires a re-execution of the CVT program     */
+          if ( grayscale_cleartype != exec->grayscale_cleartype )
+          {
+            FT_TRACE4(( "tt_loader_init: grayscale subpixel hinting change,"
+                        " re-executing `prep' table\n" ));
+
+            exec->grayscale_cleartype = grayscale_cleartype;
+            reexecute                 = TRUE;
+          }
+        }
+#endif
+
+        /* a change from mono to grayscale rendering (and vice versa) */
+        /* requires a re-execution of the CVT program                 */
+        if ( grayscale != exec->grayscale )
+        {
+          FT_TRACE4(( "tt_loader_init: grayscale hinting change,"
+                      " re-executing `prep' table\n" ));
+
+          exec->grayscale = grayscale;
+          reexecute       = TRUE;
+        }
+      }
+
+      if ( reexecute )
+      {
+        error = tt_size_run_prep( size, pedantic );
+        if ( error )
+          return error;
+        error = TT_Load_Context( exec, face, size );
+        if ( error )
+          return error;
+      }
+
+      /* check whether the cvt program has disabled hinting */
+      if ( exec->GS.instruct_control & 1 )
+        load_flags |= FT_LOAD_NO_HINTING;
+
+      /* load default graphics state -- if needed */
+      if ( exec->GS.instruct_control & 2 )
+        exec->GS = tt_default_graphics_state;
+
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+      /*
+       * Toggle backward compatibility according to what font wants, except
+       * when
+       *
+       * 1) we have a `tricky' font that heavily relies on the interpreter to
+       *    render glyphs correctly, for example DFKai-SB, or
+       * 2) FT_RENDER_MODE_MONO (i.e, monochome rendering) is requested.
+       *
+       * In those cases, backward compatibility needs to be turned off to get
+       * correct rendering.  The rendering is then completely up to the
+       * font's programming.
+       *
+       */
+      if ( driver->interpreter_version == TT_INTERPRETER_VERSION_40 &&
+           subpixel_hinting_lean                                    &&
+           !FT_IS_TRICKY( glyph->face )                             )
+        exec->backward_compatibility = !( exec->GS.instruct_control & 4 );
+      else
+        exec->backward_compatibility = FALSE;
+#endif /* TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL */
+
+      exec->pedantic_hinting = FT_BOOL( load_flags & FT_LOAD_PEDANTIC );
+      loader->exec = exec;
+      loader->instructions = exec->glyphIns;
+
+      /* Use the hdmx table if any unless FT_LOAD_COMPUTE_METRICS */
+      /* is set or backward compatibility mode of the v38 or v40  */
+      /* interpreters is active.  See `ttinterp.h' for details on */
+      /* backward compatibility mode.                             */
+      if ( IS_HINTED( loader->load_flags )                                &&
+           !( loader->load_flags & FT_LOAD_COMPUTE_METRICS )              &&
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+           !( driver->interpreter_version == TT_INTERPRETER_VERSION_40  &&
+              exec->backward_compatibility                              ) &&
+#endif
+           !face->postscript.isFixedPitch                                 )
+      {
+        loader->widthp = size->widthp;
+      }
+      else
+        loader->widthp = NULL;
+    }
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+    /* get face's glyph loader */
+    if ( !glyf_table_only )
+    {
+      FT_GlyphLoader  gloader = glyph->internal->loader;
+
+
+      FT_GlyphLoader_Rewind( gloader );
+      loader->gloader = gloader;
+    }
+
+    loader->load_flags = (FT_ULong)load_flags;
+
+    loader->face   = face;
+    loader->size   = size;
+    loader->glyph  = (FT_GlyphSlot)glyph;
+    loader->stream = stream;
+
+    loader->composites.head = NULL;
+    loader->composites.tail = NULL;
+
+    return FT_Err_Ok;
+  }
+
+
+  static void
+  tt_loader_done( TT_Loader  loader )
+  {
+    FT_List_Finalize( &loader->composites,
+                      NULL,
+                      loader->face->root.memory,
+                      NULL );
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Load_Glyph
+   *
+   * @Description:
+   *   A function used to load a single glyph within a given glyph slot,
+   *   for a given size.
+   *
+   * @InOut:
+   *   glyph ::
+   *     A handle to a target slot object where the glyph
+   *     will be loaded.
+   *
+   * @Input:
+   *   size ::
+   *     A handle to the source face size at which the glyph
+   *     must be scaled/loaded.
+   *
+   *   glyph_index ::
+   *     The index of the glyph in the font file.
+   *
+   *   load_flags ::
+   *     A flag indicating what to load for this glyph.  The
+   *     FT_LOAD_XXX constants can be used to control the
+   *     glyph loading process (e.g., whether the outline
+   *     should be scaled, whether to load bitmaps or not,
+   *     whether to hint the outline, etc).
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  TT_Load_Glyph( TT_Size       size,
+                 TT_GlyphSlot  glyph,
+                 FT_UInt       glyph_index,
+                 FT_Int32      load_flags )
+  {
+    TT_Face       face = (TT_Face)glyph->face;
+    FT_Error      error;
+    TT_LoaderRec  loader;
+
+
+    FT_TRACE1(( "TT_Load_Glyph: glyph index %d\n", glyph_index ));
+
+#ifdef TT_CONFIG_OPTION_EMBEDDED_BITMAPS
+
+    /* try to load embedded bitmap (if any) */
+    if ( size->strike_index != 0xFFFFFFFFUL      &&
+         ( load_flags & FT_LOAD_NO_BITMAP ) == 0 &&
+         IS_DEFAULT_INSTANCE( glyph->face )      )
+    {
+      FT_Fixed  x_scale = size->root.metrics.x_scale;
+      FT_Fixed  y_scale = size->root.metrics.y_scale;
+
+
+      error = load_sbit_image( size, glyph, glyph_index, load_flags );
+      if ( FT_ERR_EQ( error, Missing_Bitmap ) )
+      {
+        /* the bitmap strike is incomplete and misses the requested glyph; */
+        /* if we have a bitmap-only font, return an empty glyph            */
+        if ( !FT_IS_SCALABLE( glyph->face ) )
+        {
+          FT_Short  left_bearing = 0;
+          FT_Short  top_bearing  = 0;
+
+          FT_UShort  advance_width  = 0;
+          FT_UShort  advance_height = 0;
+
+
+          /* to return an empty glyph, however, we need metrics data   */
+          /* from the `hmtx' (or `vmtx') table; the assumption is that */
+          /* empty glyphs are missing intentionally, representing      */
+          /* whitespace - not having at least horizontal metrics is    */
+          /* thus considered an error                                  */
+          if ( !face->horz_metrics_size )
+            return error;
+
+          /* we now construct an empty bitmap glyph */
+          TT_Get_HMetrics( face, glyph_index,
+                           &left_bearing,
+                           &advance_width );
+          TT_Get_VMetrics( face, glyph_index,
+                           0,
+                           &top_bearing,
+                           &advance_height );
+
+          glyph->outline.n_points   = 0;
+          glyph->outline.n_contours = 0;
+
+          glyph->metrics.width  = 0;
+          glyph->metrics.height = 0;
+
+          glyph->metrics.horiBearingX = FT_MulFix( left_bearing, x_scale );
+          glyph->metrics.horiBearingY = 0;
+          glyph->metrics.horiAdvance  = FT_MulFix( advance_width, x_scale );
+
+          glyph->metrics.vertBearingX = 0;
+          glyph->metrics.vertBearingY = FT_MulFix( top_bearing, y_scale );
+          glyph->metrics.vertAdvance  = FT_MulFix( advance_height, y_scale );
+
+          glyph->format            = FT_GLYPH_FORMAT_BITMAP;
+          glyph->bitmap.pixel_mode = FT_PIXEL_MODE_MONO;
+
+          glyph->bitmap_left = 0;
+          glyph->bitmap_top  = 0;
+
+          return FT_Err_Ok;
+        }
+      }
+      else if ( error )
+      {
+        /* return error if font is not scalable */
+        if ( !FT_IS_SCALABLE( glyph->face ) )
+          return error;
+      }
+      else
+      {
+        if ( FT_IS_SCALABLE( glyph->face ) ||
+             FT_HAS_SBIX( glyph->face )    )
+        {
+          /* for the bbox we need the header only */
+          (void)tt_loader_init( &loader, size, glyph, load_flags, TRUE );
+          (void)load_truetype_glyph( &loader, glyph_index, 0, TRUE );
+          tt_loader_done( &loader );
+          glyph->linearHoriAdvance = loader.linear;
+          glyph->linearVertAdvance = loader.vadvance;
+
+          /* Bitmaps from the 'sbix' table need special treatment:  */
+          /* if there is a glyph contour, the bitmap origin must be */
+          /* shifted to be relative to the lower left corner of the */
+          /* glyph bounding box, also taking the left-side bearing  */
+          /* (or top bearing) into account.                         */
+          if ( face->sbit_table_type == TT_SBIT_TABLE_TYPE_SBIX &&
+               loader.n_contours > 0                            )
+          {
+            FT_Int  bitmap_left;
+            FT_Int  bitmap_top;
+
+
+            if ( load_flags & FT_LOAD_VERTICAL_LAYOUT )
+            {
+              /* This is a guess, since Apple's CoreText engine doesn't */
+              /* really do vertical typesetting.                        */
+              bitmap_left = loader.bbox.xMin;
+              bitmap_top  = loader.top_bearing;
+            }
+            else
+            {
+              bitmap_left = loader.left_bearing;
+              bitmap_top  = loader.bbox.yMin;
+            }
+
+            glyph->bitmap_left += FT_MulFix( bitmap_left, x_scale ) >> 6;
+            glyph->bitmap_top  += FT_MulFix( bitmap_top,  y_scale ) >> 6;
+          }
+
+          /* sanity checks: if `xxxAdvance' in the sbit metric */
+          /* structure isn't set, use `linearXXXAdvance'      */
+          if ( !glyph->metrics.horiAdvance && glyph->linearHoriAdvance )
+            glyph->metrics.horiAdvance = FT_MulFix( glyph->linearHoriAdvance,
+                                                    x_scale );
+          if ( !glyph->metrics.vertAdvance && glyph->linearVertAdvance )
+            glyph->metrics.vertAdvance = FT_MulFix( glyph->linearVertAdvance,
+                                                    y_scale );
+        }
+
+        return FT_Err_Ok;
+      }
+    }
+
+    if ( load_flags & FT_LOAD_SBITS_ONLY )
+    {
+      error = FT_THROW( Invalid_Argument );
+      goto Exit;
+    }
+
+#endif /* TT_CONFIG_OPTION_EMBEDDED_BITMAPS */
+
+    /* if FT_LOAD_NO_SCALE is not set, `ttmetrics' must be valid */
+    if ( !( load_flags & FT_LOAD_NO_SCALE ) && !size->ttmetrics.valid )
+    {
+      error = FT_THROW( Invalid_Size_Handle );
+      goto Exit;
+    }
+
+#ifdef FT_CONFIG_OPTION_SVG
+
+    /* check for OT-SVG */
+    if ( ( load_flags & FT_LOAD_NO_SVG ) == 0 &&
+         ( load_flags & FT_LOAD_COLOR )       &&
+         face->svg                            )
+    {
+      SFNT_Service  sfnt = (SFNT_Service)face->sfnt;
+
+
+      FT_TRACE3(( "Trying to load SVG glyph\n" ));
+
+      error = sfnt->load_svg_doc( glyph, glyph_index );
+      if ( !error )
+      {
+        FT_Fixed  x_scale = size->root.metrics.x_scale;
+        FT_Fixed  y_scale = size->root.metrics.y_scale;
+
+        FT_Short   leftBearing;
+        FT_Short   topBearing;
+        FT_UShort  advanceX;
+        FT_UShort  advanceY;
+
+
+        FT_TRACE3(( "Successfully loaded SVG glyph\n" ));
+
+        glyph->format = FT_GLYPH_FORMAT_SVG;
+
+        sfnt->get_metrics( face,
+                           FALSE,
+                           glyph_index,
+                           &leftBearing,
+                           &advanceX );
+        sfnt->get_metrics( face,
+                           TRUE,
+                           glyph_index,
+                           &topBearing,
+                           &advanceY );
+
+        glyph->linearHoriAdvance = advanceX;
+        glyph->linearVertAdvance = advanceY;
+
+        glyph->metrics.horiAdvance = FT_MulFix( advanceX, x_scale );
+        glyph->metrics.vertAdvance = FT_MulFix( advanceY, y_scale );
+
+        return error;
+      }
+
+      FT_TRACE3(( "Failed to load SVG glyph\n" ));
+    }
+
+    /* return immediately if we only want SVG glyphs */
+    if ( load_flags & FT_LOAD_SVG_ONLY )
+    {
+      error = FT_THROW( Invalid_Argument );
+      goto Exit;
+    }
+
+#endif /* FT_CONFIG_OPTION_SVG */
+
+    error = tt_loader_init( &loader, size, glyph, load_flags, FALSE );
+    if ( error )
+      goto Exit;
+
+    /* done if we are only interested in the `hdmx` advance */
+    if ( load_flags & FT_LOAD_ADVANCE_ONLY         &&
+         !( load_flags & FT_LOAD_VERTICAL_LAYOUT ) &&
+         loader.widthp                             )
+    {
+      glyph->metrics.horiAdvance = loader.widthp[glyph_index] * 64;
+      goto Done;
+    }
+
+    glyph->format        = FT_GLYPH_FORMAT_OUTLINE;
+    glyph->num_subglyphs = 0;
+    glyph->outline.flags = 0;
+
+    /* main loading loop */
+    error = load_truetype_glyph( &loader, glyph_index, 0, FALSE );
+    if ( !error )
+    {
+      if ( glyph->format == FT_GLYPH_FORMAT_COMPOSITE )
+      {
+        glyph->num_subglyphs = loader.gloader->base.num_subglyphs;
+        glyph->subglyphs     = loader.gloader->base.subglyphs;
+      }
+      else
+      {
+        glyph->outline        = loader.gloader->base.outline;
+        glyph->outline.flags &= ~FT_OUTLINE_SINGLE_PASS;
+
+        /* Translate array so that (0,0) is the glyph's origin.  Note  */
+        /* that this behaviour is independent on the value of bit 1 of */
+        /* the `flags' field in the `head' table -- at least major     */
+        /* applications like Acroread indicate that.                   */
+        if ( loader.pp1.x )
+          FT_Outline_Translate( &glyph->outline, -loader.pp1.x, 0 );
+      }
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+      if ( IS_HINTED( load_flags ) )
+      {
+        glyph->control_data = loader.exec->glyphIns;
+        glyph->control_len  = loader.exec->glyphSize;
+
+        if ( loader.exec->GS.scan_control )
+        {
+          /* convert scan conversion mode to FT_OUTLINE_XXX flags */
+          switch ( loader.exec->GS.scan_type )
+          {
+          case 0: /* simple drop-outs including stubs */
+            glyph->outline.flags |= FT_OUTLINE_INCLUDE_STUBS;
+            break;
+          case 1: /* simple drop-outs excluding stubs */
+            /* nothing; it's the default rendering mode */
+            break;
+          case 4: /* smart drop-outs including stubs */
+            glyph->outline.flags |= FT_OUTLINE_SMART_DROPOUTS |
+                                    FT_OUTLINE_INCLUDE_STUBS;
+            break;
+          case 5: /* smart drop-outs excluding stubs  */
+            glyph->outline.flags |= FT_OUTLINE_SMART_DROPOUTS;
+            break;
+
+          default: /* no drop-out control */
+            glyph->outline.flags |= FT_OUTLINE_IGNORE_DROPOUTS;
+            break;
+          }
+        }
+        else
+          glyph->outline.flags |= FT_OUTLINE_IGNORE_DROPOUTS;
+      }
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+      error = compute_glyph_metrics( &loader, glyph_index );
+    }
+
+    /* Set the `high precision' bit flag.                           */
+    /* This is _critical_ to get correct output for monochrome      */
+    /* TrueType glyphs at all sizes using the bytecode interpreter. */
+    /*                                                              */
+    if ( !( load_flags & FT_LOAD_NO_SCALE ) &&
+         size->metrics->y_ppem < 24         )
+      glyph->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
+
+    FT_TRACE1(( "  subglyphs = %u, contours = %hd, points = %hd,"
+                " flags = 0x%.3x\n",
+                loader.gloader->base.num_subglyphs,
+                glyph->outline.n_contours,
+                glyph->outline.n_points,
+                glyph->outline.flags ));
+
+  Done:
+    tt_loader_done( &loader );
+
+  Exit:
+#ifdef FT_DEBUG_LEVEL_TRACE
+    if ( error )
+      FT_TRACE1(( "  failed (error code 0x%x)\n",
+                  error ));
+#endif
+
+    return error;
+  }
+
+
+/* END */
diff --git a/src/preload/ttgload.h b/src/preload/ttgload.h
new file mode 100644
index 000000000..f18637dce
--- /dev/null
+++ b/src/preload/ttgload.h
@@ -0,0 +1,61 @@
+/****************************************************************************
+ *
+ * ttgload.h
+ *
+ *   TrueType Glyph Loader (specification).
+ *
+ * Copyright (C) 1996-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+#ifndef TTGLOAD_H_
+#define TTGLOAD_H_
+
+
+#include "ttobjs.h"
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+#include "ttinterp.h"
+#endif
+
+
+FT_BEGIN_HEADER
+
+
+  FT_LOCAL( void )
+  TT_Init_Glyph_Loading( TT_Face  face );
+
+  FT_LOCAL( void )
+  TT_Get_HMetrics( TT_Face     face,
+                   FT_UInt     idx,
+                   FT_Short*   lsb,
+                   FT_UShort*  aw );
+
+  FT_LOCAL( void )
+  TT_Get_VMetrics( TT_Face     face,
+                   FT_UInt     idx,
+                   FT_Pos      yMax,
+                   FT_Short*   tsb,
+                   FT_UShort*  ah );
+
+  FT_LOCAL( FT_Error )
+  TT_Load_Glyph( TT_Size       size,
+                 TT_GlyphSlot  glyph,
+                 FT_UInt       glyph_index,
+                 FT_Int32      load_flags );
+
+
+FT_END_HEADER
+
+#endif /* TTGLOAD_H_ */
+
+
+/* END */
diff --git a/src/preload/ttgxvar.c b/src/preload/ttgxvar.c
new file mode 100644
index 000000000..8c713f1b6
--- /dev/null
+++ b/src/preload/ttgxvar.c
@@ -0,0 +1,4661 @@
+/****************************************************************************
+ *
+ * ttgxvar.c
+ *
+ *   TrueType GX Font Variation loader
+ *
+ * Copyright (C) 2004-2023 by
+ * David Turner, Robert Wilhelm, Werner Lemberg, and George Williams.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+  /**************************************************************************
+   *
+   * Apple documents the `fvar', `gvar', `cvar', and `avar' tables at
+   *
+   *   
https://developer.apple.com/fonts/TrueType-Reference-Manual/RM06/Chap6[fgca]var.html
+   *
+   * The documentation for `gvar' is not intelligible; `cvar' refers you
+   * to `gvar' and is thus also incomprehensible.
+   *
+   * The documentation for `avar' appears correct, but Apple has no fonts
+   * with an `avar' table, so it is hard to test.
+   *
+   * Many thanks to John Jenkins (at Apple) in figuring this out.
+   *
+   *
+   * Apple's `kern' table has some references to tuple indices, but as
+   * there is no indication where these indices are defined, nor how to
+   * interpolate the kerning values (different tuples have different
+   * classes) this issue is ignored.
+   *
+   */
+
+
+#include <ft2build.h>
+#include <freetype/internal/ftdebug.h>
+#include FT_CONFIG_CONFIG_H
+#include <freetype/internal/ftcalc.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/internal/sfnt.h>
+#include <freetype/internal/services/svmetric.h>
+#include <freetype/tttags.h>
+#include <freetype/ttnameid.h>
+#include <freetype/ftmm.h>
+#include <freetype/ftlist.h>
+
+#include "ttpload.h"
+#include "ttgxvar.h"
+
+#include "tterrors.h"
+
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+
+#define FT_Stream_FTell( stream )                         \
+          (FT_ULong)( (stream)->cursor - (stream)->base )
+#define FT_Stream_SeekSet( stream, off )                               \
+          (stream)->cursor =                                           \
+            ( (off) < (FT_ULong)( (stream)->limit - (stream)->base ) ) \
+                        ? (stream)->base + (off)                       \
+                        : (stream)->limit
+
+
+  /* some macros we need */
+#define FT_fdot14ToFixed( x )                  \
+          ( (FT_Fixed)( (FT_ULong)(x) << 2 ) )
+#define FT_intToFixed( i )                      \
+          ( (FT_Fixed)( (FT_ULong)(i) << 16 ) )
+#define FT_fdot6ToFixed( i )                    \
+          ( (FT_Fixed)( (FT_ULong)(i) << 10 ) )
+#define FT_fixedToInt( x )                          \
+          ( (FT_Short)( ( (x) + 0x8000U ) >> 16 ) )
+#define FT_fixedToFdot6( x )                    \
+          ( (FT_Pos)( ( (x) + 0x200 ) >> 10 ) )
+
+
+  /**************************************************************************
+   *
+   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
+   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
+   * messages during execution.
+   */
+#undef  FT_COMPONENT
+#define FT_COMPONENT  ttgxvar
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                       Internal Routines                       *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+
+  /**************************************************************************
+   *
+   * The macro ALL_POINTS is used in `ft_var_readpackedpoints'.  It
+   * indicates that there is a delta for every point without needing to
+   * enumerate all of them.
+   */
+
+  /* ensure that value `0' has the same width as a pointer */
+#define ALL_POINTS  (FT_UShort*)~(FT_PtrDist)0
+
+
+#define GX_PT_POINTS_ARE_WORDS      0x80U
+#define GX_PT_POINT_RUN_COUNT_MASK  0x7FU
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   ft_var_readpackedpoints
+   *
+   * @Description:
+   *   Read a set of points to which the following deltas will apply.
+   *   Points are packed with a run length encoding.
+   *
+   * @Input:
+   *   stream ::
+   *     The data stream.
+   *
+   *   size ::
+   *     The size of the table holding the data.
+   *
+   * @Output:
+   *   point_cnt ::
+   *     The number of points read.  A zero value means that
+   *     all points in the glyph will be affected, without
+   *     enumerating them individually.
+   *
+   * @Return:
+   *   An array of FT_UShort containing the affected points or the
+   *   special value ALL_POINTS.
+   */
+  static FT_UShort*
+  ft_var_readpackedpoints( FT_Stream  stream,
+                           FT_ULong   size,
+                           FT_UInt   *point_cnt )
+  {
+    FT_UShort *points = NULL;
+    FT_UInt    n;
+    FT_UInt    runcnt;
+    FT_UInt    i, j;
+    FT_UShort  first;
+    FT_Memory  memory = stream->memory;
+    FT_Error   error;
+
+
+    *point_cnt = 0;
+
+    n = FT_GET_BYTE();
+    if ( n == 0 )
+      return ALL_POINTS;
+
+    if ( n & GX_PT_POINTS_ARE_WORDS )
+    {
+      n  &= GX_PT_POINT_RUN_COUNT_MASK;
+      n <<= 8;
+      n  |= FT_GET_BYTE();
+    }
+
+    if ( n > size )
+    {
+      FT_TRACE1(( "ft_var_readpackedpoints: number of points too large\n" ));
+      return NULL;
+    }
+
+    /* in the nested loops below we increase `i' twice; */
+    /* it is faster to simply allocate one more slot    */
+    /* than to add another test within the loop         */
+    if ( FT_QNEW_ARRAY( points, n + 1 ) )
+      return NULL;
+
+    *point_cnt = n;
+
+    first = 0;
+    i     = 0;
+    while ( i < n )
+    {
+      runcnt = FT_GET_BYTE();
+      if ( runcnt & GX_PT_POINTS_ARE_WORDS )
+      {
+        runcnt     &= GX_PT_POINT_RUN_COUNT_MASK;
+        first      += FT_GET_USHORT();
+        points[i++] = first;
+
+        /* first point not included in run count */
+        for ( j = 0; j < runcnt; j++ )
+        {
+          first      += FT_GET_USHORT();
+          points[i++] = first;
+          if ( i >= n )
+            break;
+        }
+      }
+      else
+      {
+        first      += FT_GET_BYTE();
+        points[i++] = first;
+
+        for ( j = 0; j < runcnt; j++ )
+        {
+          first      += FT_GET_BYTE();
+          points[i++] = first;
+          if ( i >= n )
+            break;
+        }
+      }
+    }
+
+    return points;
+  }
+
+
+#define GX_DT_DELTAS_ARE_ZERO       0x80U
+#define GX_DT_DELTAS_ARE_WORDS      0x40U
+#define GX_DT_DELTA_RUN_COUNT_MASK  0x3FU
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   ft_var_readpackeddeltas
+   *
+   * @Description:
+   *   Read a set of deltas.  These are packed slightly differently than
+   *   points.  In particular there is no overall count.
+   *
+   * @Input:
+   *   stream ::
+   *     The data stream.
+   *
+   *   size ::
+   *     The size of the table holding the data.
+   *
+   *   delta_cnt ::
+   *     The number of deltas to be read.
+   *
+   * @Return:
+   *   An array of FT_Fixed containing the deltas for the affected
+   *   points.  (This only gets the deltas for one dimension.  It will
+   *   generally be called twice, once for x, once for y.  When used in
+   *   cvt table, it will only be called once.)
+   *
+   *   We use FT_Fixed to avoid accumulation errors while summing up all
+   *   deltas (the rounding to integer values happens as the very last
+   *   step).
+   */
+  static FT_Fixed*
+  ft_var_readpackeddeltas( FT_Stream  stream,
+                           FT_ULong   size,
+                           FT_UInt    delta_cnt )
+  {
+    FT_Fixed  *deltas = NULL;
+    FT_UInt    runcnt, cnt;
+    FT_UInt    i, j;
+    FT_UInt    bytes_used;
+    FT_Memory  memory = stream->memory;
+    FT_Error   error;
+
+
+    if ( FT_QNEW_ARRAY( deltas, delta_cnt ) )
+      return NULL;
+
+    i          = 0;
+    bytes_used = 0;
+
+    while ( i < delta_cnt && bytes_used < size )
+    {
+      runcnt = FT_GET_BYTE();
+      cnt    = runcnt & GX_DT_DELTA_RUN_COUNT_MASK;
+
+      bytes_used++;
+
+      if ( runcnt & GX_DT_DELTAS_ARE_ZERO )
+      {
+        /* `cnt` + 1 zeroes get added */
+        for ( j = 0; j <= cnt && i < delta_cnt; j++ )
+          deltas[i++] = 0;
+      }
+      else if ( runcnt & GX_DT_DELTAS_ARE_WORDS )
+      {
+        /* `cnt` + 1 shorts from the stack */
+        bytes_used += 2 * ( cnt + 1 );
+        if ( bytes_used > size )
+        {
+          FT_TRACE1(( "ft_var_readpackeddeltas:"
+                      " number of short deltas too large\n" ));
+          goto Fail;
+        }
+
+        for ( j = 0; j <= cnt && i < delta_cnt; j++ )
+          deltas[i++] = FT_intToFixed( FT_GET_SHORT() );
+      }
+      else
+      {
+        /* `cnt` + 1 signed bytes from the stack */
+        bytes_used += cnt + 1;
+        if ( bytes_used > size )
+        {
+          FT_TRACE1(( "ft_var_readpackeddeltas:"
+                      " number of byte deltas too large\n" ));
+          goto Fail;
+        }
+
+        for ( j = 0; j <= cnt && i < delta_cnt; j++ )
+          deltas[i++] = FT_intToFixed( FT_GET_CHAR() );
+      }
+
+      if ( j <= cnt )
+      {
+        FT_TRACE1(( "ft_var_readpackeddeltas:"
+                    " number of deltas too large\n" ));
+        goto Fail;
+      }
+    }
+
+    if ( i < delta_cnt )
+    {
+      FT_TRACE1(( "ft_var_readpackeddeltas: not enough deltas\n" ));
+      goto Fail;
+    }
+
+    return deltas;
+
+  Fail:
+    FT_FREE( deltas );
+    return NULL;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   ft_var_load_avar
+   *
+   * @Description:
+   *   Parse the `avar' table if present.  It need not be, so we return
+   *   nothing.
+   *
+   * @InOut:
+   *   face ::
+   *     The font face.
+   */
+  static void
+  ft_var_load_avar( TT_Face  face )
+  {
+    FT_Error   error;
+    FT_Stream  stream = FT_FACE_STREAM( face );
+    FT_Memory  memory = stream->memory;
+    FT_Int     i, j;
+
+    GX_Blend        blend  = face->blend;
+    GX_AVarSegment  segment;
+    GX_AVarTable    table;
+
+    FT_Long   version;
+    FT_Long   axisCount;
+    FT_ULong  table_len;
+
+#ifndef TT_CONFIG_OPTION_NO_BORING_EXPANSION
+    FT_ULong  table_offset;
+    FT_ULong  store_offset;
+    FT_ULong  axisMap_offset;
+#endif
+
+
+    FT_TRACE2(( "AVAR " ));
+
+    blend->avar_loaded = TRUE;
+    error = face->goto_table( face, TTAG_avar, stream, &table_len );
+    if ( error )
+    {
+      FT_TRACE2(( "is missing\n" ));
+      return;
+    }
+
+#ifndef TT_CONFIG_OPTION_NO_BORING_EXPANSION
+    table_offset = FT_STREAM_POS();
+#endif
+
+    if ( FT_FRAME_ENTER( table_len ) )
+      return;
+
+    version   = FT_GET_LONG();
+    axisCount = FT_GET_LONG();
+
+    if ( version != 0x00010000L
+#ifndef TT_CONFIG_OPTION_NO_BORING_EXPANSION
+         && version != 0x00020000L
+#endif
+       )
+    {
+      FT_TRACE2(( "bad table version\n" ));
+      goto Exit;
+    }
+
+    FT_TRACE2(( "loaded\n" ));
+
+    if ( axisCount != (FT_Long)blend->mmvar->num_axis )
+    {
+      FT_TRACE2(( "ft_var_load_avar:"
+                  " number of axes in `avar' and `fvar'\n" ));
+      FT_TRACE2(( "                  table are different\n" ));
+      goto Exit;
+    }
+
+    if ( FT_NEW( blend->avar_table ) )
+      goto Exit;
+    table = blend->avar_table;
+
+    if ( FT_QNEW_ARRAY( table->avar_segment, axisCount ) )
+      goto Exit;
+
+    segment = &table->avar_segment[0];
+    for ( i = 0; i < axisCount; i++, segment++ )
+    {
+      FT_TRACE5(( "  axis %d:\n", i ));
+
+      segment->pairCount = FT_GET_USHORT();
+      if ( (FT_ULong)segment->pairCount * 4 > table_len                 ||
+           FT_QNEW_ARRAY( segment->correspondence, segment->pairCount ) )
+      {
+        /* Failure.  Free everything we have done so far.  We must do */
+        /* it right now since loading the `avar' table is optional.   */
+
+        for ( j = i - 1; j >= 0; j-- )
+          FT_FREE( table->avar_segment[j].correspondence );
+
+        FT_FREE( table->avar_segment );
+        goto Exit;
+      }
+
+      for ( j = 0; j < segment->pairCount; j++ )
+      {
+        segment->correspondence[j].fromCoord =
+          FT_fdot14ToFixed( FT_GET_SHORT() );
+        segment->correspondence[j].toCoord =
+          FT_fdot14ToFixed( FT_GET_SHORT() );
+
+        FT_TRACE5(( "    mapping %.5f to %.5f\n",
+                    (double)segment->correspondence[j].fromCoord / 65536,
+                    (double)segment->correspondence[j].toCoord / 65536 ));
+      }
+
+      FT_TRACE5(( "\n" ));
+    }
+
+#ifndef TT_CONFIG_OPTION_NO_BORING_EXPANSION
+    if ( version < 0x00020000L )
+      goto Exit;
+
+    axisMap_offset = FT_GET_ULONG();
+    store_offset   = FT_GET_ULONG();
+
+    if ( store_offset )
+    {
+      error = tt_var_load_item_variation_store(
+                FT_FACE( face ),
+                table_offset + store_offset,
+                &table->itemStore );
+      if ( error )
+        goto Exit;
+    }
+
+    if ( axisMap_offset )
+    {
+      error = tt_var_load_delta_set_index_mapping(
+                FT_FACE( face ),
+                table_offset + axisMap_offset,
+                &table->axisMap,
+                &table->itemStore,
+                table_len );
+      if ( error )
+        goto Exit;
+    }
+#endif
+
+
+  Exit:
+    FT_FRAME_EXIT();
+  }
+
+
+  FT_LOCAL_DEF( FT_Error )
+  tt_var_load_item_variation_store( FT_Face          face,      /* TT_Face */
+                                    FT_ULong         offset,
+                                    GX_ItemVarStore  itemStore )
+  {
+    TT_Face    ttface = (TT_Face)face;
+    FT_Stream  stream = FT_FACE_STREAM( face );
+    FT_Memory  memory = stream->memory;
+
+    FT_Error   error;
+    FT_UShort  format;
+    FT_ULong   region_offset;
+
+    FT_UInt    data_count;
+    FT_UShort  axis_count;
+    FT_UInt    region_count;
+
+    FT_UInt  i, j;
+    FT_Bool  long_words;
+
+    GX_Blend   blend           = ttface->blend;
+    FT_ULong*  dataOffsetArray = NULL;
+
+
+    if ( FT_STREAM_SEEK( offset ) ||
+         FT_READ_USHORT( format ) )
+      goto Exit;
+
+    if ( format != 1 )
+    {
+      FT_TRACE2(( "tt_var_load_item_variation_store: bad store format %d\n",
+                  format ));
+      error = FT_THROW( Invalid_Table );
+      goto Exit;
+    }
+
+    /* read top level fields */
+    if ( FT_READ_ULONG( region_offset ) ||
+         FT_READ_USHORT( data_count )   )
+      goto Exit;
+
+    /* we need at least one entry in `itemStore->varData' */
+    if ( !data_count )
+    {
+      FT_TRACE2(( "tt_var_load_item_variation_store: missing varData\n" ));
+      error = FT_THROW( Invalid_Table );
+      goto Exit;
+    }
+
+    /* make temporary copy of item variation data offsets; */
+    /* we will parse region list first, then come back     */
+    if ( FT_QNEW_ARRAY( dataOffsetArray, data_count ) )
+      goto Exit;
+
+    for ( i = 0; i < data_count; i++ )
+    {
+      if ( FT_READ_ULONG( dataOffsetArray[i] ) )
+        goto Exit;
+    }
+
+    /* parse array of region records (region list) */
+    if ( FT_STREAM_SEEK( offset + region_offset ) )
+      goto Exit;
+
+    if ( FT_READ_USHORT( axis_count )   ||
+         FT_READ_USHORT( region_count ) )
+      goto Exit;
+
+    if ( axis_count != (FT_Long)blend->mmvar->num_axis )
+    {
+      FT_TRACE2(( "tt_var_load_item_variation_store:"
+                  " number of axes in item variation store\n" ));
+      FT_TRACE2(( "                                 "
+                  " and `fvar' table are different\n" ));
+      error = FT_THROW( Invalid_Table );
+      goto Exit;
+    }
+    itemStore->axisCount = axis_count;
+
+    /* new constraint in OpenType 1.8.4 */
+    if ( region_count >= 32768U )
+    {
+      FT_TRACE2(( "tt_var_load_item_variation_store:"
+                  " too many variation region tables\n" ));
+      error = FT_THROW( Invalid_Table );
+      goto Exit;
+    }
+
+    if ( FT_NEW_ARRAY( itemStore->varRegionList, region_count ) )
+      goto Exit;
+    itemStore->regionCount = region_count;
+
+    for ( i = 0; i < itemStore->regionCount; i++ )
+    {
+      GX_AxisCoords  axisCoords;
+
+
+      if ( FT_NEW_ARRAY( itemStore->varRegionList[i].axisList, axis_count ) )
+        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 */
+
+    /* use dataOffsetArray now to parse varData items */
+    if ( FT_NEW_ARRAY( itemStore->varData, data_count ) )
+      goto Exit;
+    itemStore->dataCount = data_count;
+
+    for ( i = 0; i < data_count; i++ )
+    {
+      GX_ItemVarData  varData = &itemStore->varData[i];
+
+      FT_UInt  item_count;
+      FT_UInt  word_delta_count;
+      FT_UInt  region_idx_count;
+      FT_UInt  per_region_size;
+
+
+      if ( FT_STREAM_SEEK( offset + dataOffsetArray[i] ) )
+        goto Exit;
+
+      if ( FT_READ_USHORT( item_count )       ||
+           FT_READ_USHORT( word_delta_count ) ||
+           FT_READ_USHORT( region_idx_count ) )
+        goto Exit;
+
+      long_words        = !!( word_delta_count & 0x8000 );
+      word_delta_count &= 0x7FFF;
+
+      /* check some data consistency */
+      if ( word_delta_count > region_idx_count )
+      {
+        FT_TRACE2(( "bad short count %d or region count %d\n",
+                    word_delta_count,
+                    region_idx_count ));
+        error = FT_THROW( Invalid_Table );
+        goto Exit;
+      }
+
+      if ( region_idx_count > itemStore->regionCount )
+      {
+        FT_TRACE2(( "inconsistent regionCount %d in varData[%d]\n",
+                    region_idx_count,
+                    i ));
+        error = FT_THROW( Invalid_Table );
+        goto Exit;
+      }
+
+      /* parse region indices */
+      if ( FT_NEW_ARRAY( varData->regionIndices, region_idx_count ) )
+        goto Exit;
+      varData->regionIdxCount = region_idx_count;
+      varData->wordDeltaCount = word_delta_count;
+      varData->longWords      = long_words;
+
+      for ( j = 0; j < varData->regionIdxCount; j++ )
+      {
+        if ( FT_READ_USHORT( varData->regionIndices[j] ) )
+          goto Exit;
+
+        if ( varData->regionIndices[j] >= itemStore->regionCount )
+        {
+          FT_TRACE2(( "bad region index %d\n",
+                      varData->regionIndices[j] ));
+          error = FT_THROW( Invalid_Table );
+          goto Exit;
+        }
+      }
+
+      per_region_size = word_delta_count + region_idx_count;
+      if ( long_words )
+        per_region_size *= 2;
+
+      if ( FT_NEW_ARRAY( varData->deltaSet, per_region_size * item_count ) )
+        goto Exit;
+      if ( FT_Stream_Read( stream,
+                           varData->deltaSet,
+                           per_region_size * item_count ) )
+      {
+        FT_TRACE2(( "deltaSet read failed." ));
+        error = FT_THROW( Invalid_Table );
+        goto Exit;
+      }
+
+      varData->itemCount = item_count;
+    }
+
+  Exit:
+    FT_FREE( dataOffsetArray );
+
+    return error;
+  }
+
+
+  FT_LOCAL_DEF( FT_Error )
+  tt_var_load_delta_set_index_mapping( FT_Face            face, /* TT_Face */
+                                       FT_ULong           offset,
+                                       GX_DeltaSetIdxMap  map,
+                                       GX_ItemVarStore    itemStore,
+                                       FT_ULong           table_len )
+  {
+    FT_Stream  stream = FT_FACE_STREAM( face );
+    FT_Memory  memory = stream->memory;
+
+    FT_Error  error;
+
+    FT_Byte   format;
+    FT_Byte   entryFormat;
+    FT_UInt   entrySize;
+    FT_UInt   innerBitCount;
+    FT_UInt   innerIndexMask;
+    FT_ULong  i;
+    FT_UInt   j;
+
+
+    if ( FT_STREAM_SEEK( offset )    ||
+         FT_READ_BYTE( format )      ||
+         FT_READ_BYTE( entryFormat ) )
+      goto Exit;
+
+    if ( format == 0 )
+    {
+      if ( FT_READ_USHORT( map->mapCount ) )
+        goto Exit;
+    }
+    else if ( format == 1 ) /* new in OpenType 1.9 */
+    {
+      if ( FT_READ_ULONG( map->mapCount ) )
+        goto Exit;
+    }
+    else
+    {
+      FT_TRACE2(( "bad map format %d\n", format ));
+      error = FT_THROW( Invalid_Table );
+      goto Exit;
+    }
+
+    if ( entryFormat & 0xC0 )
+    {
+      FT_TRACE2(( "bad entry format %d\n", format ));
+      error = FT_THROW( Invalid_Table );
+      goto Exit;
+    }
+
+    /* bytes per entry: 1, 2, 3, or 4 */
+    entrySize      = ( ( entryFormat & 0x30 ) >> 4 ) + 1;
+    innerBitCount  = ( entryFormat & 0x0F ) + 1;
+    innerIndexMask = ( 1 << innerBitCount ) - 1;
+
+    /* rough sanity check */
+    if ( map->mapCount * entrySize > table_len )
+    {
+      FT_TRACE1(( "tt_var_load_delta_set_index_mapping:"
+                  " invalid number of delta-set index mappings\n" ));
+      error = FT_THROW( Invalid_Table );
+      goto Exit;
+    }
+
+    if ( FT_NEW_ARRAY( map->innerIndex, map->mapCount ) )
+      goto Exit;
+
+    if ( FT_NEW_ARRAY( map->outerIndex, map->mapCount ) )
+      goto Exit;
+
+    for ( i = 0; i < map->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;
+      }
+
+      /* new in OpenType 1.8.4 */
+      if ( mapData == 0xFFFFFFFFUL )
+      {
+        /* no variation data for this item */
+        map->outerIndex[i] = 0xFFFFU;
+        map->innerIndex[i] = 0xFFFFU;
+
+        continue;
+      }
+
+      outerIndex = mapData >> innerBitCount;
+
+      if ( outerIndex >= itemStore->dataCount )
+      {
+        FT_TRACE2(( "outerIndex[%ld] == %d out of range\n",
+                    i,
+                    outerIndex ));
+        error = FT_THROW( Invalid_Table );
+        goto Exit;
+      }
+
+      map->outerIndex[i] = outerIndex;
+
+      innerIndex = mapData & innerIndexMask;
+
+      if ( innerIndex >= itemStore->varData[outerIndex].itemCount )
+      {
+        FT_TRACE2(( "innerIndex[%ld] == %d out of range\n",
+                    i,
+                    innerIndex ));
+        error = FT_THROW( Invalid_Table );
+          goto Exit;
+      }
+
+      map->innerIndex[i] = innerIndex;
+    }
+
+  Exit:
+    return error;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   ft_var_load_hvvar
+   *
+   * @Description:
+   *   If `vertical' is zero, parse the `HVAR' table and set
+   *   `blend->hvar_loaded' to TRUE.  On success, `blend->hvar_checked'
+   *   is set to TRUE.
+   *
+   *   If `vertical' is not zero, parse the `VVAR' table and set
+   *   `blend->vvar_loaded' to TRUE.  On success, `blend->vvar_checked'
+   *   is set to TRUE.
+   *
+   *   Some memory may remain allocated on error; it is always freed in
+   *   `tt_done_blend', however.
+   *
+   * @InOut:
+   *   face ::
+   *     The font face.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  static FT_Error
+  ft_var_load_hvvar( TT_Face  face,
+                     FT_Bool  vertical )
+  {
+    FT_Stream  stream = FT_FACE_STREAM( face );
+    FT_Memory  memory = stream->memory;
+
+    GX_Blend  blend = face->blend;
+
+    GX_HVVarTable  table;
+
+    FT_Error   error;
+    FT_UShort  majorVersion;
+    FT_ULong   table_len;
+    FT_ULong   table_offset;
+    FT_ULong   store_offset;
+    FT_ULong   widthMap_offset;
+
+
+    if ( vertical )
+    {
+      blend->vvar_loaded = TRUE;
+
+      FT_TRACE2(( "VVAR " ));
+
+      error = face->goto_table( face, TTAG_VVAR, stream, &table_len );
+    }
+    else
+    {
+      blend->hvar_loaded = TRUE;
+
+      FT_TRACE2(( "HVAR " ));
+
+      error = face->goto_table( face, TTAG_HVAR, stream, &table_len );
+    }
+
+    if ( error )
+    {
+      FT_TRACE2(( "is missing\n" ));
+      goto Exit;
+    }
+
+    table_offset = FT_STREAM_POS();
+
+    /* skip minor version */
+    if ( FT_READ_USHORT( majorVersion ) ||
+         FT_STREAM_SKIP( 2 )            )
+      goto Exit;
+
+    if ( majorVersion != 1 )
+    {
+      FT_TRACE2(( "bad table version %d\n", majorVersion ));
+      error = FT_THROW( Invalid_Table );
+      goto Exit;
+    }
+
+    if ( FT_READ_ULONG( store_offset )    ||
+         FT_READ_ULONG( widthMap_offset ) )
+      goto Exit;
+
+    if ( vertical )
+    {
+      if ( FT_NEW( blend->vvar_table ) )
+        goto Exit;
+      table = blend->vvar_table;
+    }
+    else
+    {
+      if ( FT_NEW( blend->hvar_table ) )
+        goto Exit;
+      table = blend->hvar_table;
+    }
+
+    error = tt_var_load_item_variation_store(
+              FT_FACE( face ),
+              table_offset + store_offset,
+              &table->itemStore );
+    if ( error )
+      goto Exit;
+
+    if ( widthMap_offset )
+    {
+      error = tt_var_load_delta_set_index_mapping(
+                FT_FACE( face ),
+                table_offset + widthMap_offset,
+                &table->widthMap,
+                &table->itemStore,
+                table_len );
+      if ( error )
+        goto Exit;
+    }
+
+    FT_TRACE2(( "loaded\n" ));
+    error = FT_Err_Ok;
+
+  Exit:
+    if ( !error )
+    {
+      if ( vertical )
+      {
+        blend->vvar_checked = TRUE;
+
+        /* FreeType doesn't provide functions to quickly retrieve    */
+        /* TSB, BSB, or VORG values; we thus don't have to implement */
+        /* support for those three item variation stores.            */
+
+        face->variation_support |= TT_FACE_FLAG_VAR_VADVANCE;
+      }
+      else
+      {
+        blend->hvar_checked = TRUE;
+
+        /* FreeType doesn't provide functions to quickly retrieve */
+        /* LSB or RSB values; we thus don't have to implement     */
+        /* support for those two item variation stores.           */
+
+        face->variation_support |= TT_FACE_FLAG_VAR_HADVANCE;
+      }
+    }
+
+    return error;
+  }
+
+
+  FT_LOCAL_DEF( FT_ItemVarDelta )
+  tt_var_get_item_delta( FT_Face          face,        /* TT_Face */
+                         GX_ItemVarStore  itemStore,
+                         FT_UInt          outerIndex,
+                         FT_UInt          innerIndex )
+  {
+    TT_Face    ttface = (TT_Face)face;
+    FT_Stream  stream = FT_FACE_STREAM( face );
+    FT_Memory  memory = stream->memory;
+    FT_Error   error  = FT_Err_Ok;
+
+    GX_ItemVarData    varData;
+    FT_ItemVarDelta*  deltaSet = NULL;
+    FT_ItemVarDelta   deltaSetStack[16];
+
+    FT_Fixed*  scalars = NULL;
+    FT_Fixed   scalarsStack[16];
+
+    FT_UInt          master, j;
+    FT_ItemVarDelta  returnValue = 0;
+    FT_UInt          per_region_size;
+    FT_Byte*         bytes;
+
+
+    if ( !ttface->blend || !ttface->blend->normalizedcoords )
+      return 0;
+
+    /* OpenType 1.8.4+: No variation data for this item */
+    /* as indices have special value 0xFFFF.            */
+    if ( outerIndex == 0xFFFF && innerIndex == 0xFFFF )
+      return 0;
+
+    /* See pseudo code from `Font Variations Overview' */
+    /* in the OpenType specification.                  */
+
+    if ( outerIndex >= itemStore->dataCount )
+      return 0; /* Out of range. */
+
+    varData = &itemStore->varData[outerIndex];
+
+    if ( innerIndex >= varData->itemCount )
+      return 0; /* Out of range. */
+
+    if ( varData->regionIdxCount < 16 )
+    {
+      deltaSet = deltaSetStack;
+      scalars  = scalarsStack;
+    }
+    else
+    {
+      if ( FT_QNEW_ARRAY( deltaSet, varData->regionIdxCount ) )
+        goto Exit;
+      if ( FT_QNEW_ARRAY( scalars, varData->regionIdxCount ) )
+        goto Exit;
+    }
+
+    /* Parse delta set.                                            */
+    /*                                                             */
+    /* Deltas are (word_delta_count + region_idx_count) bytes each */
+    /* if `longWords` isn't set, and twice as much otherwise.      */
+    per_region_size = varData->wordDeltaCount + varData->regionIdxCount;
+    if ( varData->longWords )
+      per_region_size *= 2;
+
+    bytes = varData->deltaSet + per_region_size * innerIndex;
+
+    if ( varData->longWords )
+    {
+      for ( master = 0; master < varData->wordDeltaCount; master++ )
+        deltaSet[master] = FT_NEXT_LONG( bytes );
+      for ( ; master < varData->regionIdxCount; master++ )
+        deltaSet[master] = FT_NEXT_SHORT( bytes );
+    }
+    else
+    {
+      for ( master = 0; master < varData->wordDeltaCount; master++ )
+        deltaSet[master] = FT_NEXT_SHORT( bytes );
+      for ( ; master < varData->regionIdxCount; master++ )
+        deltaSet[master] = FT_NEXT_CHAR( bytes );
+    }
+
+    /* outer loop steps through master designs to be blended */
+    for ( master = 0; master < varData->regionIdxCount; master++ )
+    {
+      FT_Fixed  scalar      = 0x10000L;
+      FT_UInt   regionIndex = varData->regionIndices[master];
+
+      GX_AxisCoords  axis = itemStore->varRegionList[regionIndex].axisList;
+
+
+      /* inner loop steps through axes in this region */
+      for ( j = 0; j < itemStore->axisCount; j++, axis++ )
+      {
+        /* compute the scalar contribution of this axis; */
+        /* ignore invalid ranges                         */
+        if ( axis->startCoord > axis->peakCoord ||
+             axis->peakCoord > axis->endCoord   )
+          continue;
+
+        else if ( axis->startCoord < 0 &&
+                  axis->endCoord > 0   &&
+                  axis->peakCoord != 0 )
+          continue;
+
+        /* peak of 0 means ignore this axis */
+        else if ( axis->peakCoord == 0 )
+          continue;
+
+        else if ( ttface->blend->normalizedcoords[j] == axis->peakCoord )
+          continue;
+
+        /* ignore this region if coords are out of range */
+        else if ( ttface->blend->normalizedcoords[j] <= axis->startCoord ||
+                  ttface->blend->normalizedcoords[j] >= axis->endCoord   )
+        {
+          scalar = 0;
+          break;
+        }
+
+        /* cumulative product of all the axis scalars */
+        else if ( ttface->blend->normalizedcoords[j] < axis->peakCoord )
+          scalar =
+            FT_MulDiv( scalar,
+                       ttface->blend->normalizedcoords[j] - axis->startCoord,
+                       axis->peakCoord - axis->startCoord );
+        else
+          scalar =
+            FT_MulDiv( scalar,
+                       axis->endCoord - ttface->blend->normalizedcoords[j],
+                       axis->endCoord - axis->peakCoord );
+
+      } /* per-axis loop */
+
+      scalars[master] = scalar;
+
+    } /* per-region loop */
+
+
+    /* Compute the scaled delta for this region.
+     *
+     * From: 
https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#item-variation-store-header-and-item-variation-data-subtables:
+     *
+     *   `Fixed` is a 32-bit (16.16) type and, in the general case, requires
+     *   32-bit deltas.  As described above, the `DeltaSet` record can
+     *   accommodate deltas that are, logically, either 16-bit or 32-bit.
+     *   When scaled deltas are applied to `Fixed` values, the `Fixed` value
+     *   is treated like a 32-bit integer.
+     *
+     * `FT_MulAddFix` internally uses 64-bit precision; it thus can handle
+     * deltas ranging from small 8-bit to large 32-bit values that are
+     * applied to 16.16 `FT_Fixed` / OpenType `Fixed` values.
+     */
+    returnValue = FT_MulAddFix( scalars, deltaSet, varData->regionIdxCount );
+
+  Exit:
+    if ( scalars != scalarsStack )
+      FT_FREE( scalars );
+    if ( deltaSet != deltaSetStack )
+      FT_FREE( deltaSet );
+
+    return returnValue;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_hvadvance_adjust
+   *
+   * @Description:
+   *   Apply `HVAR' advance width or `VVAR' advance height adjustment of
+   *   a given glyph.
+   *
+   * @Input:
+   *   gindex ::
+   *     The glyph index.
+   *
+   *   vertical ::
+   *     If set, handle `VVAR' table.
+   *
+   * @InOut:
+   *   face ::
+   *     The font face.
+   *
+   *   adelta ::
+   *     Points to width or height value that gets modified.
+   */
+  static FT_Error
+  tt_hvadvance_adjust( TT_Face  face,
+                       FT_UInt  gindex,
+                       FT_Int  *avalue,
+                       FT_Bool  vertical )
+  {
+    FT_Error  error = FT_Err_Ok;
+    FT_UInt   innerIndex, outerIndex;
+    FT_Int    delta;
+
+    GX_HVVarTable  table;
+
+
+    if ( !face->doblend || !face->blend )
+      goto Exit;
+
+    if ( vertical )
+    {
+      if ( !face->blend->vvar_loaded )
+      {
+        /* initialize vvar table */
+        face->blend->vvar_error = ft_var_load_hvvar( face, 1 );
+      }
+
+      if ( !face->blend->vvar_checked )
+      {
+        error = face->blend->vvar_error;
+        goto Exit;
+      }
+
+      table = face->blend->vvar_table;
+    }
+    else
+    {
+      if ( !face->blend->hvar_loaded )
+      {
+        /* initialize hvar table */
+        face->blend->hvar_error = ft_var_load_hvvar( face, 0 );
+      }
+
+      if ( !face->blend->hvar_checked )
+      {
+        error = face->blend->hvar_error;
+        goto Exit;
+      }
+
+      table = face->blend->hvar_table;
+    }
+
+    /* advance width or height adjustments are always present in an */
+    /* `HVAR' or `VVAR' table; no need to test for this capability  */
+
+    if ( table->widthMap.innerIndex )
+    {
+      FT_UInt  idx = gindex;
+
+
+      if ( idx >= table->widthMap.mapCount )
+        idx = table->widthMap.mapCount - 1;
+
+      /* trust that HVAR parser has checked indices */
+      outerIndex = table->widthMap.outerIndex[idx];
+      innerIndex = table->widthMap.innerIndex[idx];
+    }
+    else
+    {
+      /* no widthMap data */
+      outerIndex = 0;
+      innerIndex = gindex;
+    }
+
+    delta = tt_var_get_item_delta( FT_FACE( face ),
+                                   &table->itemStore,
+                                   outerIndex,
+                                   innerIndex );
+
+    if ( delta )
+    {
+      FT_TRACE5(( "%s value %d adjusted by %d unit%s (%s)\n",
+                  vertical ? "vertical height" : "horizontal width",
+                  *avalue,
+                  delta,
+                  delta == 1 ? "" : "s",
+                  vertical ? "VVAR" : "HVAR" ));
+
+      *avalue = ADD_INT( *avalue, delta );
+    }
+
+  Exit:
+    return error;
+  }
+
+
+  FT_LOCAL_DEF( FT_Error )
+  tt_hadvance_adjust( FT_Face  face,    /* TT_Face */
+                      FT_UInt  gindex,
+                      FT_Int  *avalue )
+  {
+    return tt_hvadvance_adjust( (TT_Face)face, gindex, avalue, 0 );
+  }
+
+
+  FT_LOCAL_DEF( FT_Error )
+  tt_vadvance_adjust( FT_Face  face,    /* TT_Face */
+                      FT_UInt  gindex,
+                      FT_Int  *avalue )
+  {
+    return tt_hvadvance_adjust( (TT_Face)face, gindex, avalue, 1 );
+  }
+
+
+#define GX_VALUE_SIZE  8
+
+  /* all values are FT_Short or FT_UShort entities; */
+  /* we treat them consistently as FT_Short         */
+#define GX_VALUE_CASE( tag, dflt )      \
+          case MVAR_TAG_ ## tag :       \
+            p = (FT_Short*)&face->dflt; \
+            break
+
+#define GX_GASP_CASE( idx )                                       \
+          case MVAR_TAG_GASP_ ## idx :                            \
+            if ( idx < face->gasp.numRanges - 1 )                 \
+              p = (FT_Short*)&face->gasp.gaspRanges[idx].maxPPEM; \
+            else                                                  \
+              p = NULL;                                           \
+            break
+
+
+  static FT_Short*
+  ft_var_get_value_pointer( TT_Face   face,
+                            FT_ULong  mvar_tag )
+  {
+    FT_Short*  p;
+
+
+    switch ( mvar_tag )
+    {
+      GX_GASP_CASE( 0 );
+      GX_GASP_CASE( 1 );
+      GX_GASP_CASE( 2 );
+      GX_GASP_CASE( 3 );
+      GX_GASP_CASE( 4 );
+      GX_GASP_CASE( 5 );
+      GX_GASP_CASE( 6 );
+      GX_GASP_CASE( 7 );
+      GX_GASP_CASE( 8 );
+      GX_GASP_CASE( 9 );
+
+      GX_VALUE_CASE( CPHT, os2.sCapHeight );
+      GX_VALUE_CASE( HASC, os2.sTypoAscender );
+      GX_VALUE_CASE( HCLA, os2.usWinAscent );
+      GX_VALUE_CASE( HCLD, os2.usWinDescent );
+      GX_VALUE_CASE( HCOF, horizontal.caret_Offset );
+      GX_VALUE_CASE( HCRN, horizontal.caret_Slope_Run );
+      GX_VALUE_CASE( HCRS, horizontal.caret_Slope_Rise );
+      GX_VALUE_CASE( HDSC, os2.sTypoDescender );
+      GX_VALUE_CASE( HLGP, os2.sTypoLineGap );
+      GX_VALUE_CASE( SBXO, os2.ySubscriptXOffset);
+      GX_VALUE_CASE( SBXS, os2.ySubscriptXSize );
+      GX_VALUE_CASE( SBYO, os2.ySubscriptYOffset );
+      GX_VALUE_CASE( SBYS, os2.ySubscriptYSize );
+      GX_VALUE_CASE( SPXO, os2.ySuperscriptXOffset );
+      GX_VALUE_CASE( SPXS, os2.ySuperscriptXSize );
+      GX_VALUE_CASE( SPYO, os2.ySuperscriptYOffset );
+      GX_VALUE_CASE( SPYS, os2.ySuperscriptYSize );
+      GX_VALUE_CASE( STRO, os2.yStrikeoutPosition );
+      GX_VALUE_CASE( STRS, os2.yStrikeoutSize );
+      GX_VALUE_CASE( UNDO, postscript.underlinePosition );
+      GX_VALUE_CASE( UNDS, postscript.underlineThickness );
+      GX_VALUE_CASE( VASC, vertical.Ascender );
+      GX_VALUE_CASE( VCOF, vertical.caret_Offset );
+      GX_VALUE_CASE( VCRN, vertical.caret_Slope_Run );
+      GX_VALUE_CASE( VCRS, vertical.caret_Slope_Rise );
+      GX_VALUE_CASE( VDSC, vertical.Descender );
+      GX_VALUE_CASE( VLGP, vertical.Line_Gap );
+      GX_VALUE_CASE( XHGT, os2.sxHeight );
+
+    default:
+      /* ignore unknown tag */
+      p = NULL;
+    }
+
+    return p;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   ft_var_load_mvar
+   *
+   * @Description:
+   *   Parse the `MVAR' table.
+   *
+   *   Some memory may remain allocated on error; it is always freed in
+   *   `tt_done_blend', however.
+   *
+   * @InOut:
+   *   face ::
+   *     The font face.
+   */
+  static void
+  ft_var_load_mvar( TT_Face  face )
+  {
+    FT_Stream  stream = FT_FACE_STREAM( face );
+    FT_Memory  memory = stream->memory;
+
+    GX_Blend         blend = face->blend;
+    GX_ItemVarStore  itemStore;
+    GX_Value         value, limit;
+
+    FT_Error   error;
+    FT_UShort  majorVersion;
+    FT_ULong   table_len;
+    FT_ULong   table_offset;
+    FT_UShort  store_offset;
+    FT_ULong   records_offset;
+
+
+    FT_TRACE2(( "MVAR " ));
+
+    error = face->goto_table( face, TTAG_MVAR, stream, &table_len );
+    if ( error )
+    {
+      FT_TRACE2(( "is missing\n" ));
+      return;
+    }
+
+    table_offset = FT_STREAM_POS();
+
+    /* skip minor version */
+    if ( FT_READ_USHORT( majorVersion ) ||
+         FT_STREAM_SKIP( 2 )            )
+      return;
+
+    if ( majorVersion != 1 )
+    {
+      FT_TRACE2(( "bad table version %d\n", majorVersion ));
+      return;
+    }
+
+    if ( FT_NEW( blend->mvar_table ) )
+      return;
+
+    /* skip reserved entry and value record size */
+    if ( FT_STREAM_SKIP( 4 )                             ||
+         FT_READ_USHORT( blend->mvar_table->valueCount ) ||
+         FT_READ_USHORT( store_offset )                  )
+      return;
+
+    records_offset = FT_STREAM_POS();
+
+    error = tt_var_load_item_variation_store(
+              FT_FACE( face ),
+              table_offset + store_offset,
+              &blend->mvar_table->itemStore );
+    if ( error )
+      return;
+
+    if ( FT_NEW_ARRAY( blend->mvar_table->values,
+                       blend->mvar_table->valueCount ) )
+      return;
+
+    if ( FT_STREAM_SEEK( records_offset )                                ||
+         FT_FRAME_ENTER( blend->mvar_table->valueCount * GX_VALUE_SIZE ) )
+      return;
+
+    value     = blend->mvar_table->values;
+    limit     = FT_OFFSET( value, blend->mvar_table->valueCount );
+    itemStore = &blend->mvar_table->itemStore;
+
+    for ( ; value < limit; value++ )
+    {
+      value->tag        = FT_GET_ULONG();
+      value->outerIndex = FT_GET_USHORT();
+      value->innerIndex = FT_GET_USHORT();
+
+      /* new in OpenType 1.8.4 */
+      if ( value->outerIndex == 0xFFFFU && value->innerIndex == 0xFFFFU )
+      {
+        /* no variation data for this item */
+        continue;
+      }
+
+      if ( value->outerIndex >= itemStore->dataCount                  ||
+           value->innerIndex >= itemStore->varData[value->outerIndex]
+                                                  .itemCount          )
+      {
+        error = FT_THROW( Invalid_Table );
+        break;
+      }
+    }
+
+    FT_FRAME_EXIT();
+
+    if ( error )
+      return;
+
+    FT_TRACE2(( "loaded\n" ));
+
+    value = blend->mvar_table->values;
+    limit = FT_OFFSET( value, blend->mvar_table->valueCount );
+
+    /* save original values of the data MVAR is going to modify */
+    for ( ; value < limit; value++ )
+    {
+      FT_Short*  p = ft_var_get_value_pointer( face, value->tag );
+
+
+      if ( p )
+        value->unmodified = *p;
+#ifdef FT_DEBUG_LEVEL_TRACE
+      else
+        FT_TRACE1(( "ft_var_load_mvar: Ignoring unknown tag `%c%c%c%c'\n",
+                    (FT_Char)( value->tag >> 24 ),
+                    (FT_Char)( value->tag >> 16 ),
+                    (FT_Char)( value->tag >> 8 ),
+                    (FT_Char)( value->tag ) ));
+#endif
+    }
+
+    face->variation_support |= TT_FACE_FLAG_VAR_MVAR;
+  }
+
+
+  static FT_Error
+  ft_size_reset_iterator( FT_ListNode  node,
+                          void*        user )
+  {
+    FT_Size                       size = (FT_Size)node->data;
+    FT_Service_MetricsVariations  var  = (FT_Service_MetricsVariations)user;
+
+
+    var->size_reset( size );
+
+    return FT_Err_Ok;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_apply_mvar
+   *
+   * @Description:
+   *   Apply `MVAR' table adjustments.
+   *
+   * @InOut:
+   *   face ::
+   *     The font face.
+   */
+  FT_LOCAL_DEF( void )
+  tt_apply_mvar( FT_Face  face )  /* TT_Face */
+  {
+    TT_Face  ttface = (TT_Face)face;
+
+    GX_Blend  blend = ttface->blend;
+    GX_Value  value, limit;
+
+    FT_Short  mvar_hasc_delta = 0;
+    FT_Short  mvar_hdsc_delta = 0;
+    FT_Short  mvar_hlgp_delta = 0;
+
+
+    if ( !( ttface->variation_support & TT_FACE_FLAG_VAR_MVAR ) )
+      return;
+
+    value = blend->mvar_table->values;
+    limit = FT_OFFSET( value, blend->mvar_table->valueCount );
+
+    for ( ; value < limit; value++ )
+    {
+      FT_Short*  p = ft_var_get_value_pointer( ttface, value->tag );
+      FT_Int     delta;
+
+
+      delta = tt_var_get_item_delta( face,
+                                     &blend->mvar_table->itemStore,
+                                     value->outerIndex,
+                                     value->innerIndex );
+
+      if ( p && delta )
+      {
+        FT_TRACE5(( "value %c%c%c%c (%d unit%s) adjusted by %d unit%s 
(MVAR)\n",
+                    (FT_Char)( value->tag >> 24 ),
+                    (FT_Char)( value->tag >> 16 ),
+                    (FT_Char)( value->tag >> 8 ),
+                    (FT_Char)( value->tag ),
+                    value->unmodified,
+                    value->unmodified == 1 ? "" : "s",
+                    delta,
+                    delta == 1 ? "" : "s" ));
+
+        /* since we handle both signed and unsigned values as FT_Short, */
+        /* ensure proper overflow arithmetic                            */
+        *p = (FT_Short)( value->unmodified + (FT_Short)delta );
+
+        /* Treat hasc, hdsc and hlgp specially, see below. */
+        if ( value->tag == MVAR_TAG_HASC )
+          mvar_hasc_delta = (FT_Short)delta;
+        else if ( value->tag == MVAR_TAG_HDSC )
+          mvar_hdsc_delta = (FT_Short)delta;
+        else if ( value->tag == MVAR_TAG_HLGP )
+          mvar_hlgp_delta = (FT_Short)delta;
+      }
+    }
+
+    /* adjust all derived values */
+    {
+      FT_Service_MetricsVariations  var =
+        (FT_Service_MetricsVariations)ttface->face_var;
+
+      /*
+       * Apply the deltas of hasc, hdsc and hlgp to the FT_Face's ascender,
+       * descender and height attributes, no matter how they were originally
+       * computed.
+       *
+       * (Code that ignores those and accesses the font's metrics values
+       * directly is already served by the delta application code above.)
+       *
+       * The MVAR table supports variations for both typo and win metrics.
+       * According to Behdad Esfahbod, the thinking of the working group was
+       * that no one uses win metrics anymore for setting line metrics (the
+       * specification even calls these metrics "horizontal clipping
+       * ascent/descent", probably for their role on the Windows platform in
+       * computing clipping boxes), and new fonts should use typo metrics, so
+       * typo deltas should be applied to whatever sfnt_load_face decided the
+       * line metrics should be.
+       *
+       * Before, the following led to different line metrics between default
+       * outline and instances, visible when e.g. the default outlines were
+       * used as the regular face and instances for everything else:
+       *
+       * 1. sfnt_load_face applied the hhea metrics by default.
+       * 2. This code later applied the typo metrics by default, regardless of
+       *    whether they were actually changed or the font had the OS/2 table's
+       *    fsSelection's bit 7 (USE_TYPO_METRICS) set.
+       */
+      FT_Short  current_line_gap = face->height - face->ascender +
+                                   face->descender;
+
+
+      face->ascender  = face->ascender + mvar_hasc_delta;
+      face->descender = face->descender + mvar_hdsc_delta;
+      face->height    = face->ascender - face->descender +
+                        current_line_gap + mvar_hlgp_delta;
+
+      face->underline_position  = ttface->postscript.underlinePosition -
+                                  ttface->postscript.underlineThickness / 2;
+      face->underline_thickness = ttface->postscript.underlineThickness;
+
+      /* iterate over all FT_Size objects and call `var->size_reset' */
+      /* to propagate the metrics changes                            */
+      if ( var && var->size_reset )
+        FT_List_Iterate( &face->sizes_list,
+                         ft_size_reset_iterator,
+                         (void*)var );
+    }
+  }
+
+
+  typedef struct  GX_GVar_Head_
+  {
+    FT_Long    version;
+    FT_UShort  axisCount;
+    FT_UShort  globalCoordCount;
+    FT_ULong   offsetToCoord;
+    FT_UShort  glyphCount;
+    FT_UShort  flags;
+    FT_ULong   offsetToData;
+
+  } GX_GVar_Head;
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   ft_var_load_gvar
+   *
+   * @Description:
+   *   Parse the `gvar' table if present.  If `fvar' is there, `gvar' had
+   *   better be there too.
+   *
+   * @InOut:
+   *   face ::
+   *     The font face.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  static FT_Error
+  ft_var_load_gvar( TT_Face  face )
+  {
+    FT_Stream     stream = FT_FACE_STREAM( face );
+    FT_Memory     memory = stream->memory;
+    GX_Blend      blend  = face->blend;
+    FT_Error      error;
+    FT_UInt       i, j;
+    FT_ULong      table_len;
+    FT_ULong      gvar_start;
+    FT_ULong      offsetToData;
+    FT_ULong      offsets_len;
+    GX_GVar_Head  gvar_head;
+
+    static const FT_Frame_Field  gvar_fields[] =
+    {
+
+#undef  FT_STRUCTURE
+#define FT_STRUCTURE  GX_GVar_Head
+
+      FT_FRAME_START( 20 ),
+        FT_FRAME_LONG  ( version ),
+        FT_FRAME_USHORT( axisCount ),
+        FT_FRAME_USHORT( globalCoordCount ),
+        FT_FRAME_ULONG ( offsetToCoord ),
+        FT_FRAME_USHORT( glyphCount ),
+        FT_FRAME_USHORT( flags ),
+        FT_FRAME_ULONG ( offsetToData ),
+      FT_FRAME_END
+    };
+
+
+    FT_TRACE2(( "GVAR " ));
+
+    if ( FT_SET_ERROR( face->goto_table( face,
+                                         TTAG_gvar,
+                                         stream,
+                                         &table_len ) ) )
+    {
+      FT_TRACE2(( "is missing\n" ));
+      goto Exit;
+    }
+
+    gvar_start = FT_STREAM_POS( );
+    if ( FT_STREAM_READ_FIELDS( gvar_fields, &gvar_head ) )
+      goto Exit;
+
+    if ( gvar_head.version != 0x00010000L )
+    {
+      FT_TRACE1(( "bad table version\n" ));
+      error = FT_THROW( Invalid_Table );
+      goto Exit;
+    }
+
+    if ( gvar_head.axisCount != (FT_UShort)blend->mmvar->num_axis )
+    {
+      FT_TRACE1(( "ft_var_load_gvar:"
+                  " number of axes in `gvar' and `cvar'\n" ));
+      FT_TRACE1(( "                  table are different\n" ));
+      error = FT_THROW( Invalid_Table );
+      goto Exit;
+    }
+
+    /* rough sanity check, ignoring offsets */
+    if ( (FT_ULong)gvar_head.globalCoordCount * gvar_head.axisCount >
+           table_len / 2 )
+    {
+      FT_TRACE1(( "ft_var_load_gvar:"
+                  " invalid number of global coordinates\n" ));
+      error = FT_THROW( Invalid_Table );
+      goto Exit;
+    }
+
+    /* offsets can be either 2 or 4 bytes                  */
+    /* (one more offset than glyphs, to mark size of last) */
+    offsets_len = ( gvar_head.glyphCount + 1 ) *
+                  ( ( gvar_head.flags & 1 ) ? 4L : 2L );
+
+    /* rough sanity check */
+    if (offsets_len > table_len )
+    {
+      FT_TRACE1(( "ft_var_load_gvar: invalid number of glyphs\n" ));
+      error = FT_THROW( Invalid_Table );
+      goto Exit;
+    }
+
+    FT_TRACE2(( "loaded\n" ));
+
+    blend->gvar_size = table_len;
+    offsetToData     = gvar_start + gvar_head.offsetToData;
+
+    FT_TRACE5(( "gvar: there %s %d shared coordinate%s:\n",
+                gvar_head.globalCoordCount == 1 ? "is" : "are",
+                gvar_head.globalCoordCount,
+                gvar_head.globalCoordCount == 1 ? "" : "s" ));
+
+    if ( FT_FRAME_ENTER( offsets_len ) )
+      goto Exit;
+
+    /* offsets (one more offset than glyphs, to mark size of last) */
+    if ( FT_QNEW_ARRAY( blend->glyphoffsets, gvar_head.glyphCount + 1 ) )
+      goto Fail2;
+
+    if ( gvar_head.flags & 1 )
+    {
+      FT_ULong  limit      = gvar_start + table_len;
+      FT_ULong  max_offset = 0;
+
+
+      for ( i = 0; i <= gvar_head.glyphCount; i++ )
+      {
+        blend->glyphoffsets[i] = offsetToData + FT_GET_ULONG();
+
+        if ( max_offset <= blend->glyphoffsets[i] )
+          max_offset = blend->glyphoffsets[i];
+        else
+        {
+          FT_TRACE2(( "ft_var_load_gvar:"
+                      " glyph variation data offset %d not monotonic\n",
+                      i ));
+          blend->glyphoffsets[i] = max_offset;
+        }
+
+        /* use `<', not `<=' */
+        if ( limit < blend->glyphoffsets[i] )
+        {
+          FT_TRACE2(( "ft_var_load_gvar:"
+                      " glyph variation data offset %d out of range\n",
+                      i ));
+          blend->glyphoffsets[i] = limit;
+        }
+      }
+    }
+    else
+    {
+      FT_ULong  limit      = gvar_start + table_len;
+      FT_ULong  max_offset = 0;
+
+
+      for ( i = 0; i <= gvar_head.glyphCount; i++ )
+      {
+        blend->glyphoffsets[i] = offsetToData + FT_GET_USHORT() * 2;
+
+        if ( max_offset <= blend->glyphoffsets[i] )
+          max_offset = blend->glyphoffsets[i];
+        else
+        {
+          FT_TRACE2(( "ft_var_load_gvar:"
+                      " glyph variation data offset %d not monotonic\n",
+                      i ));
+          blend->glyphoffsets[i] = max_offset;
+        }
+
+        /* use `<', not `<=' */
+        if ( limit < blend->glyphoffsets[i] )
+        {
+          FT_TRACE2(( "ft_var_load_gvar:"
+                      " glyph variation data offset %d out of range\n",
+                      i ));
+          blend->glyphoffsets[i] = limit;
+        }
+      }
+    }
+
+    blend->gv_glyphcnt = gvar_head.glyphCount;
+
+    FT_FRAME_EXIT();
+
+    if ( gvar_head.globalCoordCount != 0 )
+    {
+      if ( FT_STREAM_SEEK( gvar_start + gvar_head.offsetToCoord ) ||
+           FT_FRAME_ENTER( gvar_head.globalCoordCount *
+                           gvar_head.axisCount * 2L )             )
+      {
+        FT_TRACE2(( "ft_var_load_gvar:"
+                    " glyph variation shared tuples missing\n" ));
+        goto Fail;
+      }
+
+      if ( FT_QNEW_ARRAY( blend->tuplecoords,
+                          gvar_head.axisCount * gvar_head.globalCoordCount ) )
+        goto Fail2;
+
+      for ( i = 0; i < gvar_head.globalCoordCount; i++ )
+      {
+        FT_TRACE5(( "  [ " ));
+        for ( j = 0; j < (FT_UInt)gvar_head.axisCount; j++ )
+        {
+          blend->tuplecoords[i * gvar_head.axisCount + j] =
+            FT_fdot14ToFixed( FT_GET_SHORT() );
+          FT_TRACE5(( "%.5f ",
+            (double)blend->tuplecoords[i * gvar_head.axisCount + j] / 65536 ));
+        }
+        FT_TRACE5(( "]\n" ));
+      }
+
+      blend->tuplecount = gvar_head.globalCoordCount;
+
+      FT_TRACE5(( "\n" ));
+
+      FT_FRAME_EXIT();
+    }
+
+  Exit:
+    return error;
+
+  Fail2:
+    FT_FRAME_EXIT();
+
+  Fail:
+    FT_FREE( blend->glyphoffsets );
+    blend->gv_glyphcnt = 0;
+    goto Exit;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   ft_var_apply_tuple
+   *
+   * @Description:
+   *   Figure out whether a given tuple (design) applies to the current
+   *   blend, and if so, what is the scaling factor.
+   *
+   * @Input:
+   *   blend ::
+   *     The current blend of the font.
+   *
+   *   tupleIndex ::
+   *     A flag saying whether this is an intermediate
+   *     tuple or not.
+   *
+   *   tuple_coords ::
+   *     The coordinates of the tuple in normalized axis
+   *     units.
+   *
+   *   im_start_coords ::
+   *     The initial coordinates where this tuple starts
+   *     to apply (for intermediate coordinates).
+   *
+   *   im_end_coords ::
+   *     The final coordinates after which this tuple no
+   *     longer applies (for intermediate coordinates).
+   *
+   * @Return:
+   *   An FT_Fixed value containing the scaling factor.
+   */
+  static FT_Fixed
+  ft_var_apply_tuple( GX_Blend   blend,
+                      FT_UShort  tupleIndex,
+                      FT_Fixed*  tuple_coords,
+                      FT_Fixed*  im_start_coords,
+                      FT_Fixed*  im_end_coords )
+  {
+    FT_UInt   i;
+    FT_Fixed  apply = 0x10000L;
+
+
+    for ( i = 0; i < blend->num_axis; i++ )
+    {
+      FT_TRACE6(( "    axis %d coordinate %.5f:\n",
+                  i, (double)blend->normalizedcoords[i] / 65536 ));
+
+      /* It's not clear why (for intermediate tuples) we don't need     */
+      /* to check against start/end -- the documentation says we don't. */
+      /* Similarly, it's unclear why we don't need to scale along the   */
+      /* axis.                                                          */
+
+      if ( tuple_coords[i] == 0 )
+      {
+        FT_TRACE6(( "      tuple coordinate is zero, ignore\n" ));
+        continue;
+      }
+
+      if ( blend->normalizedcoords[i] == 0 )
+      {
+        FT_TRACE6(( "      axis coordinate is zero, stop\n" ));
+        apply = 0;
+        break;
+      }
+
+      if ( blend->normalizedcoords[i] == tuple_coords[i] )
+      {
+        FT_TRACE6(( "      tuple coordinate %.5f fits perfectly\n",
+                    (double)tuple_coords[i] / 65536 ));
+        /* `apply' does not change */
+        continue;
+      }
+
+      if ( !( tupleIndex & GX_TI_INTERMEDIATE_TUPLE ) )
+      {
+        /* not an intermediate tuple */
+
+        if ( blend->normalizedcoords[i] < FT_MIN( 0, tuple_coords[i] ) ||
+             blend->normalizedcoords[i] > FT_MAX( 0, tuple_coords[i] ) )
+        {
+          FT_TRACE6(( "      tuple coordinate %.5f is exceeded, stop\n",
+                      (double)tuple_coords[i] / 65536 ));
+          apply = 0;
+          break;
+        }
+
+        FT_TRACE6(( "      tuple coordinate %.5f fits\n",
+                    (double)tuple_coords[i] / 65536 ));
+        apply = FT_MulDiv( apply,
+                           blend->normalizedcoords[i],
+                           tuple_coords[i] );
+      }
+      else
+      {
+        /* intermediate tuple */
+
+        if ( blend->normalizedcoords[i] <= im_start_coords[i] ||
+             blend->normalizedcoords[i] >= im_end_coords[i]   )
+        {
+          FT_TRACE6(( "      intermediate tuple range ]%.5f;%.5f[ is exceeded,"
+                      " stop\n",
+                      (double)im_start_coords[i] / 65536,
+                      (double)im_end_coords[i] / 65536 ));
+          apply = 0;
+          break;
+        }
+
+        FT_TRACE6(( "      intermediate tuple range ]%.5f;%.5f[ fits\n",
+                    (double)im_start_coords[i] / 65536,
+                    (double)im_end_coords[i] / 65536 ));
+        if ( blend->normalizedcoords[i] < tuple_coords[i] )
+          apply = FT_MulDiv( apply,
+                             blend->normalizedcoords[i] - im_start_coords[i],
+                             tuple_coords[i] - im_start_coords[i] );
+        else
+          apply = FT_MulDiv( apply,
+                             im_end_coords[i] - blend->normalizedcoords[i],
+                             im_end_coords[i] - tuple_coords[i] );
+      }
+    }
+
+    FT_TRACE6(( "    apply factor is %.5f\n", (double)apply / 65536 ));
+
+    return apply;
+  }
+
+
+  /* convert from design coordinates to normalized coordinates */
+
+  static void
+  ft_var_to_normalized( TT_Face    face,
+                        FT_UInt    num_coords,
+                        FT_Fixed*  coords,
+                        FT_Fixed*  normalized )
+  {
+    FT_Error   error  = FT_Err_Ok;
+    FT_Memory  memory = face->root.memory;
+    FT_UInt    i, j;
+
+    GX_Blend        blend;
+    FT_MM_Var*      mmvar;
+    FT_Var_Axis*    a;
+    GX_AVarSegment  av;
+
+    FT_Fixed*  new_normalized = NULL;
+    FT_Fixed*  old_normalized;
+
+
+    blend = face->blend;
+    mmvar = blend->mmvar;
+
+    if ( num_coords > mmvar->num_axis )
+    {
+      FT_TRACE2(( "ft_var_to_normalized:"
+                  " only using first %d of %d coordinates\n",
+                  mmvar->num_axis, num_coords ));
+      num_coords = mmvar->num_axis;
+    }
+
+    /* Axis normalization is a two-stage process.  First we normalize */
+    /* based on the [min,def,max] values for the axis to be [-1,0,1]. */
+    /* Then, if there's an `avar' table, we renormalize this range.   */
+
+    a = mmvar->axis;
+    for ( i = 0; i < num_coords; i++, a++ )
+    {
+      FT_Fixed  coord = coords[i];
+
+
+      FT_TRACE5(( "    %d: %.5f\n", i, (double)coord / 65536 ));
+      if ( coord > a->maximum || coord < a->minimum )
+      {
+        FT_TRACE1(( "ft_var_to_normalized: design coordinate %.5f\n",
+                    (double)coord / 65536 ));
+        FT_TRACE1(( "                      is out of range [%.5f;%.5f];"
+                    " clamping\n",
+                    (double)a->minimum / 65536,
+                    (double)a->maximum / 65536 ));
+      }
+
+      if ( coord > a->def )
+        normalized[i] = coord >= a->maximum ?  0x10000L :
+                        FT_DivFix( SUB_LONG( coord, a->def ),
+                                   SUB_LONG( a->maximum, a->def ) );
+      else if ( coord < a->def )
+        normalized[i] = coord <= a->minimum ? -0x10000L :
+                        FT_DivFix( SUB_LONG( coord, a->def ),
+                                   SUB_LONG( a->def, a->minimum ) );
+      else
+        normalized[i] = 0;
+    }
+
+    FT_TRACE5(( "\n" ));
+
+    for ( ; i < mmvar->num_axis; i++ )
+      normalized[i] = 0;
+
+    if ( blend->avar_table )
+    {
+      GX_AVarTable  table = blend->avar_table;
+
+
+      FT_TRACE5(( "normalized design coordinates"
+                  " before applying `avar' data:\n" ));
+
+      if ( table->avar_segment )
+      {
+        av = table->avar_segment;
+
+        for ( i = 0; i < mmvar->num_axis; i++, av++ )
+        {
+          for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
+          {
+            if ( normalized[i] < av->correspondence[j].fromCoord )
+            {
+              FT_TRACE5(( "  %.5f\n", (double)normalized[i] / 65536 ));
+
+              normalized[i] =
+                FT_MulDiv( normalized[i] - av->correspondence[j - 1].fromCoord,
+                           av->correspondence[j].toCoord -
+                             av->correspondence[j - 1].toCoord,
+                           av->correspondence[j].fromCoord -
+                             av->correspondence[j - 1].fromCoord ) +
+                av->correspondence[j - 1].toCoord;
+              break;
+            }
+          }
+        }
+      }
+
+      if ( table->itemStore.varData )
+      {
+        if ( FT_QNEW_ARRAY( new_normalized, mmvar->num_axis ) )
+          return;
+
+        /* Install our half-normalized coordinates for the next */
+        /* Item Variation Store to work with.                   */
+        old_normalized                = face->blend->normalizedcoords;
+        face->blend->normalizedcoords = normalized;
+
+        for ( i = 0; i < mmvar->num_axis; i++ )
+        {
+          FT_Fixed  v          = normalized[i];
+          FT_UInt   innerIndex = i;
+          FT_UInt   outerIndex = 0;
+          FT_Int    delta;
+
+
+          if ( table->axisMap.innerIndex )
+          {
+            FT_UInt  idx = i;
+
+
+            if ( idx >= table->axisMap.mapCount )
+              idx = table->axisMap.mapCount - 1;
+
+            outerIndex = table->axisMap.outerIndex[idx];
+            innerIndex = table->axisMap.innerIndex[idx];
+          }
+
+          delta = tt_var_get_item_delta( FT_FACE( face ),
+                                         &table->itemStore,
+                                         outerIndex,
+                                         innerIndex );
+
+         v += delta << 2;
+
+         /* Clamp value range. */
+         v = v >=  0x10000L ?  0x10000 : v;
+         v = v <= -0x10000L ? -0x10000 : v;
+
+          new_normalized[i] = v;
+        }
+
+        for ( i = 0; i < mmvar->num_axis; i++ )
+        {
+          normalized[i] = new_normalized[i];
+        }
+
+        face->blend->normalizedcoords = old_normalized;
+
+        FT_FREE( new_normalized );
+      }
+    }
+  }
+
+
+  /* convert from normalized coordinates to design coordinates */
+
+  static void
+  ft_var_to_design( TT_Face    face,
+                    FT_UInt    num_coords,
+                    FT_Fixed*  coords,
+                    FT_Fixed*  design )
+  {
+    GX_Blend      blend;
+    FT_MM_Var*    mmvar;
+    FT_Var_Axis*  a;
+
+    FT_UInt  i, j, nc;
+
+
+    blend = face->blend;
+
+    nc = num_coords;
+    if ( num_coords > blend->num_axis )
+    {
+      FT_TRACE2(( "ft_var_to_design:"
+                  " only using first %d of %d coordinates\n",
+                  blend->num_axis, num_coords ));
+      nc = blend->num_axis;
+    }
+
+    for ( i = 0; i < nc; i++ )
+      design[i] = coords[i];
+
+    for ( ; i < num_coords; i++ )
+      design[i] = 0;
+
+    if ( blend->avar_table && blend->avar_table->avar_segment )
+    {
+      GX_AVarSegment  av = blend->avar_table->avar_segment;
+
+
+      FT_TRACE5(( "design coordinates"
+                  " after removing `avar' distortion:\n" ));
+
+      for ( i = 0; i < nc; i++, av++ )
+      {
+        for ( j = 1; j < (FT_UInt)av->pairCount; j++ )
+        {
+          if ( design[i] < av->correspondence[j].toCoord )
+          {
+            design[i] =
+              FT_MulDiv( design[i] - av->correspondence[j - 1].toCoord,
+                         av->correspondence[j].fromCoord -
+                           av->correspondence[j - 1].fromCoord,
+                         av->correspondence[j].toCoord -
+                           av->correspondence[j - 1].toCoord ) +
+              av->correspondence[j - 1].fromCoord;
+
+            FT_TRACE5(( "  %.5f\n", (double)design[i] / 65536 ));
+            break;
+          }
+        }
+      }
+    }
+
+    mmvar = blend->mmvar;
+    a     = mmvar->axis;
+
+    for ( i = 0; i < nc; i++, a++ )
+    {
+      if ( design[i] < 0 )
+        design[i] = a->def + FT_MulFix( design[i],
+                                        a->def - a->minimum );
+      else if ( design[i] > 0 )
+        design[i] = a->def + FT_MulFix( design[i],
+                                        a->maximum - a->def );
+      else
+        design[i] = a->def;
+    }
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****               MULTIPLE MASTERS SERVICE FUNCTIONS              *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+
+  typedef struct  GX_FVar_Head_
+  {
+    FT_Long    version;
+    FT_UShort  offsetToData;
+    FT_UShort  axisCount;
+    FT_UShort  axisSize;
+    FT_UShort  instanceCount;
+    FT_UShort  instanceSize;
+
+  } GX_FVar_Head;
+
+
+  typedef struct  fvar_axis_
+  {
+    FT_ULong   axisTag;
+    FT_Fixed   minValue;
+    FT_Fixed   defaultValue;
+    FT_Fixed   maxValue;
+    FT_UShort  flags;
+    FT_UShort  nameID;
+
+  } GX_FVar_Axis;
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Get_MM_Var
+   *
+   * @Description:
+   *   Check that the font's `fvar' table is valid, parse it, and return
+   *   those data.  It also loads (and parses) the `MVAR' table, if
+   *   possible.
+   *
+   * @InOut:
+   *   face ::
+   *     The font face.
+   *     TT_Get_MM_Var initializes the blend structure.
+   *
+   * @Output:
+   *   master ::
+   *     The `fvar' data (must be freed by caller).  Can be NULL,
+   *     which makes this function simply load MM support.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  TT_Get_MM_Var( FT_Face      face,    /* TT_Face */
+                 FT_MM_Var*  *master )
+  {
+    TT_Face              ttface     = (TT_Face)face;
+    FT_Stream            stream     = FT_FACE_STREAM( face );
+    FT_Memory            memory     = FT_FACE_MEMORY( face );
+    FT_ULong             table_len;
+    FT_Error             error      = FT_Err_Ok;
+    FT_ULong             fvar_start = 0;
+    FT_UInt              i, j;
+    FT_MM_Var*           mmvar = NULL;
+    FT_Fixed*            next_coords;
+    FT_Fixed*            nsc;
+    FT_String*           next_name;
+    FT_Var_Axis*         a;
+    FT_Fixed*            c;
+    FT_Var_Named_Style*  ns;
+    GX_FVar_Head         fvar_head  = { 0, 0, 0, 0, 0, 0 };
+    FT_Bool              usePsName  = 0;
+    FT_UInt              num_instances;
+    FT_UInt              num_axes;
+    FT_UShort*           axis_flags;
+
+    FT_Offset  mmvar_size;
+    FT_Offset  axis_flags_size;
+    FT_Offset  axis_size;
+    FT_Offset  namedstyle_size;
+    FT_Offset  next_coords_size;
+    FT_Offset  next_name_size;
+
+    FT_Bool  need_init;
+
+    static const FT_Frame_Field  fvar_fields[] =
+    {
+
+#undef  FT_STRUCTURE
+#define FT_STRUCTURE  GX_FVar_Head
+
+      FT_FRAME_START( 16 ),
+        FT_FRAME_LONG      ( version ),
+        FT_FRAME_USHORT    ( offsetToData ),
+        FT_FRAME_SKIP_SHORT,
+        FT_FRAME_USHORT    ( axisCount ),
+        FT_FRAME_USHORT    ( axisSize ),
+        FT_FRAME_USHORT    ( instanceCount ),
+        FT_FRAME_USHORT    ( instanceSize ),
+      FT_FRAME_END
+    };
+
+    static const FT_Frame_Field  fvaraxis_fields[] =
+    {
+
+#undef  FT_STRUCTURE
+#define FT_STRUCTURE  GX_FVar_Axis
+
+      FT_FRAME_START( 20 ),
+        FT_FRAME_ULONG ( axisTag ),
+        FT_FRAME_LONG  ( minValue ),
+        FT_FRAME_LONG  ( defaultValue ),
+        FT_FRAME_LONG  ( maxValue ),
+        FT_FRAME_USHORT( flags ),
+        FT_FRAME_USHORT( nameID ),
+      FT_FRAME_END
+    };
+
+    /* `num_instances` holds the number of all named instances including  */
+    /* the default instance, which might be missing in the table of named */
+    /* instances (in 'fvar').  This value is validated in `sfobjs.c` and  */
+    /* may be reset to 0 if consistency checks fail.                      */
+    num_instances = (FT_UInt)face->style_flags >> 16;
+
+    /* read the font data and set up the internal representation */
+    /* if not already done                                       */
+
+    need_init = !ttface->blend;
+
+    if ( need_init )
+    {
+      FT_TRACE2(( "FVAR " ));
+
+      if ( FT_SET_ERROR( ttface->goto_table( ttface, TTAG_fvar,
+                                             stream, &table_len ) ) )
+      {
+        FT_TRACE1(( "is missing\n" ));
+        goto Exit;
+      }
+
+      fvar_start = FT_STREAM_POS( );
+
+      /* the validity of the `fvar' header data was already checked */
+      /* in function `sfnt_init_face'                               */
+      if ( FT_STREAM_READ_FIELDS( fvar_fields, &fvar_head ) )
+        goto Exit;
+
+      /* If `num_instances` is larger, synthetization of the default  */
+      /* instance is required.  If `num_instances` is smaller,        */
+      /* however, the value has been reset to 0 in `sfnt_init_face`   */
+      /* (in `sfobjs.c`); in this case we have underallocated `mmvar` */
+      /* structs.                                                     */
+      if ( num_instances < fvar_head.instanceCount )
+      {
+        error = FT_THROW( Invalid_Table );
+        goto Exit;
+      }
+
+      usePsName = FT_BOOL( fvar_head.instanceSize ==
+                           6 + 4 * fvar_head.axisCount );
+
+      FT_TRACE2(( "loaded\n" ));
+
+      FT_TRACE5(( "%d variation ax%s\n",
+                  fvar_head.axisCount,
+                  fvar_head.axisCount == 1 ? "is" : "es" ));
+
+      if ( FT_NEW( ttface->blend ) )
+        goto Exit;
+
+      num_axes                = fvar_head.axisCount;
+      ttface->blend->num_axis = num_axes;
+    }
+    else
+      num_axes = ttface->blend->num_axis;
+
+    /* prepare storage area for MM data; this cannot overflow   */
+    /* 32-bit arithmetic because of the size limits used in the */
+    /* `fvar' table validity check in `sfnt_init_face'          */
+
+    /* the various `*_size' variables, which we also use as     */
+    /* offsets into the `mmvar' array, must be multiples of the */
+    /* pointer size (except the last one); without such an      */
+    /* alignment there might be runtime errors due to           */
+    /* misaligned addresses                                     */
+#undef  ALIGN_SIZE
+#define ALIGN_SIZE( n ) \
+          ( ( (n) + sizeof (void*) - 1 ) & ~( sizeof (void*) - 1 ) )
+
+    mmvar_size       = ALIGN_SIZE( sizeof ( FT_MM_Var ) );
+    axis_flags_size  = ALIGN_SIZE( num_axes *
+                                   sizeof ( FT_UShort ) );
+    axis_size        = ALIGN_SIZE( num_axes *
+                                   sizeof ( FT_Var_Axis ) );
+    namedstyle_size  = ALIGN_SIZE( num_instances *
+                                   sizeof ( FT_Var_Named_Style ) );
+    next_coords_size = ALIGN_SIZE( num_instances *
+                                   num_axes *
+                                   sizeof ( FT_Fixed ) );
+    next_name_size   = num_axes * 5;
+
+    if ( need_init )
+    {
+      ttface->blend->mmvar_len = mmvar_size       +
+                                 axis_flags_size  +
+                                 axis_size        +
+                                 namedstyle_size  +
+                                 next_coords_size +
+                                 next_name_size;
+
+      if ( FT_ALLOC( mmvar, ttface->blend->mmvar_len ) )
+        goto Exit;
+      ttface->blend->mmvar = mmvar;
+
+      /* set up pointers and offsets into the `mmvar' array; */
+      /* the data gets filled in later on                    */
+
+      mmvar->num_axis =
+        num_axes;
+      mmvar->num_designs =
+        ~0U;                   /* meaningless in this context; each glyph */
+                               /* may have a different number of designs  */
+                               /* (or tuples, as called by Apple)         */
+      mmvar->num_namedstyles =
+        num_instances;
+
+      /* alas, no public field in `FT_Var_Axis' for axis flags */
+      axis_flags =
+        (FT_UShort*)( (char*)mmvar + mmvar_size );
+      mmvar->axis =
+        (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
+      mmvar->namedstyle =
+        (FT_Var_Named_Style*)( (char*)mmvar->axis + axis_size );
+
+      next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
+                                 namedstyle_size );
+      for ( i = 0; i < num_instances; i++ )
+      {
+        mmvar->namedstyle[i].coords  = next_coords;
+        next_coords                 += num_axes;
+      }
+
+      next_name = (FT_String*)( (char*)mmvar->namedstyle +
+                                namedstyle_size + next_coords_size );
+      for ( i = 0; i < num_axes; i++ )
+      {
+        mmvar->axis[i].name  = next_name;
+        next_name           += 5;
+      }
+
+      /* now fill in the data */
+
+      if ( FT_STREAM_SEEK( fvar_start + fvar_head.offsetToData ) )
+        goto Exit;
+
+      a = mmvar->axis;
+      for ( i = 0; i < num_axes; i++ )
+      {
+        GX_FVar_Axis  axis_rec;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+        int  invalid = 0;
+#endif
+
+
+        if ( FT_STREAM_READ_FIELDS( fvaraxis_fields, &axis_rec ) )
+          goto Exit;
+        a->tag     = axis_rec.axisTag;
+        a->minimum = axis_rec.minValue;
+        a->def     = axis_rec.defaultValue;
+        a->maximum = axis_rec.maxValue;
+        a->strid   = axis_rec.nameID;
+
+        a->name[0] = (FT_String)(   a->tag >> 24 );
+        a->name[1] = (FT_String)( ( a->tag >> 16 ) & 0xFF );
+        a->name[2] = (FT_String)( ( a->tag >>  8 ) & 0xFF );
+        a->name[3] = (FT_String)( ( a->tag       ) & 0xFF );
+        a->name[4] = '\0';
+
+        *axis_flags = axis_rec.flags;
+
+        if ( a->minimum > a->def ||
+             a->def > a->maximum )
+        {
+          a->minimum = a->def;
+          a->maximum = a->def;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+          invalid = 1;
+#endif
+        }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+        if ( i == 0 )
+          FT_TRACE5(( "  idx   tag  "
+                   /* "  XXX  `XXXX'" */
+                      "    minimum     default     maximum   flags\n" ));
+                   /* "  XXXX.XXXXX  XXXX.XXXXX  XXXX.XXXXX  0xXXXX" */
+
+        FT_TRACE5(( "  %3d  `%s'"
+                    "  %10.5f  %10.5f  %10.5f  0x%04X%s\n",
+                    i,
+                    a->name,
+                    (double)a->minimum / 65536,
+                    (double)a->def / 65536,
+                    (double)a->maximum / 65536,
+                    *axis_flags,
+                    invalid ? " (invalid, disabled)" : "" ));
+#endif
+
+        a++;
+        axis_flags++;
+      }
+
+      FT_TRACE5(( "\n" ));
+
+      /* named instance coordinates are stored as design coordinates; */
+      /* we have to convert them to normalized coordinates also       */
+      if ( FT_NEW_ARRAY( ttface->blend->normalized_stylecoords,
+                         num_axes * num_instances ) )
+        goto Exit;
+
+      if ( fvar_head.instanceCount && !ttface->blend->avar_loaded )
+      {
+        FT_ULong  offset = FT_STREAM_POS();
+
+
+        ft_var_load_avar( ttface );
+
+        if ( FT_STREAM_SEEK( offset ) )
+          goto Exit;
+      }
+
+      FT_TRACE5(( "%d named instance%s\n",
+                  fvar_head.instanceCount,
+                  fvar_head.instanceCount == 1 ? "" : "s" ));
+
+      ns  = mmvar->namedstyle;
+      nsc = ttface->blend->normalized_stylecoords;
+      for ( i = 0; i < fvar_head.instanceCount; i++, ns++ )
+      {
+        /* PostScript names add 2 bytes to the instance record size */
+        if ( FT_FRAME_ENTER( ( usePsName ? 6L : 4L ) +
+                             4L * num_axes ) )
+          goto Exit;
+
+        ns->strid       =    FT_GET_USHORT();
+        (void) /* flags = */ FT_GET_USHORT();
+
+        c = ns->coords;
+        for ( j = 0; j < num_axes; j++, c++ )
+          *c = FT_GET_LONG();
+
+        /* valid psid values are 6, [256;32767], and 0xFFFF */
+        if ( usePsName )
+          ns->psid = FT_GET_USHORT();
+        else
+          ns->psid = 0xFFFF;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+        {
+          SFNT_Service  sfnt = (SFNT_Service)ttface->sfnt;
+
+          FT_String*  strname = NULL;
+          FT_String*  psname  = NULL;
+
+          FT_ULong  pos;
+
+
+          pos = FT_STREAM_POS();
+
+          if ( ns->strid != 0xFFFF )
+          {
+            (void)sfnt->get_name( ttface,
+                                  (FT_UShort)ns->strid,
+                                  &strname );
+            if ( strname && !ft_strcmp( strname, ".notdef" ) )
+              strname = NULL;
+          }
+
+          if ( ns->psid != 0xFFFF )
+          {
+            (void)sfnt->get_name( ttface,
+                                  (FT_UShort)ns->psid,
+                                  &psname );
+            if ( psname && !ft_strcmp( psname, ".notdef" ) )
+              psname = NULL;
+          }
+
+          (void)FT_STREAM_SEEK( pos );
+
+          FT_TRACE5(( "  named instance %d (%s%s%s, %s%s%s)\n",
+                      i,
+                      strname ? "name: `" : "",
+                      strname ? strname : "unnamed",
+                      strname ? "'" : "",
+                      psname ? "PS name: `" : "",
+                      psname ? psname : "no PS name",
+                      psname ? "'" : "" ));
+
+          FT_FREE( strname );
+          FT_FREE( psname );
+        }
+#endif /* FT_DEBUG_LEVEL_TRACE */
+
+        ft_var_to_normalized( ttface, num_axes, ns->coords, nsc );
+        nsc += num_axes;
+
+        FT_FRAME_EXIT();
+      }
+
+      if ( num_instances != fvar_head.instanceCount )
+      {
+        SFNT_Service  sfnt = (SFNT_Service)ttface->sfnt;
+
+        FT_Int   found, dummy1, dummy2;
+        FT_UInt  strid = ~0U;
+
+
+        /* The default instance is missing in array the    */
+        /* of named instances; try to synthesize an entry. */
+        /* If this fails, `default_named_instance` remains */
+        /* at value zero, which doesn't do any harm.       */
+        found = sfnt->get_name_id( ttface,
+                                   TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY,
+                                   &dummy1,
+                                   &dummy2 );
+        if ( found )
+          strid = TT_NAME_ID_TYPOGRAPHIC_SUBFAMILY;
+        else
+        {
+          found = sfnt->get_name_id( ttface,
+                                     TT_NAME_ID_FONT_SUBFAMILY,
+                                     &dummy1,
+                                     &dummy2 );
+          if ( found )
+            strid = TT_NAME_ID_FONT_SUBFAMILY;
+        }
+
+        if ( found )
+        {
+          found = sfnt->get_name_id( ttface,
+                                     TT_NAME_ID_PS_NAME,
+                                     &dummy1,
+                                     &dummy2 );
+          if ( found )
+          {
+            FT_TRACE5(( "TT_Get_MM_Var:"
+                        " Adding default instance to named instances\n" ));
+
+            /* named instance indices start with value 1 */
+            ttface->var_default_named_instance = num_instances;
+
+            ns = &mmvar->namedstyle[fvar_head.instanceCount];
+
+            ns->strid = strid;
+            ns->psid  = TT_NAME_ID_PS_NAME;
+
+            a = mmvar->axis;
+            c = ns->coords;
+            for ( j = 0; j < num_axes; j++, a++, c++ )
+              *c = a->def;
+          }
+        }
+      }
+
+      ft_var_load_mvar( ttface );
+    }
+
+    /* fill the output array if requested */
+
+    if ( master )
+    {
+      FT_UInt  n;
+
+
+      if ( FT_ALLOC( mmvar, ttface->blend->mmvar_len ) )
+        goto Exit;
+      FT_MEM_COPY( mmvar, ttface->blend->mmvar, ttface->blend->mmvar_len );
+
+      axis_flags =
+        (FT_UShort*)( (char*)mmvar + mmvar_size );
+      mmvar->axis =
+        (FT_Var_Axis*)( (char*)axis_flags + axis_flags_size );
+      mmvar->namedstyle =
+        (FT_Var_Named_Style*)( (char*)mmvar->axis+ axis_size );
+
+      next_coords = (FT_Fixed*)( (char*)mmvar->namedstyle +
+                                 namedstyle_size );
+      for ( n = 0; n < mmvar->num_namedstyles; n++ )
+      {
+        mmvar->namedstyle[n].coords  = next_coords;
+        next_coords                 += num_axes;
+      }
+
+      a         = mmvar->axis;
+      next_name = (FT_String*)( (char*)mmvar->namedstyle +
+                                namedstyle_size + next_coords_size );
+      for ( n = 0; n < num_axes; n++ )
+      {
+        a->name = next_name;
+
+        /* standard PostScript names for some standard apple tags */
+        if ( a->tag == TTAG_wght )
+          a->name = (char*)"Weight";
+        else if ( a->tag == TTAG_wdth )
+          a->name = (char*)"Width";
+        else if ( a->tag == TTAG_opsz )
+          a->name = (char*)"OpticalSize";
+        else if ( a->tag == TTAG_slnt )
+          a->name = (char*)"Slant";
+        else if ( a->tag == TTAG_ital )
+          a->name = (char*)"Italic";
+
+        next_name += 5;
+        a++;
+      }
+
+      *master = mmvar;
+    }
+
+  Exit:
+    return error;
+  }
+
+
+  static FT_Error
+  tt_set_mm_blend( TT_Face    face,
+                   FT_UInt    num_coords,
+                   FT_Fixed*  coords,
+                   FT_Bool    set_design_coords )
+  {
+    FT_Error    error = FT_Err_Ok;
+    GX_Blend    blend;
+    FT_MM_Var*  mmvar;
+    FT_UInt     i;
+
+    FT_Bool     all_design_coords = FALSE;
+
+    FT_Memory   memory = face->root.memory;
+
+    enum
+    {
+      mcvt_retain,
+      mcvt_modify,
+      mcvt_load
+
+    } manageCvt;
+
+
+    face->doblend = FALSE;
+
+    if ( !face->blend )
+    {
+      if ( FT_SET_ERROR( TT_Get_MM_Var( FT_FACE( face ), NULL ) ) )
+        goto Exit;
+    }
+
+    blend = face->blend;
+    mmvar = blend->mmvar;
+
+    if ( num_coords > mmvar->num_axis )
+    {
+      FT_TRACE2(( "TT_Set_MM_Blend:"
+                  " only using first %d of %d coordinates\n",
+                  mmvar->num_axis, num_coords ));
+      num_coords = mmvar->num_axis;
+    }
+
+    FT_TRACE5(( "TT_Set_MM_Blend:\n" ));
+    FT_TRACE5(( "  normalized design coordinates:\n" ));
+
+    for ( i = 0; i < num_coords; i++ )
+    {
+      FT_TRACE5(( "    %.5f\n", (double)coords[i] / 65536 ));
+      if ( coords[i] < -0x00010000L || coords[i] > 0x00010000L )
+      {
+        FT_TRACE1(( "TT_Set_MM_Blend: normalized design coordinate %.5f\n",
+                    (double)coords[i] / 65536 ));
+        FT_TRACE1(( "                 is out of range [-1;1]\n" ));
+        error = FT_THROW( Invalid_Argument );
+        goto Exit;
+      }
+    }
+
+    FT_TRACE5(( "\n" ));
+
+    if ( !face->is_cff2 && !blend->glyphoffsets )
+    {
+      /* While a missing 'gvar' table is acceptable, for example for */
+      /* fonts that only vary metrics information or 'COLR' v1       */
+      /* `PaintVar*` tables, an incorrect SFNT table offset or size  */
+      /* for 'gvar', or an inconsistent 'gvar' table is not.         */
+      error = ft_var_load_gvar( face );
+      if ( error != FT_Err_Table_Missing && error != FT_Err_Ok )
+        goto Exit;
+      error = FT_Err_Ok;
+    }
+
+    if ( !blend->coords )
+    {
+      if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
+        goto Exit;
+
+      /* the first time we have to compute all design coordinates */
+      all_design_coords = TRUE;
+    }
+
+    if ( !blend->normalizedcoords )
+    {
+      if ( FT_NEW_ARRAY( blend->normalizedcoords, mmvar->num_axis ) )
+        goto Exit;
+
+      manageCvt = mcvt_modify;
+
+      /* If we have not set the blend coordinates before this, then the  */
+      /* cvt table will still be what we read from the `cvt ' table and  */
+      /* we don't need to reload it.  We may need to change it though... */
+    }
+    else
+    {
+      FT_Bool    have_diff = 0;
+      FT_UInt    j;
+      FT_Fixed*  c;
+      FT_Fixed*  n;
+
+
+      manageCvt = mcvt_retain;
+
+      for ( i = 0; i < num_coords; i++ )
+      {
+        if ( blend->normalizedcoords[i] != coords[i] )
+        {
+          manageCvt = mcvt_load;
+          have_diff = 1;
+          break;
+        }
+      }
+
+      if ( !have_diff )
+      {
+        if ( FT_IS_NAMED_INSTANCE( FT_FACE( face ) ) )
+        {
+          FT_UInt  instance_index = (FT_UInt)face->root.face_index >> 16;
+
+
+          c = blend->normalizedcoords + i;
+          n = blend->normalized_stylecoords            +
+              ( instance_index - 1 ) * mmvar->num_axis +
+              i;
+
+          for ( j = i; j < mmvar->num_axis; j++, n++, c++ )
+            if ( *c != *n )
+              have_diff = 1;
+        }
+        else
+        {
+          c = blend->normalizedcoords + i;
+          for ( j = i; j < mmvar->num_axis; j++, c++ )
+            if ( *c != 0 )
+              have_diff = 1;
+        }
+      }
+
+      /* return value -1 indicates `no change' */
+      if ( !have_diff )
+      {
+        face->doblend = TRUE;
+
+        return -1;
+      }
+
+      for ( ; i < mmvar->num_axis; i++ )
+      {
+        if ( blend->normalizedcoords[i] != 0 )
+        {
+          manageCvt = mcvt_load;
+          break;
+        }
+      }
+
+      /* If we don't change the blend coords then we don't need to do  */
+      /* anything to the cvt table.  It will be correct.  Otherwise we */
+      /* no longer have the original cvt (it was modified when we set  */
+      /* the blend last time), so we must reload and then modify it.   */
+    }
+
+    blend->num_axis = mmvar->num_axis;
+    if ( coords )
+      FT_MEM_COPY( blend->normalizedcoords,
+                   coords,
+                   num_coords * sizeof ( FT_Fixed ) );
+
+    if ( set_design_coords )
+      ft_var_to_design( face,
+                        all_design_coords ? blend->num_axis : num_coords,
+                        blend->normalizedcoords,
+                        blend->coords );
+
+    face->doblend = TRUE;
+
+    if ( face->cvt )
+    {
+      switch ( manageCvt )
+      {
+      case mcvt_load:
+        /* The cvt table has been loaded already; every time we change the */
+        /* blend we may need to reload and remodify the cvt table.         */
+        FT_FREE( face->cvt );
+
+        error = tt_face_load_cvt( face, face->root.stream );
+        break;
+
+      case mcvt_modify:
+        /* The original cvt table is in memory.  All we need to do is */
+        /* apply the `cvar' table (if any).                           */
+        error = tt_face_vary_cvt( face, face->root.stream );
+        break;
+
+      case mcvt_retain:
+        /* The cvt table is correct for this set of coordinates. */
+        break;
+      }
+    }
+
+  Exit:
+    return error;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Set_MM_Blend
+   *
+   * @Description:
+   *   Set the blend (normalized) coordinates for this instance of the
+   *   font.  Check that the `gvar' table is reasonable and does some
+   *   initial preparation.
+   *
+   * @InOut:
+   *   face ::
+   *     The font.
+   *     Initialize the blend structure with `gvar' data.
+   *
+   * @Input:
+   *   num_coords ::
+   *     The number of available coordinates.  If it is
+   *     larger than the number of axes, ignore the excess
+   *     values.  If it is smaller than the number of axes,
+   *     use the default value (0) for the remaining axes.
+   *
+   *   coords ::
+   *     An array of `num_coords', each between [-1,1].
+   *
+   * @Return:
+   *   FreeType error code.  0 means success, -1 means success and unchanged
+   *   axis values.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  TT_Set_MM_Blend( FT_Face    face,       /* TT_Face */
+                   FT_UInt    num_coords,
+                   FT_Fixed*  coords )
+  {
+    return tt_set_mm_blend( (TT_Face)face, num_coords, coords, 1 );
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Get_MM_Blend
+   *
+   * @Description:
+   *   Get the blend (normalized) coordinates for this instance of the
+   *   font.
+   *
+   * @InOut:
+   *   face ::
+   *     The font.
+   *     Initialize the blend structure with `gvar' data.
+   *
+   * @Input:
+   *   num_coords ::
+   *     The number of available coordinates.  If it is
+   *     larger than the number of axes, set the excess
+   *     values to 0.
+   *
+   *   coords ::
+   *     An array of `num_coords', each between [-1,1].
+   *
+   * @Return:
+   *   FreeType error code.  0 means success, -1 means success and unchanged
+   *   axis values.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  TT_Get_MM_Blend( FT_Face    face,       /* TT_Face */
+                   FT_UInt    num_coords,
+                   FT_Fixed*  coords )
+  {
+    TT_Face  ttface = (TT_Face)face;
+
+    FT_Error  error = FT_Err_Ok;
+    GX_Blend  blend;
+    FT_UInt   i, nc;
+
+
+    if ( !ttface->blend )
+    {
+      if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
+        return error;
+    }
+
+    blend = ttface->blend;
+
+    if ( !blend->coords )
+    {
+      /* select default instance coordinates */
+      /* if no instance is selected yet      */
+      if ( FT_SET_ERROR( tt_set_mm_blend( ttface, 0, NULL, 1 ) ) )
+        return error;
+    }
+
+    nc = num_coords;
+    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 ));
+      nc = blend->num_axis;
+    }
+
+    if ( ttface->doblend )
+    {
+      for ( i = 0; i < nc; i++ )
+        coords[i] = blend->normalizedcoords[i];
+    }
+    else
+    {
+      for ( i = 0; i < nc; i++ )
+        coords[i] = 0;
+    }
+
+    for ( ; i < num_coords; i++ )
+      coords[i] = 0;
+
+    return FT_Err_Ok;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Set_Var_Design
+   *
+   * @Description:
+   *   Set the coordinates for the instance, measured in the user
+   *   coordinate system.  Parse the `avar' table (if present) to convert
+   *   from user to normalized coordinates.
+   *
+   * @InOut:
+   *   face ::
+   *     The font face.
+   *     Initialize the blend struct with `gvar' data.
+   *
+   * @Input:
+   *   num_coords ::
+   *     The number of available coordinates.  If it is
+   *     larger than the number of axes, ignore the excess
+   *     values.  If it is smaller than the number of axes,
+   *     use the default values for the remaining axes.
+   *
+   *   coords ::
+   *     A coordinate array with `num_coords' elements.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  TT_Set_Var_Design( FT_Face    face,       /* TT_Face */
+                     FT_UInt    num_coords,
+                     FT_Fixed*  coords )
+  {
+    TT_Face     ttface = (TT_Face)face;
+    FT_Error    error  = FT_Err_Ok;
+    GX_Blend    blend;
+    FT_MM_Var*  mmvar;
+    FT_UInt     i;
+    FT_Memory   memory = FT_FACE_MEMORY( face );
+
+    FT_Fixed*  c;
+    FT_Fixed*  n;
+    FT_Fixed*  normalized = NULL;
+
+    FT_Bool  have_diff = 0;
+
+
+    if ( !ttface->blend )
+    {
+      if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
+        goto Exit;
+    }
+
+    blend = ttface->blend;
+    mmvar = blend->mmvar;
+
+    if ( num_coords > mmvar->num_axis )
+    {
+      FT_TRACE2(( "TT_Set_Var_Design:"
+                  " only using first %d of %d coordinates\n",
+                  mmvar->num_axis, num_coords ));
+      num_coords = mmvar->num_axis;
+    }
+
+    if ( !blend->coords )
+    {
+      if ( FT_NEW_ARRAY( blend->coords, mmvar->num_axis ) )
+        goto Exit;
+    }
+
+    c = blend->coords;
+    n = coords;
+    for ( i = 0; i < num_coords; i++, n++, c++ )
+    {
+      if ( *c != *n )
+      {
+        *c        = *n;
+        have_diff = 1;
+      }
+    }
+
+    if ( FT_IS_NAMED_INSTANCE( face ) )
+    {
+      FT_UInt              instance_index;
+      FT_Var_Named_Style*  named_style;
+
+
+      instance_index = (FT_UInt)face->face_index >> 16;
+      named_style    = mmvar->namedstyle + instance_index - 1;
+
+      n = named_style->coords + num_coords;
+      for ( ; i < mmvar->num_axis; i++, n++, c++ )
+      {
+        if ( *c != *n )
+        {
+          *c        = *n;
+          have_diff = 1;
+        }
+      }
+    }
+    else
+    {
+      FT_Var_Axis*  a;
+
+
+      a = mmvar->axis + num_coords;
+      for ( ; i < mmvar->num_axis; i++, a++, c++ )
+      {
+        if ( *c != a->def )
+        {
+          *c        = a->def;
+          have_diff = 1;
+        }
+      }
+    }
+
+    /* return value -1 indicates `no change';                      */
+    /* we can exit early if `normalizedcoords' is already computed */
+    if ( blend->normalizedcoords && !have_diff )
+      return -1;
+
+    if ( FT_NEW_ARRAY( normalized, mmvar->num_axis ) )
+      goto Exit;
+
+    if ( !ttface->blend->avar_loaded )
+      ft_var_load_avar( ttface );
+
+    FT_TRACE5(( "TT_Set_Var_Design:\n" ));
+    FT_TRACE5(( "  normalized design coordinates:\n" ));
+    ft_var_to_normalized( ttface, num_coords, blend->coords, normalized );
+
+    error = tt_set_mm_blend( ttface, mmvar->num_axis, normalized, 0 );
+    if ( error )
+      goto Exit;
+
+  Exit:
+    FT_FREE( normalized );
+    return error;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Get_Var_Design
+   *
+   * @Description:
+   *   Get the design coordinates of the currently selected interpolated
+   *   font.
+   *
+   * @Input:
+   *   face ::
+   *     A handle to the source face.
+   *
+   *   num_coords ::
+   *     The number of design coordinates to retrieve.  If it
+   *     is larger than the number of axes, set the excess
+   *     values to~0.
+   *
+   * @Output:
+   *   coords ::
+   *     The design coordinates array.
+   *
+   * @Return:
+   *   FreeType error code.  0~means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  TT_Get_Var_Design( FT_Face    face,       /* TT_Face */
+                     FT_UInt    num_coords,
+                     FT_Fixed*  coords )
+  {
+    TT_Face   ttface = (TT_Face)face;
+    FT_Error  error  = FT_Err_Ok;
+    GX_Blend  blend;
+    FT_UInt   i, nc;
+
+
+    if ( !ttface->blend )
+    {
+      if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
+        return error;
+    }
+
+    blend = ttface->blend;
+
+    if ( !blend->coords )
+    {
+      /* select default instance coordinates */
+      /* if no instance is selected yet      */
+      if ( FT_SET_ERROR( tt_set_mm_blend( ttface, 0, NULL, 1 ) ) )
+        return error;
+    }
+
+    nc = num_coords;
+    if ( num_coords > blend->num_axis )
+    {
+      FT_TRACE2(( "TT_Get_Var_Design:"
+                  " only using first %d of %d coordinates\n",
+                  blend->num_axis, num_coords ));
+      nc = blend->num_axis;
+    }
+
+    if ( ttface->doblend )
+    {
+      for ( i = 0; i < nc; i++ )
+        coords[i] = blend->coords[i];
+    }
+    else
+    {
+      for ( i = 0; i < nc; i++ )
+        coords[i] = 0;
+    }
+
+    for ( ; i < num_coords; i++ )
+      coords[i] = 0;
+
+    return FT_Err_Ok;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Set_Named_Instance
+   *
+   * @Description:
+   *   Set the given named instance, also resetting any further
+   *   variation.
+   *
+   * @Input:
+   *   face ::
+   *     A handle to the source face.
+   *
+   *   instance_index ::
+   *     The instance index, starting with value 1.
+   *     Value 0 indicates to not use an instance.
+   *
+   * @Return:
+   *   FreeType error code.  0~means success, -1 means success and unchanged
+   *   axis values.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  TT_Set_Named_Instance( FT_Face  face,            /* TT_Face */
+                         FT_UInt  instance_index )
+  {
+    TT_Face     ttface = (TT_Face)face;
+    FT_Error    error;
+    GX_Blend    blend;
+    FT_MM_Var*  mmvar;
+
+    FT_Memory  memory = FT_FACE_MEMORY( face );
+
+    FT_UInt  num_instances;
+
+
+    if ( !ttface->blend )
+    {
+      if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
+        goto Exit;
+    }
+
+    blend = ttface->blend;
+    mmvar = blend->mmvar;
+
+    num_instances = (FT_UInt)face->style_flags >> 16;
+
+    /* `instance_index' starts with value 1, thus `>' */
+    if ( instance_index > num_instances )
+    {
+      error = FT_ERR( Invalid_Argument );
+      goto Exit;
+    }
+
+    if ( instance_index > 0 )
+    {
+      SFNT_Service  sfnt = (SFNT_Service)ttface->sfnt;
+
+      FT_Var_Named_Style*  named_style;
+      FT_String*           style_name;
+
+
+      named_style = mmvar->namedstyle + instance_index - 1;
+
+      error = sfnt->get_name( ttface,
+                              (FT_UShort)named_style->strid,
+                              &style_name );
+      if ( error )
+        goto Exit;
+
+      /* set (or replace) style name */
+      FT_FREE( face->style_name );
+      face->style_name = style_name;
+
+      /* finally, select the named instance */
+      error = TT_Set_Var_Design( face,
+                                 mmvar->num_axis,
+                                 named_style->coords );
+    }
+    else
+    {
+      /* restore non-VF style name */
+      FT_FREE( face->style_name );
+      if ( FT_STRDUP( face->style_name, ttface->non_var_style_name ) )
+        goto Exit;
+      error = TT_Set_Var_Design( face, 0, NULL );
+    }
+
+  Exit:
+    return error;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Get_Default_Named_Instance
+   *
+   * @Description:
+   *   Get the default named instance.
+   *
+   * @Input:
+   *   face ::
+   *     A handle to the source face.
+   *
+   * @Output:
+   *   instance_index ::
+   *     The default named instance index.
+   *
+   * @Return:
+   *   FreeType error code.  0~means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  TT_Get_Default_Named_Instance( FT_Face   face,
+                                 FT_UInt  *instance_index )
+  {
+    TT_Face   ttface = (TT_Face)face;
+    FT_Error  error  = FT_Err_Ok;
+
+
+    if ( !ttface->blend )
+    {
+      if ( FT_SET_ERROR( TT_Get_MM_Var( face, NULL ) ) )
+        goto Exit;
+    }
+
+    *instance_index = ttface->var_default_named_instance;
+
+  Exit:
+    return error;
+  }
+
+
+  /* This function triggers (lazy) recomputation of the `postscript_name` */
+  /* field in `TT_Face`.                                                  */
+
+  FT_LOCAL_DEF( void )
+  tt_construct_ps_name( FT_Face  face )
+  {
+    TT_Face    ttface = (TT_Face)face;
+    FT_Memory  memory = FT_FACE_MEMORY( face );
+
+
+    FT_FREE( ttface->postscript_name );
+  }
+
+
+  /*************************************************************************/
+  /*************************************************************************/
+  /*****                                                               *****/
+  /*****                     GX VAR PARSING ROUTINES                   *****/
+  /*****                                                               *****/
+  /*************************************************************************/
+  /*************************************************************************/
+
+
+#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
+
+  static FT_Error
+  tt_cvt_ready_iterator( FT_ListNode  node,
+                         void*        user )
+  {
+    TT_Size  size = (TT_Size)node->data;
+
+    FT_UNUSED( user );
+
+
+    size->cvt_ready = -1;
+
+    return FT_Err_Ok;
+  }
+
+#endif /* TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
+
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_face_vary_cvt
+   *
+   * @Description:
+   *   Modify the loaded cvt table according to the `cvar' table and the
+   *   font's blend.
+   *
+   * @InOut:
+   *   face ::
+   *     A handle to the target face object.
+   *
+   * @Input:
+   *   stream ::
+   *     A handle to the input stream.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   *
+   *   Most errors are ignored.  It is perfectly valid not to have a
+   *   `cvar' table even if there is a `gvar' and `fvar' table.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_face_vary_cvt( TT_Face    face,
+                    FT_Stream  stream )
+  {
+#ifdef TT_CONFIG_OPTION_BYTECODE_INTERPRETER
+
+    FT_Error   error;
+    FT_Memory  memory = stream->memory;
+
+    FT_Face  root = &face->root;
+
+    FT_ULong  table_start;
+    FT_ULong  table_len;
+
+    FT_UInt   tupleCount;
+    FT_ULong  offsetToData;
+
+    FT_ULong  here;
+    FT_UInt   i, j;
+
+    FT_Fixed*  tuple_coords    = NULL;
+    FT_Fixed*  im_start_coords = NULL;
+    FT_Fixed*  im_end_coords   = NULL;
+
+    GX_Blend  blend = face->blend;
+
+    FT_UInt  point_count;
+    FT_UInt  spoint_count = 0;
+
+    FT_UShort*  sharedpoints = NULL;
+    FT_UShort*  localpoints  = NULL;
+    FT_UShort*  points;
+
+    FT_Fixed*  deltas     = NULL;
+    FT_Fixed*  cvt_deltas = NULL;
+
+
+    FT_TRACE2(( "CVAR " ));
+
+    if ( !blend )
+    {
+      FT_TRACE2(( "\n" ));
+      FT_TRACE2(( "tt_face_vary_cvt: no blend specified\n" ));
+      error = FT_Err_Ok;
+      goto Exit;
+    }
+
+    if ( !face->cvt )
+    {
+      FT_TRACE2(( "\n" ));
+      FT_TRACE2(( "tt_face_vary_cvt: no `cvt ' table\n" ));
+      error = FT_Err_Ok;
+      goto Exit;
+    }
+
+    error = face->goto_table( face, TTAG_cvar, stream, &table_len );
+    if ( error )
+    {
+      FT_TRACE2(( "is missing\n" ));
+
+      error = FT_Err_Ok;
+      goto Exit;
+    }
+
+    if ( FT_FRAME_ENTER( table_len ) )
+    {
+      error = FT_Err_Ok;
+      goto Exit;
+    }
+
+    table_start = FT_Stream_FTell( stream );
+    if ( FT_GET_LONG() != 0x00010000L )
+    {
+      FT_TRACE2(( "bad table version\n" ));
+
+      error = FT_Err_Ok;
+      goto FExit;
+    }
+
+    FT_TRACE2(( "loaded\n" ));
+
+    if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
+         FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
+         FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
+      goto FExit;
+
+    tupleCount   = FT_GET_USHORT();
+    offsetToData = FT_GET_USHORT();
+
+    /* rough sanity test */
+    if ( offsetToData + ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 >
+           table_len )
+    {
+      FT_TRACE2(( "tt_face_vary_cvt:"
+                  " invalid CVT variation array header\n" ));
+
+      error = FT_THROW( Invalid_Table );
+      goto FExit;
+    }
+
+    offsetToData += table_start;
+
+    if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
+    {
+      here = FT_Stream_FTell( stream );
+
+      FT_Stream_SeekSet( stream, offsetToData );
+
+      sharedpoints = ft_var_readpackedpoints( stream,
+                                              table_len,
+                                              &spoint_count );
+      offsetToData = FT_Stream_FTell( stream );
+
+      FT_Stream_SeekSet( stream, here );
+    }
+
+    FT_TRACE5(( "cvar: there %s %d tuple%s:\n",
+                ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
+                tupleCount & GX_TC_TUPLE_COUNT_MASK,
+                ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
+
+    if ( FT_NEW_ARRAY( cvt_deltas, face->cvt_size ) )
+      goto FExit;
+
+    for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
+    {
+      FT_UInt   tupleDataSize;
+      FT_UInt   tupleIndex;
+      FT_Fixed  apply;
+
+
+      FT_TRACE6(( "  tuple %d:\n", i ));
+
+      tupleDataSize = FT_GET_USHORT();
+      tupleIndex    = FT_GET_USHORT();
+
+      if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
+      {
+        for ( j = 0; j < blend->num_axis; j++ )
+          tuple_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
+      }
+      else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
+      {
+        FT_TRACE2(( "tt_face_vary_cvt:"
+                    " invalid tuple index\n" ));
+
+        error = FT_THROW( Invalid_Table );
+        goto FExit;
+      }
+      else
+      {
+        if ( !blend->tuplecoords )
+        {
+          FT_TRACE2(( "tt_face_vary_cvt:"
+                      " no valid tuple coordinates available\n" ));
+
+          error = FT_THROW( Invalid_Table );
+          goto FExit;
+        }
+
+        FT_MEM_COPY(
+          tuple_coords,
+          blend->tuplecoords +
+            ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis,
+          blend->num_axis * sizeof ( FT_Fixed ) );
+      }
+
+      if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
+      {
+        for ( j = 0; j < blend->num_axis; j++ )
+          im_start_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
+        for ( j = 0; j < blend->num_axis; j++ )
+          im_end_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
+      }
+
+      apply = ft_var_apply_tuple( blend,
+                                  (FT_UShort)tupleIndex,
+                                  tuple_coords,
+                                  im_start_coords,
+                                  im_end_coords );
+
+      if ( apply == 0 )              /* tuple isn't active for our blend */
+      {
+        offsetToData += tupleDataSize;
+        continue;
+      }
+
+      here = FT_Stream_FTell( stream );
+
+      FT_Stream_SeekSet( stream, offsetToData );
+
+      if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
+      {
+        localpoints = ft_var_readpackedpoints( stream,
+                                               table_len,
+                                               &point_count );
+        points      = localpoints;
+      }
+      else
+      {
+        localpoints = NULL;
+        points      = sharedpoints;
+        point_count = spoint_count;
+      }
+
+      deltas = ft_var_readpackeddeltas( stream,
+                                        table_len,
+                                        point_count == 0 ? face->cvt_size
+                                                         : point_count );
+
+      if ( !points || !deltas )
+        ; /* failure, ignore it */
+
+      else if ( localpoints == ALL_POINTS )
+      {
+#ifdef FT_DEBUG_LEVEL_TRACE
+        int  count = 0;
+#endif
+
+
+        FT_TRACE7(( "    CVT deltas:\n" ));
+
+        /* this means that there are deltas for every entry in cvt */
+        for ( j = 0; j < face->cvt_size; j++ )
+        {
+          FT_Fixed  old_cvt_delta;
+
+
+          old_cvt_delta = cvt_deltas[j];
+          cvt_deltas[j] = old_cvt_delta + FT_MulFix( deltas[j], apply );
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+          if ( old_cvt_delta != cvt_deltas[j] )
+          {
+            FT_TRACE7(( "      %d: %f -> %f\n",
+                        j,
+                        (double)( FT_fdot6ToFixed( face->cvt[j] ) +
+                                    old_cvt_delta ) / 65536,
+                        (double)( FT_fdot6ToFixed( face->cvt[j] ) +
+                                    cvt_deltas[j] ) / 65536 ));
+            count++;
+          }
+#endif
+        }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+        if ( !count )
+          FT_TRACE7(( "      none\n" ));
+#endif
+      }
+
+      else
+      {
+#ifdef FT_DEBUG_LEVEL_TRACE
+        int  count = 0;
+#endif
+
+
+        FT_TRACE7(( "    CVT deltas:\n" ));
+
+        for ( j = 0; j < point_count; j++ )
+        {
+          int       pindex;
+          FT_Fixed  old_cvt_delta;
+
+
+          pindex = points[j];
+          if ( (FT_ULong)pindex >= face->cvt_size )
+            continue;
+
+          old_cvt_delta      = cvt_deltas[pindex];
+          cvt_deltas[pindex] = old_cvt_delta + FT_MulFix( deltas[j], apply );
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+          if ( old_cvt_delta != cvt_deltas[pindex] )
+          {
+            FT_TRACE7(( "      %d: %f -> %f\n",
+                        pindex,
+                        (double)( FT_fdot6ToFixed( face->cvt[pindex] ) +
+                                    old_cvt_delta ) / 65536,
+                        (double)( FT_fdot6ToFixed( face->cvt[pindex] ) +
+                                    cvt_deltas[pindex] ) / 65536 ));
+            count++;
+          }
+#endif
+        }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+        if ( !count )
+          FT_TRACE7(( "      none\n" ));
+#endif
+      }
+
+      if ( localpoints != ALL_POINTS )
+        FT_FREE( localpoints );
+      FT_FREE( deltas );
+
+      offsetToData += tupleDataSize;
+
+      FT_Stream_SeekSet( stream, here );
+    }
+
+    FT_TRACE5(( "\n" ));
+
+    for ( i = 0; i < face->cvt_size; i++ )
+      face->cvt[i] += FT_fixedToFdot6( cvt_deltas[i] );
+
+  FExit:
+    FT_FRAME_EXIT();
+
+  Exit:
+    if ( sharedpoints != ALL_POINTS )
+      FT_FREE( sharedpoints );
+    FT_FREE( tuple_coords );
+    FT_FREE( im_start_coords );
+    FT_FREE( im_end_coords );
+    FT_FREE( cvt_deltas );
+
+    /* iterate over all FT_Size objects and set `cvt_ready' to -1 */
+    /* to trigger rescaling of all CVT values                     */
+    FT_List_Iterate( &root->sizes_list,
+                     tt_cvt_ready_iterator,
+                     NULL );
+
+    return error;
+
+#else /* !TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
+
+    FT_UNUSED( face );
+    FT_UNUSED( stream );
+
+    return FT_Err_Ok;
+
+#endif /* !TT_CONFIG_OPTION_BYTECODE_INTERPRETER */
+
+  }
+
+
+  /* Shift the original coordinates of all points between indices `p1' */
+  /* and `p2', using the same difference as given by index `ref'.      */
+
+  /* modeled after `af_iup_shift' */
+
+  static void
+  tt_delta_shift( int         p1,
+                  int         p2,
+                  int         ref,
+                  FT_Vector*  in_points,
+                  FT_Vector*  out_points )
+  {
+    int        p;
+    FT_Vector  delta;
+
+
+    delta.x = out_points[ref].x - in_points[ref].x;
+    delta.y = out_points[ref].y - in_points[ref].y;
+
+    if ( delta.x == 0 && delta.y == 0 )
+      return;
+
+    for ( p = p1; p < ref; p++ )
+    {
+      out_points[p].x += delta.x;
+      out_points[p].y += delta.y;
+    }
+
+    for ( p = ref + 1; p <= p2; p++ )
+    {
+      out_points[p].x += delta.x;
+      out_points[p].y += delta.y;
+    }
+  }
+
+
+  /* Interpolate the original coordinates of all points with indices */
+  /* between `p1' and `p2', using `ref1' and `ref2' as the reference */
+  /* point indices.                                                  */
+
+  /* modeled after `af_iup_interp', `_iup_worker_interpolate', and   */
+  /* `Ins_IUP' with spec differences in handling ill-defined cases.  */
+  static void
+  tt_delta_interpolate( int         p1,
+                        int         p2,
+                        int         ref1,
+                        int         ref2,
+                        FT_Vector*  in_points,
+                        FT_Vector*  out_points )
+  {
+    int  p, i;
+
+    FT_Pos  out, in1, in2, out1, out2, d1, d2;
+
+
+    if ( p1 > p2 )
+      return;
+
+    /* handle both horizontal and vertical coordinates */
+    for ( i = 0; i <= 1; i++ )
+    {
+      /* shift array pointers so that we can access `foo.y' as `foo.x' */
+      in_points  = (FT_Vector*)( (FT_Pos*)in_points + i );
+      out_points = (FT_Vector*)( (FT_Pos*)out_points + i );
+
+      if ( in_points[ref1].x > in_points[ref2].x )
+      {
+        p    = ref1;
+        ref1 = ref2;
+        ref2 = p;
+      }
+
+      in1  = in_points[ref1].x;
+      in2  = in_points[ref2].x;
+      out1 = out_points[ref1].x;
+      out2 = out_points[ref2].x;
+      d1   = out1 - in1;
+      d2   = out2 - in2;
+
+      /* If the reference points have the same coordinate but different */
+      /* delta, inferred delta is zero.  Otherwise interpolate.         */
+      if ( in1 != in2 || out1 == out2 )
+      {
+        FT_Fixed  scale = in1 != in2 ? FT_DivFix( out2 - out1, in2 - in1 )
+                                     : 0;
+
+
+        for ( p = p1; p <= p2; p++ )
+        {
+          out = in_points[p].x;
+
+          if ( out <= in1 )
+            out += d1;
+          else if ( out >= in2 )
+            out += d2;
+          else
+            out = out1 + FT_MulFix( out - in1, scale );
+
+          out_points[p].x = out;
+        }
+      }
+    }
+  }
+
+
+  /* Interpolate points without delta values, similar to */
+  /* the `IUP' hinting instruction.                      */
+
+  /* modeled after `Ins_IUP */
+
+  static void
+  tt_interpolate_deltas( FT_Outline*  outline,
+                         FT_Vector*   out_points,
+                         FT_Vector*   in_points,
+                         FT_Bool*     has_delta )
+  {
+    FT_Int  first_point;
+    FT_Int  end_point;
+
+    FT_Int  first_delta;
+    FT_Int  cur_delta;
+
+    FT_Int    point;
+    FT_Short  contour;
+
+
+    /* ignore empty outlines */
+    if ( !outline->n_contours )
+      return;
+
+    contour = 0;
+    point   = 0;
+
+    do
+    {
+      end_point   = outline->contours[contour];
+      first_point = point;
+
+      /* search first point that has a delta */
+      while ( point <= end_point && !has_delta[point] )
+        point++;
+
+      if ( point <= end_point )
+      {
+        first_delta = point;
+        cur_delta   = point;
+
+        point++;
+
+        while ( point <= end_point )
+        {
+          /* search next point that has a delta  */
+          /* and interpolate intermediate points */
+          if ( has_delta[point] )
+          {
+            tt_delta_interpolate( cur_delta + 1,
+                                  point - 1,
+                                  cur_delta,
+                                  point,
+                                  in_points,
+                                  out_points );
+            cur_delta = point;
+          }
+
+          point++;
+        }
+
+        /* shift contour if we only have a single delta */
+        if ( cur_delta == first_delta )
+          tt_delta_shift( first_point,
+                          end_point,
+                          cur_delta,
+                          in_points,
+                          out_points );
+        else
+        {
+          /* otherwise handle remaining points       */
+          /* at the end and beginning of the contour */
+          tt_delta_interpolate( cur_delta + 1,
+                                end_point,
+                                cur_delta,
+                                first_delta,
+                                in_points,
+                                out_points );
+
+          if ( first_delta > 0 )
+            tt_delta_interpolate( first_point,
+                                  first_delta - 1,
+                                  cur_delta,
+                                  first_delta,
+                                  in_points,
+                                  out_points );
+        }
+      }
+      contour++;
+
+    } while ( contour < outline->n_contours );
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Vary_Apply_Glyph_Deltas
+   *
+   * @Description:
+   *   Apply the appropriate deltas to the current glyph.
+   *
+   * @InOut:
+   *   loader ::
+   *     A handle to the loader object.
+   *
+   *   outline ::
+   *     The outline to change, with appended phantom points.
+   *
+   * @Output:
+   *   unrounded ::
+   *     An array with `n_points' elements that is filled with unrounded
+   *     point coordinates (in 26.6 format).
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  TT_Vary_Apply_Glyph_Deltas( TT_Loader    loader,
+                              FT_Outline*  outline,
+                              FT_Vector*   unrounded )
+  {
+    FT_Error   error;
+    TT_Face    face        = loader->face;
+    FT_Stream  stream      = face->root.stream;
+    FT_Memory  memory      = stream->memory;
+    FT_UInt    glyph_index = loader->glyph_index;
+    FT_UInt    n_points    = (FT_UInt)outline->n_points + 4;
+
+    FT_Vector*  points_org = NULL;  /* coordinates in 16.16 format */
+    FT_Vector*  points_out = NULL;  /* coordinates in 16.16 format */
+    FT_Bool*    has_delta  = NULL;
+
+    FT_ULong  glyph_start;
+
+    FT_UInt   tupleCount;
+    FT_ULong  offsetToData;
+    FT_ULong  dataSize;
+
+    FT_ULong  here;
+    FT_UInt   i, j;
+
+    FT_Fixed*  tuple_coords    = NULL;
+    FT_Fixed*  im_start_coords = NULL;
+    FT_Fixed*  im_end_coords   = NULL;
+
+    GX_Blend  blend = face->blend;
+
+    FT_UInt  point_count;
+    FT_UInt  spoint_count = 0;
+
+    FT_UShort*  sharedpoints = NULL;
+    FT_UShort*  localpoints  = NULL;
+    FT_UShort*  points;
+
+    FT_Fixed*  deltas_x       = NULL;
+    FT_Fixed*  deltas_y       = NULL;
+    FT_Fixed*  point_deltas_x = NULL;
+    FT_Fixed*  point_deltas_y = NULL;
+
+
+    if ( !face->doblend || !blend )
+      return FT_THROW( Invalid_Argument );
+
+    for ( i = 0; i < n_points; i++ )
+    {
+      unrounded[i].x = INT_TO_F26DOT6( outline->points[i].x );
+      unrounded[i].y = INT_TO_F26DOT6( outline->points[i].y );
+    }
+
+    if ( glyph_index >= blend->gv_glyphcnt      ||
+         blend->glyphoffsets[glyph_index] ==
+           blend->glyphoffsets[glyph_index + 1] )
+    {
+      FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
+                  " no variation data for glyph %d\n", glyph_index ));
+      return FT_Err_Ok;
+    }
+
+    if ( FT_NEW_ARRAY( points_org, n_points ) ||
+         FT_NEW_ARRAY( points_out, n_points ) ||
+         FT_NEW_ARRAY( has_delta, n_points )  )
+      goto Fail1;
+
+    dataSize = blend->glyphoffsets[glyph_index + 1] -
+                 blend->glyphoffsets[glyph_index];
+
+    if ( FT_STREAM_SEEK( blend->glyphoffsets[glyph_index] ) ||
+         FT_FRAME_ENTER( dataSize )                         )
+      goto Fail1;
+
+    glyph_start = FT_Stream_FTell( stream );
+
+    /* each set of glyph variation data is formatted similarly to `cvar' */
+
+    if ( FT_NEW_ARRAY( tuple_coords, blend->num_axis )    ||
+         FT_NEW_ARRAY( im_start_coords, blend->num_axis ) ||
+         FT_NEW_ARRAY( im_end_coords, blend->num_axis )   )
+      goto Fail2;
+
+    tupleCount   = FT_GET_USHORT();
+    offsetToData = FT_GET_USHORT();
+
+    /* rough sanity test */
+    if ( offsetToData > dataSize                                ||
+         ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) * 4 > dataSize )
+    {
+      FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
+                  " invalid glyph variation array header\n" ));
+
+      error = FT_THROW( Invalid_Table );
+      goto Fail2;
+    }
+
+    offsetToData += glyph_start;
+
+    if ( tupleCount & GX_TC_TUPLES_SHARE_POINT_NUMBERS )
+    {
+      here = FT_Stream_FTell( stream );
+
+      FT_Stream_SeekSet( stream, offsetToData );
+
+      sharedpoints = ft_var_readpackedpoints( stream,
+                                              blend->gvar_size,
+                                              &spoint_count );
+      offsetToData = FT_Stream_FTell( stream );
+
+      FT_Stream_SeekSet( stream, here );
+    }
+
+    FT_TRACE5(( "gvar: there %s %d tuple%s:\n",
+                ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "is" : "are",
+                tupleCount & GX_TC_TUPLE_COUNT_MASK,
+                ( tupleCount & GX_TC_TUPLE_COUNT_MASK ) == 1 ? "" : "s" ));
+
+    if ( FT_NEW_ARRAY( point_deltas_x, n_points ) ||
+         FT_NEW_ARRAY( point_deltas_y, n_points ) )
+      goto Fail3;
+
+    for ( j = 0; j < n_points; j++ )
+    {
+      points_org[j].x = FT_intToFixed( outline->points[j].x );
+      points_org[j].y = FT_intToFixed( outline->points[j].y );
+    }
+
+    for ( i = 0; i < ( tupleCount & GX_TC_TUPLE_COUNT_MASK ); i++ )
+    {
+      FT_UInt   tupleDataSize;
+      FT_UInt   tupleIndex;
+      FT_Fixed  apply;
+
+
+      FT_TRACE6(( "  tuple %d:\n", i ));
+
+      tupleDataSize = FT_GET_USHORT();
+      tupleIndex    = FT_GET_USHORT();
+
+      if ( tupleIndex & GX_TI_EMBEDDED_TUPLE_COORD )
+      {
+        for ( j = 0; j < blend->num_axis; j++ )
+          tuple_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
+      }
+      else if ( ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) >= blend->tuplecount )
+      {
+        FT_TRACE2(( "TT_Vary_Apply_Glyph_Deltas:"
+                    " invalid tuple index\n" ));
+
+        error = FT_THROW( Invalid_Table );
+        goto Fail3;
+      }
+      else
+        FT_MEM_COPY(
+          tuple_coords,
+          blend->tuplecoords +
+            ( tupleIndex & GX_TI_TUPLE_INDEX_MASK ) * blend->num_axis,
+          blend->num_axis * sizeof ( FT_Fixed ) );
+
+      if ( tupleIndex & GX_TI_INTERMEDIATE_TUPLE )
+      {
+        for ( j = 0; j < blend->num_axis; j++ )
+          im_start_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
+        for ( j = 0; j < blend->num_axis; j++ )
+          im_end_coords[j] = FT_fdot14ToFixed( FT_GET_SHORT() );
+      }
+
+      apply = ft_var_apply_tuple( blend,
+                                  (FT_UShort)tupleIndex,
+                                  tuple_coords,
+                                  im_start_coords,
+                                  im_end_coords );
+
+      if ( apply == 0 )              /* tuple isn't active for our blend */
+      {
+        offsetToData += tupleDataSize;
+        continue;
+      }
+
+      here = FT_Stream_FTell( stream );
+
+      FT_Stream_SeekSet( stream, offsetToData );
+
+      if ( tupleIndex & GX_TI_PRIVATE_POINT_NUMBERS )
+      {
+        localpoints = ft_var_readpackedpoints( stream,
+                                               blend->gvar_size,
+                                               &point_count );
+        points      = localpoints;
+      }
+      else
+      {
+        points      = sharedpoints;
+        point_count = spoint_count;
+      }
+
+      deltas_x = ft_var_readpackeddeltas( stream,
+                                          blend->gvar_size,
+                                          point_count == 0 ? n_points
+                                                           : point_count );
+      deltas_y = ft_var_readpackeddeltas( stream,
+                                          blend->gvar_size,
+                                          point_count == 0 ? n_points
+                                                           : point_count );
+
+      if ( !points || !deltas_y || !deltas_x )
+        ; /* failure, ignore it */
+
+      else if ( points == ALL_POINTS )
+      {
+#ifdef FT_DEBUG_LEVEL_TRACE
+        int  count = 0;
+#endif
+
+
+        FT_TRACE7(( "    point deltas:\n" ));
+
+        /* this means that there are deltas for every point in the glyph */
+        for ( j = 0; j < n_points; j++ )
+        {
+          FT_Fixed  old_point_delta_x = point_deltas_x[j];
+          FT_Fixed  old_point_delta_y = point_deltas_y[j];
+
+          FT_Fixed  point_delta_x = FT_MulFix( deltas_x[j], apply );
+          FT_Fixed  point_delta_y = FT_MulFix( deltas_y[j], apply );
+
+
+          point_deltas_x[j] = old_point_delta_x + point_delta_x;
+          point_deltas_y[j] = old_point_delta_y + point_delta_y;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+          if ( point_delta_x || point_delta_y )
+          {
+            FT_TRACE7(( "      %d: (%f, %f) -> (%f, %f)\n",
+                        j,
+                        (double)( FT_intToFixed( outline->points[j].x ) +
+                                    old_point_delta_x ) / 65536,
+                        (double)( FT_intToFixed( outline->points[j].y ) +
+                                    old_point_delta_y ) / 65536,
+                        (double)( FT_intToFixed( outline->points[j].x ) +
+                                    point_deltas_x[j] ) / 65536,
+                        (double)( FT_intToFixed( outline->points[j].y ) +
+                                    point_deltas_y[j] ) / 65536 ));
+            count++;
+          }
+#endif
+        }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+        if ( !count )
+          FT_TRACE7(( "      none\n" ));
+#endif
+      }
+
+      else
+      {
+#ifdef FT_DEBUG_LEVEL_TRACE
+        int  count = 0;
+#endif
+
+
+        /* we have to interpolate the missing deltas similar to the */
+        /* IUP bytecode instruction                                 */
+        for ( j = 0; j < n_points; j++ )
+        {
+          has_delta[j]  = FALSE;
+          points_out[j] = points_org[j];
+        }
+
+        for ( j = 0; j < point_count; j++ )
+        {
+          FT_UShort  idx = points[j];
+
+
+          if ( idx >= n_points )
+            continue;
+
+          has_delta[idx] = TRUE;
+
+          points_out[idx].x += FT_MulFix( deltas_x[j], apply );
+          points_out[idx].y += FT_MulFix( deltas_y[j], apply );
+        }
+
+        /* no need to handle phantom points here,      */
+        /* since solitary points can't be interpolated */
+        tt_interpolate_deltas( outline,
+                               points_out,
+                               points_org,
+                               has_delta );
+
+        FT_TRACE7(( "    point deltas:\n" ));
+
+        for ( j = 0; j < n_points; j++ )
+        {
+          FT_Fixed  old_point_delta_x = point_deltas_x[j];
+          FT_Fixed  old_point_delta_y = point_deltas_y[j];
+
+          FT_Pos  point_delta_x = points_out[j].x - points_org[j].x;
+          FT_Pos  point_delta_y = points_out[j].y - points_org[j].y;
+
+
+          point_deltas_x[j] = old_point_delta_x + point_delta_x;
+          point_deltas_y[j] = old_point_delta_y + point_delta_y;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+          if ( point_delta_x || point_delta_y )
+          {
+            FT_TRACE7(( "      %d: (%f, %f) -> (%f, %f)\n",
+                        j,
+                        (double)( FT_intToFixed( outline->points[j].x ) +
+                                    old_point_delta_x ) / 65536,
+                        (double)( FT_intToFixed( outline->points[j].y ) +
+                                    old_point_delta_y ) / 65536,
+                        (double)( FT_intToFixed( outline->points[j].x ) +
+                                    point_deltas_x[j] ) / 65536,
+                        (double)( FT_intToFixed( outline->points[j].y ) +
+                                    point_deltas_y[j] ) / 65536 ));
+            count++;
+          }
+#endif
+        }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+        if ( !count )
+          FT_TRACE7(( "      none\n" ));
+#endif
+      }
+
+      if ( localpoints != ALL_POINTS )
+        FT_FREE( localpoints );
+      FT_FREE( deltas_x );
+      FT_FREE( deltas_y );
+
+      offsetToData += tupleDataSize;
+
+      FT_Stream_SeekSet( stream, here );
+    }
+
+    FT_TRACE5(( "\n" ));
+
+    /* To avoid double adjustment of advance width or height, */
+    /* do not move phantom points if there is HVAR or VVAR    */
+    /* support, respectively.                                 */
+    if ( face->variation_support & TT_FACE_FLAG_VAR_HADVANCE )
+    {
+      point_deltas_x[n_points - 4] = 0;
+      point_deltas_y[n_points - 4] = 0;
+      point_deltas_x[n_points - 3] = 0;
+      point_deltas_y[n_points - 3] = 0;
+    }
+    if ( face->variation_support & TT_FACE_FLAG_VAR_VADVANCE )
+    {
+      point_deltas_x[n_points - 2] = 0;
+      point_deltas_y[n_points - 2] = 0;
+      point_deltas_x[n_points - 1] = 0;
+      point_deltas_y[n_points - 1] = 0;
+    }
+
+    for ( i = 0; i < n_points; i++ )
+    {
+      unrounded[i].x += FT_fixedToFdot6( point_deltas_x[i] );
+      unrounded[i].y += FT_fixedToFdot6( point_deltas_y[i] );
+
+      outline->points[i].x += FT_fixedToInt( point_deltas_x[i] );
+      outline->points[i].y += FT_fixedToInt( point_deltas_y[i] );
+    }
+
+    /* To avoid double adjustment of advance width or height, */
+    /* adjust phantom points only if there is no HVAR or VVAR */
+    /* support, respectively.                                 */
+    if ( !( face->variation_support & TT_FACE_FLAG_VAR_HADVANCE ) )
+    {
+      loader->pp1      = outline->points[n_points - 4];
+      loader->pp2      = outline->points[n_points - 3];
+      loader->linear   = FT_PIX_ROUND( unrounded[n_points - 3].x -
+                                       unrounded[n_points - 4].x ) / 64;
+    }
+    if ( !( face->variation_support & TT_FACE_FLAG_VAR_VADVANCE ) )
+    {
+      loader->pp3      = outline->points[n_points - 2];
+      loader->pp4      = outline->points[n_points - 1];
+      loader->vadvance = FT_PIX_ROUND( unrounded[n_points - 1].y -
+                                       unrounded[n_points - 2].y ) / 64;
+    }
+
+  Fail3:
+    FT_FREE( point_deltas_x );
+    FT_FREE( point_deltas_y );
+
+  Fail2:
+    if ( sharedpoints != ALL_POINTS )
+      FT_FREE( sharedpoints );
+    FT_FREE( tuple_coords );
+    FT_FREE( im_start_coords );
+    FT_FREE( im_end_coords );
+
+    FT_FRAME_EXIT();
+
+  Fail1:
+    FT_FREE( points_org );
+    FT_FREE( points_out );
+    FT_FREE( has_delta );
+
+    return error;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_get_var_blend
+   *
+   * @Description:
+   *   An extended internal version of `TT_Get_MM_Blend' that returns
+   *   pointers instead of copying data, without any initialization of
+   *   the MM machinery in case it isn't loaded yet.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_get_var_blend( FT_Face      face,             /* TT_Face */
+                    FT_UInt     *num_coords,
+                    FT_Fixed*   *coords,
+                    FT_Fixed*   *normalizedcoords,
+                    FT_MM_Var*  *mm_var )
+  {
+    TT_Face  ttface = (TT_Face)face;
+
+
+    if ( ttface->blend )
+    {
+      if ( num_coords )
+        *num_coords       = ttface->blend->num_axis;
+      if ( coords )
+        *coords           = ttface->blend->coords;
+      if ( normalizedcoords )
+        *normalizedcoords = ttface->blend->normalizedcoords;
+      if ( mm_var )
+        *mm_var           = ttface->blend->mmvar;
+    }
+    else
+    {
+      if ( num_coords )
+        *num_coords = 0;
+      if ( coords )
+        *coords     = NULL;
+      if ( mm_var )
+        *mm_var     = NULL;
+    }
+
+    return FT_Err_Ok;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  tt_var_done_item_variation_store( FT_Face          face,
+                                    GX_ItemVarStore  itemStore )
+  {
+    FT_Memory  memory = FT_FACE_MEMORY( face );
+    FT_UInt    i;
+
+
+    if ( itemStore->varData )
+    {
+      for ( i = 0; i < itemStore->dataCount; i++ )
+      {
+        FT_FREE( itemStore->varData[i].regionIndices );
+        FT_FREE( itemStore->varData[i].deltaSet );
+      }
+
+      FT_FREE( itemStore->varData );
+    }
+
+    if ( itemStore->varRegionList )
+    {
+      for ( i = 0; i < itemStore->regionCount; i++ )
+        FT_FREE( itemStore->varRegionList[i].axisList );
+
+      FT_FREE( itemStore->varRegionList );
+    }
+  }
+
+
+  FT_LOCAL_DEF( void )
+  tt_var_done_delta_set_index_map( FT_Face            face,
+                                   GX_DeltaSetIdxMap  deltaSetIdxMap )
+  {
+    FT_Memory  memory = FT_FACE_MEMORY( face );
+
+
+    FT_FREE( deltaSetIdxMap->innerIndex );
+    FT_FREE( deltaSetIdxMap->outerIndex );
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_done_blend
+   *
+   * @Description:
+   *   Free the blend internal data structure.
+   */
+  FT_LOCAL_DEF( void )
+  tt_done_blend( FT_Face  face )
+  {
+    TT_Face    ttface = (TT_Face)face;
+    FT_Memory  memory = FT_FACE_MEMORY( face );
+    GX_Blend   blend  = ttface->blend;
+
+
+    if ( blend )
+    {
+      FT_UInt  i, num_axes;
+
+
+      /* blend->num_axis might not be set up yet */
+      num_axes = blend->mmvar->num_axis;
+
+      FT_FREE( blend->coords );
+      FT_FREE( blend->normalizedcoords );
+      FT_FREE( blend->normalized_stylecoords );
+      FT_FREE( blend->mmvar );
+
+      if ( blend->avar_table )
+      {
+        if ( blend->avar_table->avar_segment )
+        {
+          for ( i = 0; i < num_axes; i++ )
+            FT_FREE( blend->avar_table->avar_segment[i].correspondence );
+          FT_FREE( blend->avar_table->avar_segment );
+        }
+
+        tt_var_done_item_variation_store( face,
+                                          &blend->avar_table->itemStore );
+
+        tt_var_done_delta_set_index_map( face,
+                                         &blend->avar_table->axisMap );
+
+        FT_FREE( blend->avar_table );
+      }
+
+      if ( blend->hvar_table )
+      {
+        tt_var_done_item_variation_store( face,
+                                          &blend->hvar_table->itemStore );
+
+        tt_var_done_delta_set_index_map( face,
+                                         &blend->hvar_table->widthMap );
+        FT_FREE( blend->hvar_table );
+      }
+
+      if ( blend->vvar_table )
+      {
+        tt_var_done_item_variation_store( face,
+                                          &blend->vvar_table->itemStore );
+
+        tt_var_done_delta_set_index_map( face,
+                                         &blend->vvar_table->widthMap );
+        FT_FREE( blend->vvar_table );
+      }
+
+      if ( blend->mvar_table )
+      {
+        tt_var_done_item_variation_store( face,
+                                          &blend->mvar_table->itemStore );
+
+        FT_FREE( blend->mvar_table->values );
+        FT_FREE( blend->mvar_table );
+      }
+
+      FT_FREE( blend->tuplecoords );
+      FT_FREE( blend->glyphoffsets );
+      FT_FREE( blend );
+    }
+  }
+
+#else /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+  /* ANSI C doesn't like empty source files */
+  typedef int  tt_gxvar_dummy_;
+
+#endif /* !TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+
+/* END */
diff --git a/src/preload/ttgxvar.h b/src/preload/ttgxvar.h
new file mode 100644
index 000000000..e3da6d170
--- /dev/null
+++ b/src/preload/ttgxvar.h
@@ -0,0 +1,453 @@
+/****************************************************************************
+ *
+ * ttgxvar.h
+ *
+ *   TrueType GX Font Variation loader (specification)
+ *
+ * Copyright (C) 2004-2023 by
+ * David Turner, Robert Wilhelm, Werner Lemberg and George Williams.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+#ifndef TTGXVAR_H_
+#define TTGXVAR_H_
+
+
+#include <freetype/internal/ftmmtypes.h>
+#include "ttobjs.h"
+
+
+FT_BEGIN_HEADER
+
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+  /**************************************************************************
+   *
+   * @Struct:
+   *   GX_AVarCorrespondenceRec
+   *
+   * @Description:
+   *   A data structure representing `shortFracCorrespondence' in `avar'
+   *   table according to the specifications from Apple.
+   */
+  typedef struct  GX_AVarCorrespondenceRec_
+  {
+    FT_Fixed  fromCoord;
+    FT_Fixed  toCoord;
+
+  } GX_AVarCorrespondenceRec_, *GX_AVarCorrespondence;
+
+
+  /**************************************************************************
+   *
+   * @Struct:
+   *   GX_AVarRec
+   *
+   * @Description:
+   *   Data from the segment field of `avar' table.
+   *   There is one of these for each axis.
+   */
+  typedef struct  GX_AVarSegmentRec_
+  {
+    FT_UShort              pairCount;
+    GX_AVarCorrespondence  correspondence; /* array with pairCount entries */
+
+  } GX_AVarSegmentRec, *GX_AVarSegment;
+
+
+  /**************************************************************************
+   *
+   * @Struct:
+   *   GX_AVarTableRec
+   *
+   * @Description:
+   *   Data from the `avar' table.
+   */
+  typedef struct  GX_AVarTableRec_
+  {
+    GX_AVarSegment        avar_segment;   /* avar_segment[num_axis] */
+    GX_ItemVarStoreRec    itemStore;      /* Item Variation Store   */
+    GX_DeltaSetIdxMapRec  axisMap;        /* Axis Mapping           */
+
+  } GX_AVarTableRec, *GX_AVarTable;
+
+
+  /**************************************************************************
+   *
+   * @Struct:
+   *   GX_HVVarTableRec
+   *
+   * @Description:
+   *   Data from either the `HVAR' or `VVAR' table.
+   */
+  typedef struct  GX_HVVarTableRec_
+  {
+    GX_ItemVarStoreRec    itemStore;        /* Item Variation Store  */
+    GX_DeltaSetIdxMapRec  widthMap;         /* Advance Width Mapping */
+
+#if 0
+    GX_DeltaSetIdxMapRec  lsbMap;           /* not implemented */
+    GX_DeltaSetIdxMapRec  rsbMap;           /* not implemented */
+
+    GX_DeltaSetIdxMapRec  tsbMap;           /* not implemented */
+    GX_DeltaSetIdxMapRec  bsbMap;           /* not implemented */
+    GX_DeltaSetIdxMapRec  vorgMap;          /* not implemented */
+#endif
+
+  } GX_HVVarTableRec, *GX_HVVarTable;
+
+
+#define MVAR_TAG_GASP_0  FT_MAKE_TAG( 'g', 's', 'p', '0' )
+#define MVAR_TAG_GASP_1  FT_MAKE_TAG( 'g', 's', 'p', '1' )
+#define MVAR_TAG_GASP_2  FT_MAKE_TAG( 'g', 's', 'p', '2' )
+#define MVAR_TAG_GASP_3  FT_MAKE_TAG( 'g', 's', 'p', '3' )
+#define MVAR_TAG_GASP_4  FT_MAKE_TAG( 'g', 's', 'p', '4' )
+#define MVAR_TAG_GASP_5  FT_MAKE_TAG( 'g', 's', 'p', '5' )
+#define MVAR_TAG_GASP_6  FT_MAKE_TAG( 'g', 's', 'p', '6' )
+#define MVAR_TAG_GASP_7  FT_MAKE_TAG( 'g', 's', 'p', '7' )
+#define MVAR_TAG_GASP_8  FT_MAKE_TAG( 'g', 's', 'p', '8' )
+#define MVAR_TAG_GASP_9  FT_MAKE_TAG( 'g', 's', 'p', '9' )
+
+#define MVAR_TAG_CPHT  FT_MAKE_TAG( 'c', 'p', 'h', 't' )
+#define MVAR_TAG_HASC  FT_MAKE_TAG( 'h', 'a', 's', 'c' )
+#define MVAR_TAG_HCLA  FT_MAKE_TAG( 'h', 'c', 'l', 'a' )
+#define MVAR_TAG_HCLD  FT_MAKE_TAG( 'h', 'c', 'l', 'd' )
+#define MVAR_TAG_HCOF  FT_MAKE_TAG( 'h', 'c', 'o', 'f' )
+#define MVAR_TAG_HCRN  FT_MAKE_TAG( 'h', 'c', 'r', 'n' )
+#define MVAR_TAG_HCRS  FT_MAKE_TAG( 'h', 'c', 'r', 's' )
+#define MVAR_TAG_HDSC  FT_MAKE_TAG( 'h', 'd', 's', 'c' )
+#define MVAR_TAG_HLGP  FT_MAKE_TAG( 'h', 'l', 'g', 'p' )
+#define MVAR_TAG_SBXO  FT_MAKE_TAG( 's', 'b', 'x', 'o' )
+#define MVAR_TAG_SBXS  FT_MAKE_TAG( 's', 'b', 'x', 's' )
+#define MVAR_TAG_SBYO  FT_MAKE_TAG( 's', 'b', 'y', 'o' )
+#define MVAR_TAG_SBYS  FT_MAKE_TAG( 's', 'b', 'y', 's' )
+#define MVAR_TAG_SPXO  FT_MAKE_TAG( 's', 'p', 'x', 'o' )
+#define MVAR_TAG_SPXS  FT_MAKE_TAG( 's', 'p', 'x', 's' )
+#define MVAR_TAG_SPYO  FT_MAKE_TAG( 's', 'p', 'y', 'o' )
+#define MVAR_TAG_SPYS  FT_MAKE_TAG( 's', 'p', 'y', 's' )
+#define MVAR_TAG_STRO  FT_MAKE_TAG( 's', 't', 'r', 'o' )
+#define MVAR_TAG_STRS  FT_MAKE_TAG( 's', 't', 'r', 's' )
+#define MVAR_TAG_UNDO  FT_MAKE_TAG( 'u', 'n', 'd', 'o' )
+#define MVAR_TAG_UNDS  FT_MAKE_TAG( 'u', 'n', 'd', 's' )
+#define MVAR_TAG_VASC  FT_MAKE_TAG( 'v', 'a', 's', 'c' )
+#define MVAR_TAG_VCOF  FT_MAKE_TAG( 'v', 'c', 'o', 'f' )
+#define MVAR_TAG_VCRN  FT_MAKE_TAG( 'v', 'c', 'r', 'n' )
+#define MVAR_TAG_VCRS  FT_MAKE_TAG( 'v', 'c', 'r', 's' )
+#define MVAR_TAG_VDSC  FT_MAKE_TAG( 'v', 'd', 's', 'c' )
+#define MVAR_TAG_VLGP  FT_MAKE_TAG( 'v', 'l', 'g', 'p' )
+#define MVAR_TAG_XHGT  FT_MAKE_TAG( 'x', 'h', 'g', 't' )
+
+
+  typedef struct  GX_ValueRec_
+  {
+    FT_ULong   tag;
+    FT_UShort  outerIndex;
+    FT_UShort  innerIndex;
+
+    FT_Short  unmodified;  /* values are either FT_Short or FT_UShort */
+
+  } GX_ValueRec, *GX_Value;
+
+
+  /**************************************************************************
+   *
+   * @Struct:
+   *   GX_MVarTableRec
+   *
+   * @Description:
+   *   Data from the `MVAR' table.
+   */
+  typedef struct  GX_MVarTableRec_
+  {
+    FT_UShort  valueCount;
+
+    GX_ItemVarStoreRec  itemStore;        /* Item Variation Store  */
+    GX_Value            values;           /* Value Records         */
+
+  } GX_MVarTableRec, *GX_MVarTable;
+
+
+  /**************************************************************************
+   *
+   * @Struct:
+   *   GX_BlendRec
+   *
+   * @Description:
+   *   Data for interpolating a font from a distortable font specified
+   *   by the GX *var tables ([fgcahvm]var).
+   *
+   * @Fields:
+   *   num_axis ::
+   *     The number of axes along which interpolation may happen.
+   *
+   *   coords ::
+   *     An array of design coordinates (in user space) indicating the
+   *     contribution along each axis to the final interpolated font.
+   *     `normalizedcoords' holds the same values.
+   *
+   *   normalizedcoords ::
+   *     An array of normalized values (between [-1,1]) indicating the
+   *     contribution along each axis to the final interpolated font.
+   *     `coords' holds the same values.
+   *
+   *   mmvar ::
+   *     Data from the `fvar' table.
+   *
+   *   mmvar_len ::
+   *     The length of the `mmvar' structure.
+   *
+   *   normalized_stylecoords ::
+   *     A two-dimensional array that holds the named instance data from
+   *     `mmvar' as normalized values.
+   *
+   *   avar_loaded ::
+   *     A Boolean; if set, FreeType tried to load (and parse) the `avar'
+   *     table.
+   *
+   *   avar_table ::
+   *     Data from the `avar' table.
+   *
+   *   hvar_loaded ::
+   *     A Boolean; if set, FreeType tried to load (and parse) the `hvar'
+   *     table.
+   *
+   *   hvar_checked ::
+   *     A Boolean; if set, FreeType successfully loaded and parsed the
+   *     `hvar' table.
+   *
+   *   hvar_error ::
+   *     If loading and parsing of the `hvar' table failed, this field
+   *     holds the corresponding error code.
+   *
+   *   hvar_table ::
+   *     Data from the `hvar' table.
+   *
+   *   vvar_loaded ::
+   *     A Boolean; if set, FreeType tried to load (and parse) the `vvar'
+   *     table.
+   *
+   *   vvar_checked ::
+   *     A Boolean; if set, FreeType successfully loaded and parsed the
+   *     `vvar' table.
+   *
+   *   vvar_error ::
+   *     If loading and parsing of the `vvar' table failed, this field
+   *     holds the corresponding error code.
+   *
+   *   vvar_table ::
+   *     Data from the `vvar' table.
+   *
+   *   mvar_table ::
+   *     Data from the `mvar' table.
+   *
+   *   tuplecount ::
+   *     The number of shared tuples in the `gvar' table.
+   *
+   *   tuplecoords ::
+   *     A two-dimensional array that holds the shared tuple coordinates
+   *     in the `gvar' table.
+   *
+   *   gv_glyphcnt ::
+   *     The number of glyphs handled in the `gvar' table.
+   *
+   *   glyphoffsets ::
+   *     Offsets into the glyph variation data array.
+   *
+   *   gvar_size ::
+   *     The size of the `gvar' table.
+   */
+  typedef struct  GX_BlendRec_
+  {
+    FT_UInt         num_axis;
+    FT_Fixed*       coords;
+    FT_Fixed*       normalizedcoords;
+
+    FT_MM_Var*      mmvar;
+    FT_Offset       mmvar_len;
+
+    FT_Fixed*       normalized_stylecoords;
+                      /* normalized_stylecoords[num_namedstyles][num_axis] */
+
+    FT_Bool         avar_loaded;
+    GX_AVarTable    avar_table;
+
+    FT_Bool         hvar_loaded;
+    FT_Bool         hvar_checked;
+    FT_Error        hvar_error;
+    GX_HVVarTable   hvar_table;
+
+    FT_Bool         vvar_loaded;
+    FT_Bool         vvar_checked;
+    FT_Error        vvar_error;
+    GX_HVVarTable   vvar_table;
+
+    GX_MVarTable    mvar_table;
+
+    FT_UInt         tuplecount;
+    FT_Fixed*       tuplecoords;      /* tuplecoords[tuplecount][num_axis] */
+
+    FT_UInt         gv_glyphcnt;
+    FT_ULong*       glyphoffsets;         /* glyphoffsets[gv_glyphcnt + 1] */
+
+    FT_ULong        gvar_size;
+
+  } GX_BlendRec;
+
+
+  /**************************************************************************
+   *
+   * @enum:
+   *   GX_TupleCountFlags
+   *
+   * @Description:
+   *   Flags used within the `TupleCount' field of the `gvar' table.
+   */
+  typedef enum  GX_TupleCountFlags_
+  {
+    GX_TC_TUPLES_SHARE_POINT_NUMBERS = 0x8000,
+    GX_TC_RESERVED_TUPLE_FLAGS       = 0x7000,
+    GX_TC_TUPLE_COUNT_MASK           = 0x0FFF
+
+  } GX_TupleCountFlags;
+
+
+  /**************************************************************************
+   *
+   * @enum:
+   *   GX_TupleIndexFlags
+   *
+   * @Description:
+   *   Flags used within the `TupleIndex' field of the `gvar' and `cvar'
+   *   tables.
+   */
+  typedef enum  GX_TupleIndexFlags_
+  {
+    GX_TI_EMBEDDED_TUPLE_COORD  = 0x8000,
+    GX_TI_INTERMEDIATE_TUPLE    = 0x4000,
+    GX_TI_PRIVATE_POINT_NUMBERS = 0x2000,
+    GX_TI_RESERVED_TUPLE_FLAG   = 0x1000,
+    GX_TI_TUPLE_INDEX_MASK      = 0x0FFF
+
+  } GX_TupleIndexFlags;
+
+
+#define TTAG_wght  FT_MAKE_TAG( 'w', 'g', 'h', 't' )
+#define TTAG_wdth  FT_MAKE_TAG( 'w', 'd', 't', 'h' )
+#define TTAG_opsz  FT_MAKE_TAG( 'o', 'p', 's', 'z' )
+#define TTAG_slnt  FT_MAKE_TAG( 's', 'l', 'n', 't' )
+#define TTAG_ital  FT_MAKE_TAG( 'i', 't', 'a', 'l' )
+
+
+  FT_LOCAL( FT_Error )
+  TT_Set_MM_Blend( FT_Face    face,
+                   FT_UInt    num_coords,
+                   FT_Fixed*  coords );
+
+  FT_LOCAL( FT_Error )
+  TT_Get_MM_Blend( FT_Face    face,
+                   FT_UInt    num_coords,
+                   FT_Fixed*  coords );
+
+  FT_LOCAL( FT_Error )
+  TT_Set_Var_Design( FT_Face    face,
+                     FT_UInt    num_coords,
+                     FT_Fixed*  coords );
+
+  FT_LOCAL( FT_Error )
+  TT_Get_MM_Var( FT_Face      face,
+                 FT_MM_Var*  *master );
+
+  FT_LOCAL( FT_Error )
+  TT_Get_Var_Design( FT_Face    face,
+                     FT_UInt    num_coords,
+                     FT_Fixed*  coords );
+
+  FT_LOCAL( FT_Error )
+  TT_Set_Named_Instance( FT_Face  face,
+                         FT_UInt  instance_index );
+
+  FT_LOCAL( FT_Error )
+  TT_Get_Default_Named_Instance( FT_Face   face,
+                                 FT_UInt  *instance_index );
+
+  FT_LOCAL( void )
+  tt_construct_ps_name( FT_Face  face );
+
+  FT_LOCAL( FT_Error )
+  tt_face_vary_cvt( TT_Face    face,
+                    FT_Stream  stream );
+
+
+  FT_LOCAL( FT_Error )
+  TT_Vary_Apply_Glyph_Deltas( TT_Loader    loader,
+                              FT_Outline*  outline,
+                              FT_Vector*   unrounded );
+
+  FT_LOCAL( FT_Error )
+  tt_hadvance_adjust( FT_Face  face,
+                      FT_UInt  gindex,
+                      FT_Int  *adelta );
+
+  FT_LOCAL( FT_Error )
+  tt_vadvance_adjust( FT_Face  face,
+                      FT_UInt  gindex,
+                      FT_Int  *adelta );
+
+  FT_LOCAL( void )
+  tt_apply_mvar( FT_Face  face );
+
+  FT_LOCAL( FT_Error )
+  tt_var_load_item_variation_store( FT_Face          face,
+                                    FT_ULong         offset,
+                                    GX_ItemVarStore  itemStore );
+
+  FT_LOCAL( FT_Error )
+  tt_var_load_delta_set_index_mapping( FT_Face            face,
+                                       FT_ULong           offset,
+                                       GX_DeltaSetIdxMap  map,
+                                       GX_ItemVarStore    itemStore,
+                                       FT_ULong           table_len );
+
+  FT_LOCAL( FT_ItemVarDelta )
+  tt_var_get_item_delta( FT_Face          face,
+                         GX_ItemVarStore  itemStore,
+                         FT_UInt          outerIndex,
+                         FT_UInt          innerIndex );
+
+  FT_LOCAL( void )
+  tt_var_done_item_variation_store( FT_Face          face,
+                                    GX_ItemVarStore  itemStore );
+
+  FT_LOCAL( void )
+  tt_var_done_delta_set_index_map( FT_Face            face,
+                                   GX_DeltaSetIdxMap  deltaSetIdxMap );
+
+
+  FT_LOCAL( FT_Error )
+  tt_get_var_blend( FT_Face      face,
+                    FT_UInt     *num_coords,
+                    FT_Fixed*   *coords,
+                    FT_Fixed*   *normalizedcoords,
+                    FT_MM_Var*  *mm_var );
+
+  FT_LOCAL( void )
+  tt_done_blend( FT_Face  face );
+
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+
+FT_END_HEADER
+
+
+#endif /* TTGXVAR_H_ */
+
+
+/* END */
diff --git a/src/preload/ttinterp.c b/src/preload/ttinterp.c
new file mode 100644
index 000000000..79df4555d
--- /dev/null
+++ b/src/preload/ttinterp.c
@@ -0,0 +1,7753 @@
+/****************************************************************************
+ *
+ * ttinterp.c
+ *
+ *   TrueType bytecode interpreter (body).
+ *
+ * Copyright (C) 1996-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+/* Greg Hitchcock from Microsoft has helped a lot in resolving unclear */
+/* issues; many thanks!                                                */
+
+
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftcalc.h>
+#include <freetype/fttrigon.h>
+#include <freetype/ftsystem.h>
+#include <freetype/ftdriver.h>
+#include <freetype/ftmm.h>
+
+#include "ttinterp.h"
+#include "tterrors.h"
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include "ttgxvar.h"
+#endif
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+
+  /**************************************************************************
+   *
+   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
+   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
+   * messages during execution.
+   */
+#undef  FT_COMPONENT
+#define FT_COMPONENT  ttinterp
+
+
+#define NO_SUBPIXEL_HINTING                                                  \
+          ( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \
+            TT_INTERPRETER_VERSION_35 )
+
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+#define SUBPIXEL_HINTING_MINIMAL                                             \
+          ( ((TT_Driver)FT_FACE_DRIVER( exc->face ))->interpreter_version == \
+            TT_INTERPRETER_VERSION_40 )
+#endif
+
+#define PROJECT( v1, v2 )                                   \
+          exc->func_project( exc,                           \
+                             SUB_LONG( (v1)->x, (v2)->x ),  \
+                             SUB_LONG( (v1)->y, (v2)->y ) )
+
+#define DUALPROJ( v1, v2 )                                   \
+          exc->func_dualproj( exc,                           \
+                              SUB_LONG( (v1)->x, (v2)->x ),  \
+                              SUB_LONG( (v1)->y, (v2)->y ) )
+
+#define FAST_PROJECT( v )                          \
+          exc->func_project( exc, (v)->x, (v)->y )
+
+#define FAST_DUALPROJ( v )                          \
+          exc->func_dualproj( exc, (v)->x, (v)->y )
+
+
+  /**************************************************************************
+   *
+   * Two simple bounds-checking macros.
+   */
+#define BOUNDS( x, n )   ( (FT_UInt)(x)  >= (FT_UInt)(n)  )
+#define BOUNDSL( x, n )  ( (FT_ULong)(x) >= (FT_ULong)(n) )
+
+
+#undef  SUCCESS
+#define SUCCESS  0
+
+#undef  FAILURE
+#define FAILURE  1
+
+
+  /**************************************************************************
+   *
+   *                       CODERANGE FUNCTIONS
+   *
+   */
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Goto_CodeRange
+   *
+   * @Description:
+   *   Switches to a new code range (updates the code related elements in
+   *   `exec', and `IP').
+   *
+   * @Input:
+   *   range ::
+   *     The new execution code range.
+   *
+   *   IP ::
+   *     The new IP in the new code range.
+   *
+   * @InOut:
+   *   exec ::
+   *     The target execution context.
+   */
+  FT_LOCAL_DEF( void )
+  TT_Goto_CodeRange( TT_ExecContext  exec,
+                     FT_Int          range,
+                     FT_Long         IP )
+  {
+    TT_CodeRange*  coderange;
+
+
+    FT_ASSERT( range >= 1 && range <= 3 );
+
+    coderange = &exec->codeRangeTable[range - 1];
+
+    FT_ASSERT( coderange->base );
+
+    /* NOTE: Because the last instruction of a program may be a CALL */
+    /*       which will return to the first byte *after* the code    */
+    /*       range, we test for IP <= Size instead of IP < Size.     */
+    /*                                                               */
+    FT_ASSERT( IP <= coderange->size );
+
+    exec->code     = coderange->base;
+    exec->codeSize = coderange->size;
+    exec->IP       = IP;
+    exec->curRange = range;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Set_CodeRange
+   *
+   * @Description:
+   *   Sets a code range.
+   *
+   * @Input:
+   *   range ::
+   *     The code range index.
+   *
+   *   base ::
+   *     The new code base.
+   *
+   *   length ::
+   *     The range size in bytes.
+   *
+   * @InOut:
+   *   exec ::
+   *     The target execution context.
+   */
+  FT_LOCAL_DEF( void )
+  TT_Set_CodeRange( TT_ExecContext  exec,
+                    FT_Int          range,
+                    void*           base,
+                    FT_Long         length )
+  {
+    FT_ASSERT( range >= 1 && range <= 3 );
+
+    exec->codeRangeTable[range - 1].base = (FT_Byte*)base;
+    exec->codeRangeTable[range - 1].size = length;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Clear_CodeRange
+   *
+   * @Description:
+   *   Clears a code range.
+   *
+   * @Input:
+   *   range ::
+   *     The code range index.
+   *
+   * @InOut:
+   *   exec ::
+   *     The target execution context.
+   */
+  FT_LOCAL_DEF( void )
+  TT_Clear_CodeRange( TT_ExecContext  exec,
+                      FT_Int          range )
+  {
+    FT_ASSERT( range >= 1 && range <= 3 );
+
+    exec->codeRangeTable[range - 1].base = NULL;
+    exec->codeRangeTable[range - 1].size = 0;
+  }
+
+
+  /**************************************************************************
+   *
+   *                  EXECUTION CONTEXT ROUTINES
+   *
+   */
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Done_Context
+   *
+   * @Description:
+   *   Destroys a given context.
+   *
+   * @Input:
+   *   exec ::
+   *     A handle to the target execution context.
+   *
+   *   memory ::
+   *     A handle to the parent memory object.
+   *
+   * @Note:
+   *   Only the glyph loader and debugger should call this function.
+   */
+  FT_LOCAL_DEF( void )
+  TT_Done_Context( TT_ExecContext  exec )
+  {
+    FT_Memory  memory = exec->memory;
+
+
+    /* points zone */
+    exec->maxPoints   = 0;
+    exec->maxContours = 0;
+
+    /* free stack */
+    FT_FREE( exec->stack );
+    exec->stackSize = 0;
+
+    /* free glyf cvt working area */
+    FT_FREE( exec->glyfCvt );
+    exec->glyfCvtSize = 0;
+
+    /* free glyf storage working area */
+    FT_FREE( exec->glyfStorage );
+    exec->glyfStoreSize = 0;
+
+    /* free call stack */
+    FT_FREE( exec->callStack );
+    exec->callSize = 0;
+    exec->callTop  = 0;
+
+    /* free glyph code range */
+    FT_FREE( exec->glyphIns );
+    exec->glyphSize = 0;
+
+    exec->size = NULL;
+    exec->face = NULL;
+
+    FT_FREE( exec );
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Load_Context
+   *
+   * @Description:
+   *   Prepare an execution context for glyph hinting.
+   *
+   * @Input:
+   *   face ::
+   *     A handle to the source face object.
+   *
+   *   size ::
+   *     A handle to the source size object.
+   *
+   * @InOut:
+   *   exec ::
+   *     A handle to the target execution context.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   *
+   * @Note:
+   *   Only the glyph loader and debugger should call this function.
+   *
+   *   Note that not all members of `TT_ExecContext` get initialized.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  TT_Load_Context( TT_ExecContext  exec,
+                   TT_Face         face,
+                   TT_Size         size )
+  {
+    FT_Int          i;
+    TT_MaxProfile*  maxp;
+    FT_Error        error;
+    FT_Memory       memory = exec->memory;
+
+
+    exec->face = face;
+    maxp       = &face->max_profile;
+    exec->size = size;
+
+    if ( size )
+    {
+      exec->numFDefs   = size->num_function_defs;
+      exec->maxFDefs   = size->max_function_defs;
+      exec->numIDefs   = size->num_instruction_defs;
+      exec->maxIDefs   = size->max_instruction_defs;
+      exec->FDefs      = size->function_defs;
+      exec->IDefs      = size->instruction_defs;
+      exec->pointSize  = size->point_size;
+      exec->tt_metrics = size->ttmetrics;
+      exec->metrics    = *size->metrics;
+
+      exec->maxFunc    = size->max_func;
+      exec->maxIns     = size->max_ins;
+
+      for ( i = 0; i < TT_MAX_CODE_RANGES; i++ )
+        exec->codeRangeTable[i] = size->codeRangeTable[i];
+
+      /* set graphics state */
+      exec->GS = size->GS;
+
+      exec->cvtSize = size->cvt_size;
+      exec->cvt     = size->cvt;
+
+      exec->storeSize = size->storage_size;
+      exec->storage   = size->storage;
+
+      exec->twilight  = size->twilight;
+
+      /* In case of multi-threading it can happen that the old size object */
+      /* no longer exists, thus we must clear all glyph zone references.   */
+      FT_ZERO( &exec->zp0 );
+      exec->zp1 = exec->zp0;
+      exec->zp2 = exec->zp0;
+    }
+
+    /* XXX: We reserve a little more elements on the stack to deal safely */
+    /*      with broken fonts like arialbs, courbs, timesbs, etc.         */
+    if ( FT_QRENEW_ARRAY( exec->stack,
+                          exec->stackSize,
+                          maxp->maxStackElements + 32 ) )
+      return error;
+    exec->stackSize = maxp->maxStackElements + 32;
+
+    /* free previous glyph code range */
+    FT_FREE( exec->glyphIns );
+    exec->glyphSize = 0;
+
+    exec->pts.n_points   = 0;
+    exec->pts.n_contours = 0;
+
+    exec->zp1 = exec->pts;
+    exec->zp2 = exec->pts;
+    exec->zp0 = exec->pts;
+
+    exec->instruction_trap = FALSE;
+
+    return FT_Err_Ok;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Save_Context
+   *
+   * @Description:
+   *   Saves the code ranges in a `size' object.
+   *
+   * @Input:
+   *   exec ::
+   *     A handle to the source execution context.
+   *
+   * @InOut:
+   *   size ::
+   *     A handle to the target size object.
+   *
+   * @Note:
+   *   Only the glyph loader and debugger should call this function.
+   */
+  FT_LOCAL_DEF( void )
+  TT_Save_Context( TT_ExecContext  exec,
+                   TT_Size         size )
+  {
+    FT_Int  i;
+
+
+    /* XXX: Will probably disappear soon with all the code range */
+    /*      management, which is now rather obsolete.            */
+    /*                                                           */
+    size->num_function_defs    = exec->numFDefs;
+    size->num_instruction_defs = exec->numIDefs;
+
+    size->max_func = exec->maxFunc;
+    size->max_ins  = exec->maxIns;
+
+    for ( i = 0; i < TT_MAX_CODE_RANGES; i++ )
+      size->codeRangeTable[i] = exec->codeRangeTable[i];
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_Run_Context
+   *
+   * @Description:
+   *   Executes one or more instructions in the execution context.
+   *
+   * @Input:
+   *   exec ::
+   *     A handle to the target execution context.
+   *
+   * @Return:
+   *   TrueType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  TT_Run_Context( TT_ExecContext  exec )
+  {
+    TT_Goto_CodeRange( exec, tt_coderange_glyph, 0 );
+
+    exec->zp0 = exec->pts;
+    exec->zp1 = exec->pts;
+    exec->zp2 = exec->pts;
+
+    exec->GS.gep0 = 1;
+    exec->GS.gep1 = 1;
+    exec->GS.gep2 = 1;
+
+    exec->GS.projVector.x = 0x4000;
+    exec->GS.projVector.y = 0x0000;
+
+    exec->GS.freeVector = exec->GS.projVector;
+    exec->GS.dualVector = exec->GS.projVector;
+
+    exec->GS.round_state = 1;
+    exec->GS.loop        = 1;
+
+    /* some glyphs leave something on the stack. so we clean it */
+    /* before a new execution.                                  */
+    exec->top     = 0;
+    exec->callTop = 0;
+
+    return exec->face->interpreter( exec );
+  }
+
+
+  /* The default value for `scan_control' is documented as FALSE in the */
+  /* TrueType specification.  This is confusing since it implies a      */
+  /* Boolean value.  However, this is not the case, thus both the       */
+  /* default values of our `scan_type' and `scan_control' fields (which */
+  /* the documentation's `scan_control' variable is split into) are     */
+  /* zero.                                                              */
+
+  const TT_GraphicsState  tt_default_graphics_state =
+  {
+    0, 0, 0,
+    { 0x4000, 0 },
+    { 0x4000, 0 },
+    { 0x4000, 0 },
+
+    1, 64, 1,
+    TRUE, 68, 0, 0, 9, 3,
+    0, FALSE, 0, 1, 1, 1
+  };
+
+
+  /* documentation is in ttinterp.h */
+
+  FT_EXPORT_DEF( TT_ExecContext )
+  TT_New_Context( TT_Driver  driver )
+  {
+    FT_Memory  memory;
+    FT_Error   error;
+
+    TT_ExecContext  exec = NULL;
+
+
+    if ( !driver )
+      goto Fail;
+
+    memory = driver->root.root.memory;
+
+    /* allocate object and zero everything inside */
+    if ( FT_NEW( exec ) )
+      goto Fail;
+
+    /* create callStack here, other allocations delayed */
+    exec->memory   = memory;
+    exec->callSize = 32;
+
+    if ( FT_QNEW_ARRAY( exec->callStack, exec->callSize ) )
+      FT_FREE( exec );
+
+  Fail:
+    return exec;
+  }
+
+
+  /**************************************************************************
+   *
+   * Before an opcode is executed, the interpreter verifies that there are
+   * enough arguments on the stack, with the help of the `Pop_Push_Count'
+   * table.
+   *
+   * For each opcode, the first column gives the number of arguments that
+   * are popped from the stack; the second one gives the number of those
+   * that are pushed in result.
+   *
+   * Opcodes which have a varying number of parameters in the data stream
+   * (NPUSHB, NPUSHW) are handled specially; they have a negative value in
+   * the `opcode_length' table, and the value in `Pop_Push_Count' is set
+   * to zero.
+   *
+   */
+
+
+#undef  PACK
+#define PACK( x, y )  ( ( x << 4 ) | y )
+
+
+  static
+  const FT_Byte  Pop_Push_Count[256] =
+  {
+    /* opcodes are gathered in groups of 16 */
+    /* please keep the spaces as they are   */
+
+    /* 0x00 */
+    /*  SVTCA[0]  */  PACK( 0, 0 ),
+    /*  SVTCA[1]  */  PACK( 0, 0 ),
+    /*  SPVTCA[0] */  PACK( 0, 0 ),
+    /*  SPVTCA[1] */  PACK( 0, 0 ),
+    /*  SFVTCA[0] */  PACK( 0, 0 ),
+    /*  SFVTCA[1] */  PACK( 0, 0 ),
+    /*  SPVTL[0]  */  PACK( 2, 0 ),
+    /*  SPVTL[1]  */  PACK( 2, 0 ),
+    /*  SFVTL[0]  */  PACK( 2, 0 ),
+    /*  SFVTL[1]  */  PACK( 2, 0 ),
+    /*  SPVFS     */  PACK( 2, 0 ),
+    /*  SFVFS     */  PACK( 2, 0 ),
+    /*  GPV       */  PACK( 0, 2 ),
+    /*  GFV       */  PACK( 0, 2 ),
+    /*  SFVTPV    */  PACK( 0, 0 ),
+    /*  ISECT     */  PACK( 5, 0 ),
+
+    /* 0x10 */
+    /*  SRP0      */  PACK( 1, 0 ),
+    /*  SRP1      */  PACK( 1, 0 ),
+    /*  SRP2      */  PACK( 1, 0 ),
+    /*  SZP0      */  PACK( 1, 0 ),
+    /*  SZP1      */  PACK( 1, 0 ),
+    /*  SZP2      */  PACK( 1, 0 ),
+    /*  SZPS      */  PACK( 1, 0 ),
+    /*  SLOOP     */  PACK( 1, 0 ),
+    /*  RTG       */  PACK( 0, 0 ),
+    /*  RTHG      */  PACK( 0, 0 ),
+    /*  SMD       */  PACK( 1, 0 ),
+    /*  ELSE      */  PACK( 0, 0 ),
+    /*  JMPR      */  PACK( 1, 0 ),
+    /*  SCVTCI    */  PACK( 1, 0 ),
+    /*  SSWCI     */  PACK( 1, 0 ),
+    /*  SSW       */  PACK( 1, 0 ),
+
+    /* 0x20 */
+    /*  DUP       */  PACK( 1, 2 ),
+    /*  POP       */  PACK( 1, 0 ),
+    /*  CLEAR     */  PACK( 0, 0 ),
+    /*  SWAP      */  PACK( 2, 2 ),
+    /*  DEPTH     */  PACK( 0, 1 ),
+    /*  CINDEX    */  PACK( 1, 1 ),
+    /*  MINDEX    */  PACK( 1, 0 ),
+    /*  ALIGNPTS  */  PACK( 2, 0 ),
+    /*  INS_$28   */  PACK( 0, 0 ),
+    /*  UTP       */  PACK( 1, 0 ),
+    /*  LOOPCALL  */  PACK( 2, 0 ),
+    /*  CALL      */  PACK( 1, 0 ),
+    /*  FDEF      */  PACK( 1, 0 ),
+    /*  ENDF      */  PACK( 0, 0 ),
+    /*  MDAP[0]   */  PACK( 1, 0 ),
+    /*  MDAP[1]   */  PACK( 1, 0 ),
+
+    /* 0x30 */
+    /*  IUP[0]    */  PACK( 0, 0 ),
+    /*  IUP[1]    */  PACK( 0, 0 ),
+    /*  SHP[0]    */  PACK( 0, 0 ), /* loops */
+    /*  SHP[1]    */  PACK( 0, 0 ), /* loops */
+    /*  SHC[0]    */  PACK( 1, 0 ),
+    /*  SHC[1]    */  PACK( 1, 0 ),
+    /*  SHZ[0]    */  PACK( 1, 0 ),
+    /*  SHZ[1]    */  PACK( 1, 0 ),
+    /*  SHPIX     */  PACK( 1, 0 ), /* loops */
+    /*  IP        */  PACK( 0, 0 ), /* loops */
+    /*  MSIRP[0]  */  PACK( 2, 0 ),
+    /*  MSIRP[1]  */  PACK( 2, 0 ),
+    /*  ALIGNRP   */  PACK( 0, 0 ), /* loops */
+    /*  RTDG      */  PACK( 0, 0 ),
+    /*  MIAP[0]   */  PACK( 2, 0 ),
+    /*  MIAP[1]   */  PACK( 2, 0 ),
+
+    /* 0x40 */
+    /*  NPUSHB    */  PACK( 0, 0 ),
+    /*  NPUSHW    */  PACK( 0, 0 ),
+    /*  WS        */  PACK( 2, 0 ),
+    /*  RS        */  PACK( 1, 1 ),
+    /*  WCVTP     */  PACK( 2, 0 ),
+    /*  RCVT      */  PACK( 1, 1 ),
+    /*  GC[0]     */  PACK( 1, 1 ),
+    /*  GC[1]     */  PACK( 1, 1 ),
+    /*  SCFS      */  PACK( 2, 0 ),
+    /*  MD[0]     */  PACK( 2, 1 ),
+    /*  MD[1]     */  PACK( 2, 1 ),
+    /*  MPPEM     */  PACK( 0, 1 ),
+    /*  MPS       */  PACK( 0, 1 ),
+    /*  FLIPON    */  PACK( 0, 0 ),
+    /*  FLIPOFF   */  PACK( 0, 0 ),
+    /*  DEBUG     */  PACK( 1, 0 ),
+
+    /* 0x50 */
+    /*  LT        */  PACK( 2, 1 ),
+    /*  LTEQ      */  PACK( 2, 1 ),
+    /*  GT        */  PACK( 2, 1 ),
+    /*  GTEQ      */  PACK( 2, 1 ),
+    /*  EQ        */  PACK( 2, 1 ),
+    /*  NEQ       */  PACK( 2, 1 ),
+    /*  ODD       */  PACK( 1, 1 ),
+    /*  EVEN      */  PACK( 1, 1 ),
+    /*  IF        */  PACK( 1, 0 ),
+    /*  EIF       */  PACK( 0, 0 ),
+    /*  AND       */  PACK( 2, 1 ),
+    /*  OR        */  PACK( 2, 1 ),
+    /*  NOT       */  PACK( 1, 1 ),
+    /*  DELTAP1   */  PACK( 1, 0 ),
+    /*  SDB       */  PACK( 1, 0 ),
+    /*  SDS       */  PACK( 1, 0 ),
+
+    /* 0x60 */
+    /*  ADD       */  PACK( 2, 1 ),
+    /*  SUB       */  PACK( 2, 1 ),
+    /*  DIV       */  PACK( 2, 1 ),
+    /*  MUL       */  PACK( 2, 1 ),
+    /*  ABS       */  PACK( 1, 1 ),
+    /*  NEG       */  PACK( 1, 1 ),
+    /*  FLOOR     */  PACK( 1, 1 ),
+    /*  CEILING   */  PACK( 1, 1 ),
+    /*  ROUND[0]  */  PACK( 1, 1 ),
+    /*  ROUND[1]  */  PACK( 1, 1 ),
+    /*  ROUND[2]  */  PACK( 1, 1 ),
+    /*  ROUND[3]  */  PACK( 1, 1 ),
+    /*  NROUND[0] */  PACK( 1, 1 ),
+    /*  NROUND[1] */  PACK( 1, 1 ),
+    /*  NROUND[2] */  PACK( 1, 1 ),
+    /*  NROUND[3] */  PACK( 1, 1 ),
+
+    /* 0x70 */
+    /*  WCVTF     */  PACK( 2, 0 ),
+    /*  DELTAP2   */  PACK( 1, 0 ),
+    /*  DELTAP3   */  PACK( 1, 0 ),
+    /*  DELTAC1   */  PACK( 1, 0 ),
+    /*  DELTAC2   */  PACK( 1, 0 ),
+    /*  DELTAC3   */  PACK( 1, 0 ),
+    /*  SROUND    */  PACK( 1, 0 ),
+    /*  S45ROUND  */  PACK( 1, 0 ),
+    /*  JROT      */  PACK( 2, 0 ),
+    /*  JROF      */  PACK( 2, 0 ),
+    /*  ROFF      */  PACK( 0, 0 ),
+    /*  INS_$7B   */  PACK( 0, 0 ),
+    /*  RUTG      */  PACK( 0, 0 ),
+    /*  RDTG      */  PACK( 0, 0 ),
+    /*  SANGW     */  PACK( 1, 0 ),
+    /*  AA        */  PACK( 1, 0 ),
+
+    /* 0x80 */
+    /*  FLIPPT    */  PACK( 0, 0 ), /* loops */
+    /*  FLIPRGON  */  PACK( 2, 0 ),
+    /*  FLIPRGOFF */  PACK( 2, 0 ),
+    /*  INS_$83   */  PACK( 0, 0 ),
+    /*  INS_$84   */  PACK( 0, 0 ),
+    /*  SCANCTRL  */  PACK( 1, 0 ),
+    /*  SDPVTL[0] */  PACK( 2, 0 ),
+    /*  SDPVTL[1] */  PACK( 2, 0 ),
+    /*  GETINFO   */  PACK( 1, 1 ),
+    /*  IDEF      */  PACK( 1, 0 ),
+    /*  ROLL      */  PACK( 3, 3 ),
+    /*  MAX       */  PACK( 2, 1 ),
+    /*  MIN       */  PACK( 2, 1 ),
+    /*  SCANTYPE  */  PACK( 1, 0 ),
+    /*  INSTCTRL  */  PACK( 2, 0 ),
+    /*  INS_$8F   */  PACK( 0, 0 ),
+
+    /* 0x90 */
+    /*  INS_$90  */   PACK( 0, 0 ),
+    /*  GETVAR   */   PACK( 0, 0 ), /* will be handled specially */
+    /*  GETDATA  */   PACK( 0, 1 ),
+    /*  INS_$93  */   PACK( 0, 0 ),
+    /*  INS_$94  */   PACK( 0, 0 ),
+    /*  INS_$95  */   PACK( 0, 0 ),
+    /*  INS_$96  */   PACK( 0, 0 ),
+    /*  INS_$97  */   PACK( 0, 0 ),
+    /*  INS_$98  */   PACK( 0, 0 ),
+    /*  INS_$99  */   PACK( 0, 0 ),
+    /*  INS_$9A  */   PACK( 0, 0 ),
+    /*  INS_$9B  */   PACK( 0, 0 ),
+    /*  INS_$9C  */   PACK( 0, 0 ),
+    /*  INS_$9D  */   PACK( 0, 0 ),
+    /*  INS_$9E  */   PACK( 0, 0 ),
+    /*  INS_$9F  */   PACK( 0, 0 ),
+
+    /* 0xA0 */
+    /*  INS_$A0  */   PACK( 0, 0 ),
+    /*  INS_$A1  */   PACK( 0, 0 ),
+    /*  INS_$A2  */   PACK( 0, 0 ),
+    /*  INS_$A3  */   PACK( 0, 0 ),
+    /*  INS_$A4  */   PACK( 0, 0 ),
+    /*  INS_$A5  */   PACK( 0, 0 ),
+    /*  INS_$A6  */   PACK( 0, 0 ),
+    /*  INS_$A7  */   PACK( 0, 0 ),
+    /*  INS_$A8  */   PACK( 0, 0 ),
+    /*  INS_$A9  */   PACK( 0, 0 ),
+    /*  INS_$AA  */   PACK( 0, 0 ),
+    /*  INS_$AB  */   PACK( 0, 0 ),
+    /*  INS_$AC  */   PACK( 0, 0 ),
+    /*  INS_$AD  */   PACK( 0, 0 ),
+    /*  INS_$AE  */   PACK( 0, 0 ),
+    /*  INS_$AF  */   PACK( 0, 0 ),
+
+    /* 0xB0 */
+    /*  PUSHB[0]  */  PACK( 0, 1 ),
+    /*  PUSHB[1]  */  PACK( 0, 2 ),
+    /*  PUSHB[2]  */  PACK( 0, 3 ),
+    /*  PUSHB[3]  */  PACK( 0, 4 ),
+    /*  PUSHB[4]  */  PACK( 0, 5 ),
+    /*  PUSHB[5]  */  PACK( 0, 6 ),
+    /*  PUSHB[6]  */  PACK( 0, 7 ),
+    /*  PUSHB[7]  */  PACK( 0, 8 ),
+    /*  PUSHW[0]  */  PACK( 0, 1 ),
+    /*  PUSHW[1]  */  PACK( 0, 2 ),
+    /*  PUSHW[2]  */  PACK( 0, 3 ),
+    /*  PUSHW[3]  */  PACK( 0, 4 ),
+    /*  PUSHW[4]  */  PACK( 0, 5 ),
+    /*  PUSHW[5]  */  PACK( 0, 6 ),
+    /*  PUSHW[6]  */  PACK( 0, 7 ),
+    /*  PUSHW[7]  */  PACK( 0, 8 ),
+
+    /* 0xC0 */
+    /*  MDRP[00]  */  PACK( 1, 0 ),
+    /*  MDRP[01]  */  PACK( 1, 0 ),
+    /*  MDRP[02]  */  PACK( 1, 0 ),
+    /*  MDRP[03]  */  PACK( 1, 0 ),
+    /*  MDRP[04]  */  PACK( 1, 0 ),
+    /*  MDRP[05]  */  PACK( 1, 0 ),
+    /*  MDRP[06]  */  PACK( 1, 0 ),
+    /*  MDRP[07]  */  PACK( 1, 0 ),
+    /*  MDRP[08]  */  PACK( 1, 0 ),
+    /*  MDRP[09]  */  PACK( 1, 0 ),
+    /*  MDRP[10]  */  PACK( 1, 0 ),
+    /*  MDRP[11]  */  PACK( 1, 0 ),
+    /*  MDRP[12]  */  PACK( 1, 0 ),
+    /*  MDRP[13]  */  PACK( 1, 0 ),
+    /*  MDRP[14]  */  PACK( 1, 0 ),
+    /*  MDRP[15]  */  PACK( 1, 0 ),
+
+    /* 0xD0 */
+    /*  MDRP[16]  */  PACK( 1, 0 ),
+    /*  MDRP[17]  */  PACK( 1, 0 ),
+    /*  MDRP[18]  */  PACK( 1, 0 ),
+    /*  MDRP[19]  */  PACK( 1, 0 ),
+    /*  MDRP[20]  */  PACK( 1, 0 ),
+    /*  MDRP[21]  */  PACK( 1, 0 ),
+    /*  MDRP[22]  */  PACK( 1, 0 ),
+    /*  MDRP[23]  */  PACK( 1, 0 ),
+    /*  MDRP[24]  */  PACK( 1, 0 ),
+    /*  MDRP[25]  */  PACK( 1, 0 ),
+    /*  MDRP[26]  */  PACK( 1, 0 ),
+    /*  MDRP[27]  */  PACK( 1, 0 ),
+    /*  MDRP[28]  */  PACK( 1, 0 ),
+    /*  MDRP[29]  */  PACK( 1, 0 ),
+    /*  MDRP[30]  */  PACK( 1, 0 ),
+    /*  MDRP[31]  */  PACK( 1, 0 ),
+
+    /* 0xE0 */
+    /*  MIRP[00]  */  PACK( 2, 0 ),
+    /*  MIRP[01]  */  PACK( 2, 0 ),
+    /*  MIRP[02]  */  PACK( 2, 0 ),
+    /*  MIRP[03]  */  PACK( 2, 0 ),
+    /*  MIRP[04]  */  PACK( 2, 0 ),
+    /*  MIRP[05]  */  PACK( 2, 0 ),
+    /*  MIRP[06]  */  PACK( 2, 0 ),
+    /*  MIRP[07]  */  PACK( 2, 0 ),
+    /*  MIRP[08]  */  PACK( 2, 0 ),
+    /*  MIRP[09]  */  PACK( 2, 0 ),
+    /*  MIRP[10]  */  PACK( 2, 0 ),
+    /*  MIRP[11]  */  PACK( 2, 0 ),
+    /*  MIRP[12]  */  PACK( 2, 0 ),
+    /*  MIRP[13]  */  PACK( 2, 0 ),
+    /*  MIRP[14]  */  PACK( 2, 0 ),
+    /*  MIRP[15]  */  PACK( 2, 0 ),
+
+    /* 0xF0 */
+    /*  MIRP[16]  */  PACK( 2, 0 ),
+    /*  MIRP[17]  */  PACK( 2, 0 ),
+    /*  MIRP[18]  */  PACK( 2, 0 ),
+    /*  MIRP[19]  */  PACK( 2, 0 ),
+    /*  MIRP[20]  */  PACK( 2, 0 ),
+    /*  MIRP[21]  */  PACK( 2, 0 ),
+    /*  MIRP[22]  */  PACK( 2, 0 ),
+    /*  MIRP[23]  */  PACK( 2, 0 ),
+    /*  MIRP[24]  */  PACK( 2, 0 ),
+    /*  MIRP[25]  */  PACK( 2, 0 ),
+    /*  MIRP[26]  */  PACK( 2, 0 ),
+    /*  MIRP[27]  */  PACK( 2, 0 ),
+    /*  MIRP[28]  */  PACK( 2, 0 ),
+    /*  MIRP[29]  */  PACK( 2, 0 ),
+    /*  MIRP[30]  */  PACK( 2, 0 ),
+    /*  MIRP[31]  */  PACK( 2, 0 )
+  };
+
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+
+  /* the first hex digit gives the length of the opcode name; the space */
+  /* after the digit is here just to increase readability of the source */
+  /* code                                                               */
+
+  static
+  const char*  const opcode_name[256] =
+  {
+    /* 0x00 */
+    "8 SVTCA[y]",
+    "8 SVTCA[x]",
+    "9 SPVTCA[y]",
+    "9 SPVTCA[x]",
+    "9 SFVTCA[y]",
+    "9 SFVTCA[x]",
+    "9 SPVTL[||]",
+    "8 SPVTL[+]",
+    "9 SFVTL[||]",
+    "8 SFVTL[+]",
+    "5 SPVFS",
+    "5 SFVFS",
+    "3 GPV",
+    "3 GFV",
+    "6 SFVTPV",
+    "5 ISECT",
+
+    /* 0x10 */
+    "4 SRP0",
+    "4 SRP1",
+    "4 SRP2",
+    "4 SZP0",
+    "4 SZP1",
+    "4 SZP2",
+    "4 SZPS",
+    "5 SLOOP",
+    "3 RTG",
+    "4 RTHG",
+    "3 SMD",
+    "4 ELSE",
+    "4 JMPR",
+    "6 SCVTCI",
+    "5 SSWCI",
+    "3 SSW",
+
+    /* 0x20 */
+    "3 DUP",
+    "3 POP",
+    "5 CLEAR",
+    "4 SWAP",
+    "5 DEPTH",
+    "6 CINDEX",
+    "6 MINDEX",
+    "8 ALIGNPTS",
+    "7 INS_$28",
+    "3 UTP",
+    "8 LOOPCALL",
+    "4 CALL",
+    "4 FDEF",
+    "4 ENDF",
+    "6 MDAP[]",
+    "9 MDAP[rnd]",
+
+    /* 0x30 */
+    "6 IUP[y]",
+    "6 IUP[x]",
+    "8 SHP[rp2]",
+    "8 SHP[rp1]",
+    "8 SHC[rp2]",
+    "8 SHC[rp1]",
+    "8 SHZ[rp2]",
+    "8 SHZ[rp1]",
+    "5 SHPIX",
+    "2 IP",
+    "7 MSIRP[]",
+    "A MSIRP[rp0]",
+    "7 ALIGNRP",
+    "4 RTDG",
+    "6 MIAP[]",
+    "9 MIAP[rnd]",
+
+    /* 0x40 */
+    "6 NPUSHB",
+    "6 NPUSHW",
+    "2 WS",
+    "2 RS",
+    "5 WCVTP",
+    "4 RCVT",
+    "8 GC[curr]",
+    "8 GC[orig]",
+    "4 SCFS",
+    "8 MD[curr]",
+    "8 MD[orig]",
+    "5 MPPEM",
+    "3 MPS",
+    "6 FLIPON",
+    "7 FLIPOFF",
+    "5 DEBUG",
+
+    /* 0x50 */
+    "2 LT",
+    "4 LTEQ",
+    "2 GT",
+    "4 GTEQ",
+    "2 EQ",
+    "3 NEQ",
+    "3 ODD",
+    "4 EVEN",
+    "2 IF",
+    "3 EIF",
+    "3 AND",
+    "2 OR",
+    "3 NOT",
+    "7 DELTAP1",
+    "3 SDB",
+    "3 SDS",
+
+    /* 0x60 */
+    "3 ADD",
+    "3 SUB",
+    "3 DIV",
+    "3 MUL",
+    "3 ABS",
+    "3 NEG",
+    "5 FLOOR",
+    "7 CEILING",
+    "8 ROUND[G]",
+    "8 ROUND[B]",
+    "8 ROUND[W]",
+    "7 ROUND[]",
+    "9 NROUND[G]",
+    "9 NROUND[B]",
+    "9 NROUND[W]",
+    "8 NROUND[]",
+
+    /* 0x70 */
+    "5 WCVTF",
+    "7 DELTAP2",
+    "7 DELTAP3",
+    "7 DELTAC1",
+    "7 DELTAC2",
+    "7 DELTAC3",
+    "6 SROUND",
+    "8 S45ROUND",
+    "4 JROT",
+    "4 JROF",
+    "4 ROFF",
+    "7 INS_$7B",
+    "4 RUTG",
+    "4 RDTG",
+    "5 SANGW",
+    "2 AA",
+
+    /* 0x80 */
+    "6 FLIPPT",
+    "8 FLIPRGON",
+    "9 FLIPRGOFF",
+    "7 INS_$83",
+    "7 INS_$84",
+    "8 SCANCTRL",
+    "A SDPVTL[||]",
+    "9 SDPVTL[+]",
+    "7 GETINFO",
+    "4 IDEF",
+    "4 ROLL",
+    "3 MAX",
+    "3 MIN",
+    "8 SCANTYPE",
+    "8 INSTCTRL",
+    "7 INS_$8F",
+
+    /* 0x90 */
+    "7 INS_$90",
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+    "C GETVARIATION",
+    "7 GETDATA",
+#else
+    "7 INS_$91",
+    "7 INS_$92",
+#endif
+    "7 INS_$93",
+    "7 INS_$94",
+    "7 INS_$95",
+    "7 INS_$96",
+    "7 INS_$97",
+    "7 INS_$98",
+    "7 INS_$99",
+    "7 INS_$9A",
+    "7 INS_$9B",
+    "7 INS_$9C",
+    "7 INS_$9D",
+    "7 INS_$9E",
+    "7 INS_$9F",
+
+    /* 0xA0 */
+    "7 INS_$A0",
+    "7 INS_$A1",
+    "7 INS_$A2",
+    "7 INS_$A3",
+    "7 INS_$A4",
+    "7 INS_$A5",
+    "7 INS_$A6",
+    "7 INS_$A7",
+    "7 INS_$A8",
+    "7 INS_$A9",
+    "7 INS_$AA",
+    "7 INS_$AB",
+    "7 INS_$AC",
+    "7 INS_$AD",
+    "7 INS_$AE",
+    "7 INS_$AF",
+
+    /* 0xB0 */
+    "8 PUSHB[0]",
+    "8 PUSHB[1]",
+    "8 PUSHB[2]",
+    "8 PUSHB[3]",
+    "8 PUSHB[4]",
+    "8 PUSHB[5]",
+    "8 PUSHB[6]",
+    "8 PUSHB[7]",
+    "8 PUSHW[0]",
+    "8 PUSHW[1]",
+    "8 PUSHW[2]",
+    "8 PUSHW[3]",
+    "8 PUSHW[4]",
+    "8 PUSHW[5]",
+    "8 PUSHW[6]",
+    "8 PUSHW[7]",
+
+    /* 0xC0 */
+    "7 MDRP[G]",
+    "7 MDRP[B]",
+    "7 MDRP[W]",
+    "6 MDRP[]",
+    "8 MDRP[rG]",
+    "8 MDRP[rB]",
+    "8 MDRP[rW]",
+    "7 MDRP[r]",
+    "8 MDRP[mG]",
+    "8 MDRP[mB]",
+    "8 MDRP[mW]",
+    "7 MDRP[m]",
+    "9 MDRP[mrG]",
+    "9 MDRP[mrB]",
+    "9 MDRP[mrW]",
+    "8 MDRP[mr]",
+
+    /* 0xD0 */
+    "8 MDRP[pG]",
+    "8 MDRP[pB]",
+    "8 MDRP[pW]",
+    "7 MDRP[p]",
+    "9 MDRP[prG]",
+    "9 MDRP[prB]",
+    "9 MDRP[prW]",
+    "8 MDRP[pr]",
+    "9 MDRP[pmG]",
+    "9 MDRP[pmB]",
+    "9 MDRP[pmW]",
+    "8 MDRP[pm]",
+    "A MDRP[pmrG]",
+    "A MDRP[pmrB]",
+    "A MDRP[pmrW]",
+    "9 MDRP[pmr]",
+
+    /* 0xE0 */
+    "7 MIRP[G]",
+    "7 MIRP[B]",
+    "7 MIRP[W]",
+    "6 MIRP[]",
+    "8 MIRP[rG]",
+    "8 MIRP[rB]",
+    "8 MIRP[rW]",
+    "7 MIRP[r]",
+    "8 MIRP[mG]",
+    "8 MIRP[mB]",
+    "8 MIRP[mW]",
+    "7 MIRP[m]",
+    "9 MIRP[mrG]",
+    "9 MIRP[mrB]",
+    "9 MIRP[mrW]",
+    "8 MIRP[mr]",
+
+    /* 0xF0 */
+    "8 MIRP[pG]",
+    "8 MIRP[pB]",
+    "8 MIRP[pW]",
+    "7 MIRP[p]",
+    "9 MIRP[prG]",
+    "9 MIRP[prB]",
+    "9 MIRP[prW]",
+    "8 MIRP[pr]",
+    "9 MIRP[pmG]",
+    "9 MIRP[pmB]",
+    "9 MIRP[pmW]",
+    "8 MIRP[pm]",
+    "A MIRP[pmrG]",
+    "A MIRP[pmrB]",
+    "A MIRP[pmrW]",
+    "9 MIRP[pmr]"
+  };
+
+#endif /* FT_DEBUG_LEVEL_TRACE */
+
+
+  static
+  const FT_Char  opcode_length[256] =
+  {
+    1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
+    1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
+    1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
+    1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
+
+   -1,-2, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
+    1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
+    1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
+    1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
+
+    1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
+    1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
+    1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
+    2, 3, 4, 5,  6, 7, 8, 9,  3, 5, 7, 9, 11,13,15,17,
+
+    1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
+    1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
+    1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,
+    1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1,  1, 1, 1, 1
+  };
+
+#undef PACK
+
+
+#ifndef FT_CONFIG_OPTION_NO_ASSEMBLER
+
+#if defined( __arm__ )                                 && \
+    ( defined( __thumb2__ ) || !defined( __thumb__ ) )
+
+#define TT_MulFix14  TT_MulFix14_arm
+
+  static FT_Int32
+  TT_MulFix14_arm( FT_Int32  a,
+                   FT_Int    b )
+  {
+    FT_Int32  t, t2;
+
+
+#if defined( __CC_ARM ) || defined( __ARMCC__ )
+
+    __asm
+    {
+      smull t2, t,  b,  a           /* (lo=t2,hi=t) = a*b */
+      mov   a,  t,  asr #31         /* a   = (hi >> 31) */
+      add   a,  a,  #0x2000         /* a  += 0x2000 */
+      adds  t2, t2, a               /* t2 += a */
+      adc   t,  t,  #0              /* t  += carry */
+      mov   a,  t2, lsr #14         /* a   = t2 >> 14 */
+      orr   a,  a,  t,  lsl #18     /* a  |= t << 18 */
+    }
+
+#elif defined( __GNUC__ )
+
+    __asm__ __volatile__ (
+      "smull  %1, %2, %4, %3\n\t"       /* (lo=%1,hi=%2) = a*b */
+      "mov    %0, %2, asr #31\n\t"      /* %0  = (hi >> 31) */
+#if defined( __clang__ ) && defined( __thumb2__ )
+      "add.w  %0, %0, #0x2000\n\t"      /* %0 += 0x2000 */
+#else
+      "add    %0, %0, #0x2000\n\t"      /* %0 += 0x2000 */
+#endif
+      "adds   %1, %1, %0\n\t"           /* %1 += %0 */
+      "adc    %2, %2, #0\n\t"           /* %2 += carry */
+      "mov    %0, %1, lsr #14\n\t"      /* %0  = %1 >> 16 */
+      "orr    %0, %0, %2, lsl #18\n\t"  /* %0 |= %2 << 16 */
+      : "=r"(a), "=&r"(t2), "=&r"(t)
+      : "r"(a), "r"(b)
+      : "cc" );
+
+#endif
+
+    return a;
+  }
+
+#endif /* __arm__ && ( __thumb2__ || !__thumb__ ) */
+
+#endif /* !FT_CONFIG_OPTION_NO_ASSEMBLER */
+
+
+#if defined( __GNUC__ )                              && \
+    ( defined( __i386__ ) || defined( __x86_64__ ) )
+
+#define TT_MulFix14  TT_MulFix14_long_long
+
+  /* Temporarily disable the warning that C90 doesn't support `long long'. */
+#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406
+#pragma GCC diagnostic push
+#endif
+#pragma GCC diagnostic ignored "-Wlong-long"
+
+  /* This is declared `noinline' because inlining the function results */
+  /* in slower code.  The `pure' attribute indicates that the result   */
+  /* only depends on the parameters.                                   */
+  static __attribute__(( noinline ))
+         __attribute__(( pure )) FT_Int32
+  TT_MulFix14_long_long( FT_Int32  a,
+                         FT_Int    b )
+  {
+
+    long long  ret = (long long)a * b;
+
+    /* The following line assumes that right shifting of signed values */
+    /* will actually preserve the sign bit.  The exact behaviour is    */
+    /* undefined, but this is true on x86 and x86_64.                  */
+    long long  tmp = ret >> 63;
+
+
+    ret += 0x2000 + tmp;
+
+    return (FT_Int32)( ret >> 14 );
+  }
+
+#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406
+#pragma GCC diagnostic pop
+#endif
+
+#endif /* __GNUC__ && ( __i386__ || __x86_64__ ) */
+
+
+#ifndef TT_MulFix14
+
+  /* Compute (a*b)/2^14 with maximum accuracy and rounding.  */
+  /* This is optimized to be faster than calling FT_MulFix() */
+  /* for platforms where sizeof(int) == 2.                   */
+  static FT_Int32
+  TT_MulFix14( FT_Int32  a,
+               FT_Int    b )
+  {
+    FT_Int32   sign;
+    FT_UInt32  ah, al, mid, lo, hi;
+
+
+    sign = a ^ b;
+
+    if ( a < 0 )
+      a = -a;
+    if ( b < 0 )
+      b = -b;
+
+    ah = (FT_UInt32)( ( a >> 16 ) & 0xFFFFU );
+    al = (FT_UInt32)( a & 0xFFFFU );
+
+    lo    = al * b;
+    mid   = ah * b;
+    hi    = mid >> 16;
+    mid   = ( mid << 16 ) + ( 1 << 13 ); /* rounding */
+    lo   += mid;
+    if ( lo < mid )
+      hi += 1;
+
+    mid = ( lo >> 14 ) | ( hi << 18 );
+
+    return sign >= 0 ? (FT_Int32)mid : -(FT_Int32)mid;
+  }
+
+#endif  /* !TT_MulFix14 */
+
+
+#if defined( __GNUC__ )        && \
+    ( defined( __i386__ )   ||    \
+      defined( __x86_64__ ) ||    \
+      defined( __arm__ )    )
+
+#define TT_DotFix14  TT_DotFix14_long_long
+
+#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406
+#pragma GCC diagnostic push
+#endif
+#pragma GCC diagnostic ignored "-Wlong-long"
+
+  static __attribute__(( pure )) FT_Int32
+  TT_DotFix14_long_long( FT_Int32  ax,
+                         FT_Int32  ay,
+                         FT_Int    bx,
+                         FT_Int    by )
+  {
+    /* Temporarily disable the warning that C90 doesn't support */
+    /* `long long'.                                             */
+
+    long long  temp1 = (long long)ax * bx;
+    long long  temp2 = (long long)ay * by;
+
+
+    temp1 += temp2;
+    temp2  = temp1 >> 63;
+    temp1 += 0x2000 + temp2;
+
+    return (FT_Int32)( temp1 >> 14 );
+
+  }
+
+#if ( __GNUC__ * 100 + __GNUC_MINOR__ ) >= 406
+#pragma GCC diagnostic pop
+#endif
+
+#endif /* __GNUC__ && (__arm__ || __i386__ || __x86_64__) */
+
+
+#ifndef TT_DotFix14
+
+  /* compute (ax*bx+ay*by)/2^14 with maximum accuracy and rounding */
+  static FT_Int32
+  TT_DotFix14( FT_Int32  ax,
+               FT_Int32  ay,
+               FT_Int    bx,
+               FT_Int    by )
+  {
+    FT_Int32   m, s, hi1, hi2, hi;
+    FT_UInt32  l, lo1, lo2, lo;
+
+
+    /* compute ax*bx as 64-bit value */
+    l = (FT_UInt32)( ( ax & 0xFFFFU ) * bx );
+    m = ( ax >> 16 ) * bx;
+
+    lo1 = l + ( (FT_UInt32)m << 16 );
+    hi1 = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo1 < l );
+
+    /* compute ay*by as 64-bit value */
+    l = (FT_UInt32)( ( ay & 0xFFFFU ) * by );
+    m = ( ay >> 16 ) * by;
+
+    lo2 = l + ( (FT_UInt32)m << 16 );
+    hi2 = ( m >> 16 ) + ( (FT_Int32)l >> 31 ) + ( lo2 < l );
+
+    /* add them */
+    lo = lo1 + lo2;
+    hi = hi1 + hi2 + ( lo < lo1 );
+
+    /* divide the result by 2^14 with rounding */
+    s   = hi >> 31;
+    l   = lo + (FT_UInt32)s;
+    hi += s + ( l < lo );
+    lo  = l;
+
+    l   = lo + 0x2000U;
+    hi += ( l < lo );
+
+    return (FT_Int32)( ( (FT_UInt32)hi << 18 ) | ( l >> 14 ) );
+  }
+
+#endif /* TT_DotFix14 */
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Current_Ratio
+   *
+   * @Description:
+   *   Returns the current aspect ratio scaling factor depending on the
+   *   projection vector's state and device resolutions.
+   *
+   * @Return:
+   *   The aspect ratio in 16.16 format, always <= 1.0 .
+   */
+  static FT_Long
+  Current_Ratio( TT_ExecContext  exc )
+  {
+    if ( !exc->tt_metrics.ratio )
+    {
+      if ( exc->GS.projVector.y == 0 )
+        exc->tt_metrics.ratio = exc->tt_metrics.x_ratio;
+
+      else if ( exc->GS.projVector.x == 0 )
+        exc->tt_metrics.ratio = exc->tt_metrics.y_ratio;
+
+      else
+      {
+        FT_F26Dot6  x, y;
+
+
+        x = TT_MulFix14( exc->tt_metrics.x_ratio,
+                         exc->GS.projVector.x );
+        y = TT_MulFix14( exc->tt_metrics.y_ratio,
+                         exc->GS.projVector.y );
+        exc->tt_metrics.ratio = FT_Hypot( x, y );
+      }
+    }
+    return exc->tt_metrics.ratio;
+  }
+
+
+  FT_CALLBACK_DEF( FT_Long )
+  Current_Ppem( TT_ExecContext  exc )
+  {
+    return exc->tt_metrics.ppem;
+  }
+
+
+  FT_CALLBACK_DEF( FT_Long )
+  Current_Ppem_Stretched( TT_ExecContext  exc )
+  {
+    return FT_MulFix( exc->tt_metrics.ppem, Current_Ratio( exc ) );
+  }
+
+
+  /**************************************************************************
+   *
+   * Functions related to the control value table (CVT).
+   *
+   */
+
+
+  FT_CALLBACK_DEF( FT_F26Dot6 )
+  Read_CVT( TT_ExecContext  exc,
+            FT_ULong        idx )
+  {
+    return exc->cvt[idx];
+  }
+
+
+  FT_CALLBACK_DEF( FT_F26Dot6 )
+  Read_CVT_Stretched( TT_ExecContext  exc,
+                      FT_ULong        idx )
+  {
+    return FT_MulFix( exc->cvt[idx], Current_Ratio( exc ) );
+  }
+
+
+  static void
+  Modify_CVT_Check( TT_ExecContext  exc )
+  {
+    if ( exc->iniRange == tt_coderange_glyph &&
+         exc->cvt != exc->glyfCvt            )
+    {
+      FT_Memory  memory = exc->memory;
+      FT_Error   error;
+
+
+      FT_MEM_QRENEW_ARRAY( exc->glyfCvt, exc->glyfCvtSize, exc->cvtSize );
+      exc->error = error;
+      if ( error )
+        return;
+
+      exc->glyfCvtSize = exc->cvtSize;
+      FT_ARRAY_COPY( exc->glyfCvt, exc->cvt, exc->glyfCvtSize );
+      exc->cvt = exc->glyfCvt;
+    }
+  }
+
+
+  FT_CALLBACK_DEF( void )
+  Write_CVT( TT_ExecContext  exc,
+             FT_ULong        idx,
+             FT_F26Dot6      value )
+  {
+    Modify_CVT_Check( exc );
+    if ( exc->error )
+      return;
+
+    exc->cvt[idx] = value;
+  }
+
+
+  FT_CALLBACK_DEF( void )
+  Write_CVT_Stretched( TT_ExecContext  exc,
+                       FT_ULong        idx,
+                       FT_F26Dot6      value )
+  {
+    Modify_CVT_Check( exc );
+    if ( exc->error )
+      return;
+
+    exc->cvt[idx] = FT_DivFix( value, Current_Ratio( exc ) );
+  }
+
+
+  FT_CALLBACK_DEF( void )
+  Move_CVT( TT_ExecContext  exc,
+            FT_ULong        idx,
+            FT_F26Dot6      value )
+  {
+    Modify_CVT_Check( exc );
+    if ( exc->error )
+      return;
+
+    exc->cvt[idx] = ADD_LONG( exc->cvt[idx], value );
+  }
+
+
+  FT_CALLBACK_DEF( void )
+  Move_CVT_Stretched( TT_ExecContext  exc,
+                      FT_ULong        idx,
+                      FT_F26Dot6      value )
+  {
+    Modify_CVT_Check( exc );
+    if ( exc->error )
+      return;
+
+    exc->cvt[idx] = ADD_LONG( exc->cvt[idx],
+                              FT_DivFix( value, Current_Ratio( exc ) ) );
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   GetShortIns
+   *
+   * @Description:
+   *   Returns a short integer taken from the instruction stream at
+   *   address IP.
+   *
+   * @Return:
+   *   Short read at code[IP].
+   *
+   * @Note:
+   *   This one could become a macro.
+   */
+  static FT_Short
+  GetShortIns( TT_ExecContext  exc )
+  {
+    /* Reading a byte stream so there is no endianness (DaveP) */
+    exc->IP += 2;
+    return (FT_Short)( ( exc->code[exc->IP - 2] << 8 ) +
+                         exc->code[exc->IP - 1]      );
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Ins_Goto_CodeRange
+   *
+   * @Description:
+   *   Goes to a certain code range in the instruction stream.
+   *
+   * @Input:
+   *   aRange ::
+   *     The index of the code range.
+   *
+   *   aIP ::
+   *     The new IP address in the code range.
+   *
+   * @Return:
+   *   SUCCESS or FAILURE.
+   */
+  static FT_Bool
+  Ins_Goto_CodeRange( TT_ExecContext  exc,
+                      FT_Int          aRange,
+                      FT_Long         aIP )
+  {
+    TT_CodeRange*  range;
+
+
+    if ( aRange < 1 || aRange > 3 )
+    {
+      exc->error = FT_THROW( Bad_Argument );
+      return FAILURE;
+    }
+
+    range = &exc->codeRangeTable[aRange - 1];
+
+    if ( !range->base )     /* invalid coderange */
+    {
+      exc->error = FT_THROW( Invalid_CodeRange );
+      return FAILURE;
+    }
+
+    /* NOTE: Because the last instruction of a program may be a CALL */
+    /*       which will return to the first byte *after* the code    */
+    /*       range, we test for aIP <= Size, instead of aIP < Size.  */
+
+    if ( aIP > range->size )
+    {
+      exc->error = FT_THROW( Code_Overflow );
+      return FAILURE;
+    }
+
+    exc->code     = range->base;
+    exc->codeSize = range->size;
+    exc->IP       = aIP;
+    exc->curRange = aRange;
+
+    return SUCCESS;
+  }
+
+
+  /*
+   *
+   * Apple's TrueType specification at
+   *
+   *   
https://developer.apple.com/fonts/TrueType-Reference-Manual/RM02/Chap2.html#order
+   *
+   * gives the following order of operations in instructions that move
+   * points.
+   *
+   *   - check single width cut-in (MIRP, MDRP)
+   *
+   *   - check control value cut-in (MIRP, MIAP)
+   *
+   *   - apply engine compensation (MIRP, MDRP)
+   *
+   *   - round distance (MIRP, MDRP) or value (MIAP, MDAP)
+   *
+   *   - check minimum distance (MIRP,MDRP)
+   *
+   *   - move point (MIRP, MDRP, MIAP, MSIRP, MDAP)
+   *
+   * For rounding instructions, engine compensation happens before rounding.
+   *
+   */
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Direct_Move
+   *
+   * @Description:
+   *   Moves a point by a given distance along the freedom vector.  The
+   *   point will be `touched'.
+   *
+   * @Input:
+   *   point ::
+   *     The index of the point to move.
+   *
+   *   distance ::
+   *     The distance to apply.
+   *
+   * @InOut:
+   *   zone ::
+   *     The affected glyph zone.
+   *
+   * @Note:
+   *   See `ttinterp.h' for details on backward compatibility mode.
+   *   `Touches' the point.
+   */
+  static void
+  Direct_Move( TT_ExecContext  exc,
+               TT_GlyphZone    zone,
+               FT_UShort       point,
+               FT_F26Dot6      distance )
+  {
+    FT_F26Dot6  v;
+
+
+    v = exc->GS.freeVector.x;
+
+    if ( v != 0 )
+    {
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+      /* Exception to the post-IUP curfew: Allow the x component of */
+      /* diagonal moves, but only post-IUP.  DejaVu tries to adjust */
+      /* diagonal stems like on `Z' and `z' post-IUP.               */
+      if ( SUBPIXEL_HINTING_MINIMAL && !exc->backward_compatibility )
+        zone->cur[point].x = ADD_LONG( zone->cur[point].x,
+                                       FT_MulDiv( distance,
+                                                  v,
+                                                  exc->F_dot_P ) );
+      else
+#endif
+
+      if ( NO_SUBPIXEL_HINTING )
+        zone->cur[point].x = ADD_LONG( zone->cur[point].x,
+                                       FT_MulDiv( distance,
+                                                  v,
+                                                  exc->F_dot_P ) );
+
+      zone->tags[point] |= FT_CURVE_TAG_TOUCH_X;
+    }
+
+    v = exc->GS.freeVector.y;
+
+    if ( v != 0 )
+    {
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+      if ( !( SUBPIXEL_HINTING_MINIMAL    &&
+              exc->backward_compatibility &&
+              exc->iupx_called            &&
+              exc->iupy_called            ) )
+#endif
+        zone->cur[point].y = ADD_LONG( zone->cur[point].y,
+                                       FT_MulDiv( distance,
+                                                  v,
+                                                  exc->F_dot_P ) );
+
+      zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y;
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Direct_Move_Orig
+   *
+   * @Description:
+   *   Moves the *original* position of a point by a given distance along
+   *   the freedom vector.  Obviously, the point will not be `touched'.
+   *
+   * @Input:
+   *   point ::
+   *     The index of the point to move.
+   *
+   *   distance ::
+   *     The distance to apply.
+   *
+   * @InOut:
+   *   zone ::
+   *     The affected glyph zone.
+   */
+  static void
+  Direct_Move_Orig( TT_ExecContext  exc,
+                    TT_GlyphZone    zone,
+                    FT_UShort       point,
+                    FT_F26Dot6      distance )
+  {
+    FT_F26Dot6  v;
+
+
+    v = exc->GS.freeVector.x;
+
+    if ( v != 0 )
+      zone->org[point].x = ADD_LONG( zone->org[point].x,
+                                     FT_MulDiv( distance,
+                                                v,
+                                                exc->F_dot_P ) );
+
+    v = exc->GS.freeVector.y;
+
+    if ( v != 0 )
+      zone->org[point].y = ADD_LONG( zone->org[point].y,
+                                     FT_MulDiv( distance,
+                                                v,
+                                                exc->F_dot_P ) );
+  }
+
+
+  /**************************************************************************
+   *
+   * Special versions of Direct_Move()
+   *
+   *   The following versions are used whenever both vectors are both
+   *   along one of the coordinate unit vectors, i.e. in 90% of the cases.
+   *   See `ttinterp.h' for details on backward compatibility mode.
+   *
+   */
+
+
+  static void
+  Direct_Move_X( TT_ExecContext  exc,
+                 TT_GlyphZone    zone,
+                 FT_UShort       point,
+                 FT_F26Dot6      distance )
+  {
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    if ( SUBPIXEL_HINTING_MINIMAL && !exc->backward_compatibility )
+      zone->cur[point].x = ADD_LONG( zone->cur[point].x, distance );
+    else
+#endif
+
+    if ( NO_SUBPIXEL_HINTING )
+      zone->cur[point].x = ADD_LONG( zone->cur[point].x, distance );
+
+    zone->tags[point]  |= FT_CURVE_TAG_TOUCH_X;
+  }
+
+
+  static void
+  Direct_Move_Y( TT_ExecContext  exc,
+                 TT_GlyphZone    zone,
+                 FT_UShort       point,
+                 FT_F26Dot6      distance )
+  {
+    FT_UNUSED( exc );
+
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    if ( !( SUBPIXEL_HINTING_MINIMAL             &&
+            exc->backward_compatibility          &&
+            exc->iupx_called && exc->iupy_called ) )
+#endif
+      zone->cur[point].y = ADD_LONG( zone->cur[point].y, distance );
+
+    zone->tags[point] |= FT_CURVE_TAG_TOUCH_Y;
+  }
+
+
+  /**************************************************************************
+   *
+   * Special versions of Direct_Move_Orig()
+   *
+   *   The following versions are used whenever both vectors are both
+   *   along one of the coordinate unit vectors, i.e. in 90% of the cases.
+   *
+   */
+
+
+  static void
+  Direct_Move_Orig_X( TT_ExecContext  exc,
+                      TT_GlyphZone    zone,
+                      FT_UShort       point,
+                      FT_F26Dot6      distance )
+  {
+    FT_UNUSED( exc );
+
+    zone->org[point].x = ADD_LONG( zone->org[point].x, distance );
+  }
+
+
+  static void
+  Direct_Move_Orig_Y( TT_ExecContext  exc,
+                      TT_GlyphZone    zone,
+                      FT_UShort       point,
+                      FT_F26Dot6      distance )
+  {
+    FT_UNUSED( exc );
+
+    zone->org[point].y = ADD_LONG( zone->org[point].y, distance );
+  }
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Round_None
+   *
+   * @Description:
+   *   Does not round, but adds engine compensation.
+   *
+   * @Input:
+   *   distance ::
+   *     The distance (not) to round.
+   *
+   *   color ::
+   *     The engine compensation color.
+   *
+   * @Return:
+   *   The compensated distance.
+   */
+  static FT_F26Dot6
+  Round_None( TT_ExecContext  exc,
+              FT_F26Dot6      distance,
+              FT_Int          color )
+  {
+    FT_F26Dot6  compensation = exc->tt_metrics.compensations[color];
+    FT_F26Dot6  val;
+
+
+    if ( distance >= 0 )
+    {
+      val = ADD_LONG( distance, compensation );
+      if ( val < 0 )
+        val = 0;
+    }
+    else
+    {
+      val = SUB_LONG( distance, compensation );
+      if ( val > 0 )
+        val = 0;
+    }
+    return val;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Round_To_Grid
+   *
+   * @Description:
+   *   Rounds value to grid after adding engine compensation.
+   *
+   * @Input:
+   *   distance ::
+   *     The distance to round.
+   *
+   *   color ::
+   *     The engine compensation color.
+   *
+   * @Return:
+   *   Rounded distance.
+   */
+  static FT_F26Dot6
+  Round_To_Grid( TT_ExecContext  exc,
+                 FT_F26Dot6      distance,
+                 FT_Int          color )
+  {
+    FT_F26Dot6  compensation = exc->tt_metrics.compensations[color];
+    FT_F26Dot6  val;
+
+
+    if ( distance >= 0 )
+    {
+      val = FT_PIX_ROUND_LONG( ADD_LONG( distance, compensation ) );
+      if ( val < 0 )
+        val = 0;
+    }
+    else
+    {
+      val = NEG_LONG( FT_PIX_ROUND_LONG( SUB_LONG( compensation,
+                                                   distance ) ) );
+      if ( val > 0 )
+        val = 0;
+    }
+
+    return val;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Round_To_Half_Grid
+   *
+   * @Description:
+   *   Rounds value to half grid after adding engine compensation.
+   *
+   * @Input:
+   *   distance ::
+   *     The distance to round.
+   *
+   *   color ::
+   *     The engine compensation color.
+   *
+   * @Return:
+   *   Rounded distance.
+   */
+  static FT_F26Dot6
+  Round_To_Half_Grid( TT_ExecContext  exc,
+                      FT_F26Dot6      distance,
+                      FT_Int          color )
+  {
+    FT_F26Dot6  compensation = exc->tt_metrics.compensations[color];
+    FT_F26Dot6  val;
+
+
+    if ( distance >= 0 )
+    {
+      val = ADD_LONG( FT_PIX_FLOOR( ADD_LONG( distance, compensation ) ),
+                      32 );
+      if ( val < 0 )
+        val = 32;
+    }
+    else
+    {
+      val = NEG_LONG( ADD_LONG( FT_PIX_FLOOR( SUB_LONG( compensation,
+                                                        distance ) ),
+                                32 ) );
+      if ( val > 0 )
+        val = -32;
+    }
+
+    return val;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Round_Down_To_Grid
+   *
+   * @Description:
+   *   Rounds value down to grid after adding engine compensation.
+   *
+   * @Input:
+   *   distance ::
+   *     The distance to round.
+   *
+   *   color ::
+   *     The engine compensation color.
+   *
+   * @Return:
+   *   Rounded distance.
+   */
+  static FT_F26Dot6
+  Round_Down_To_Grid( TT_ExecContext  exc,
+                      FT_F26Dot6      distance,
+                      FT_Int          color )
+  {
+    FT_F26Dot6  compensation = exc->tt_metrics.compensations[color];
+    FT_F26Dot6  val;
+
+
+    if ( distance >= 0 )
+    {
+      val = FT_PIX_FLOOR( ADD_LONG( distance, compensation ) );
+      if ( val < 0 )
+        val = 0;
+    }
+    else
+    {
+      val = NEG_LONG( FT_PIX_FLOOR( SUB_LONG( compensation, distance ) ) );
+      if ( val > 0 )
+        val = 0;
+    }
+
+    return val;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Round_Up_To_Grid
+   *
+   * @Description:
+   *   Rounds value up to grid after adding engine compensation.
+   *
+   * @Input:
+   *   distance ::
+   *     The distance to round.
+   *
+   *   color ::
+   *     The engine compensation color.
+   *
+   * @Return:
+   *   Rounded distance.
+   */
+  static FT_F26Dot6
+  Round_Up_To_Grid( TT_ExecContext  exc,
+                    FT_F26Dot6      distance,
+                    FT_Int          color )
+  {
+    FT_F26Dot6  compensation = exc->tt_metrics.compensations[color];
+    FT_F26Dot6  val;
+
+
+    if ( distance >= 0 )
+    {
+      val = FT_PIX_CEIL_LONG( ADD_LONG( distance, compensation ) );
+      if ( val < 0 )
+        val = 0;
+    }
+    else
+    {
+      val = NEG_LONG( FT_PIX_CEIL_LONG( SUB_LONG( compensation,
+                                                  distance ) ) );
+      if ( val > 0 )
+        val = 0;
+    }
+
+    return val;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Round_To_Double_Grid
+   *
+   * @Description:
+   *   Rounds value to double grid after adding engine compensation.
+   *
+   * @Input:
+   *   distance ::
+   *     The distance to round.
+   *
+   *   color ::
+   *     The engine compensation color.
+   *
+   * @Return:
+   *   Rounded distance.
+   */
+  static FT_F26Dot6
+  Round_To_Double_Grid( TT_ExecContext  exc,
+                        FT_F26Dot6      distance,
+                        FT_Int          color )
+  {
+    FT_F26Dot6  compensation = exc->tt_metrics.compensations[color];
+    FT_F26Dot6  val;
+
+
+    if ( distance >= 0 )
+    {
+      val = FT_PAD_ROUND_LONG( ADD_LONG( distance, compensation ), 32 );
+      if ( val < 0 )
+        val = 0;
+    }
+    else
+    {
+      val = NEG_LONG( FT_PAD_ROUND_LONG( SUB_LONG( compensation, distance ),
+                                         32 ) );
+      if ( val > 0 )
+        val = 0;
+    }
+
+    return val;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Round_Super
+   *
+   * @Description:
+   *   Super-rounds value to grid after adding engine compensation.
+   *
+   * @Input:
+   *   distance ::
+   *     The distance to round.
+   *
+   *   color ::
+   *     The engine compensation color.
+   *
+   * @Return:
+   *   Rounded distance.
+   *
+   * @Note:
+   *   The TrueType specification says very little about the relationship
+   *   between rounding and engine compensation.  However, it seems from
+   *   the description of super round that we should add the compensation
+   *   before rounding.
+   */
+  static FT_F26Dot6
+  Round_Super( TT_ExecContext  exc,
+               FT_F26Dot6      distance,
+               FT_Int          color )
+  {
+    FT_F26Dot6  compensation = exc->tt_metrics.compensations[color];
+    FT_F26Dot6  val;
+
+
+    if ( distance >= 0 )
+    {
+      val = ADD_LONG( distance,
+                      exc->threshold - exc->phase + compensation ) &
+              -exc->period;
+      val = ADD_LONG( val, exc->phase );
+      if ( val < 0 )
+        val = exc->phase;
+    }
+    else
+    {
+      val = NEG_LONG( SUB_LONG( exc->threshold - exc->phase + compensation,
+                                distance ) &
+                        -exc->period );
+      val = SUB_LONG( val, exc->phase );
+      if ( val > 0 )
+        val = -exc->phase;
+    }
+
+    return val;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Round_Super_45
+   *
+   * @Description:
+   *   Super-rounds value to grid after adding engine compensation.
+   *
+   * @Input:
+   *   distance ::
+   *     The distance to round.
+   *
+   *   color ::
+   *     The engine compensation color.
+   *
+   * @Return:
+   *   Rounded distance.
+   *
+   * @Note:
+   *   There is a separate function for Round_Super_45() as we may need
+   *   greater precision.
+   */
+  static FT_F26Dot6
+  Round_Super_45( TT_ExecContext  exc,
+                  FT_F26Dot6      distance,
+                  FT_Int          color )
+  {
+    FT_F26Dot6  compensation = exc->tt_metrics.compensations[color];
+    FT_F26Dot6  val;
+
+
+    if ( distance >= 0 )
+    {
+      val = ( ADD_LONG( distance,
+                        exc->threshold - exc->phase + compensation ) /
+                exc->period ) * exc->period;
+      val = ADD_LONG( val, exc->phase );
+      if ( val < 0 )
+        val = exc->phase;
+    }
+    else
+    {
+      val = NEG_LONG( ( SUB_LONG( exc->threshold - exc->phase + compensation,
+                                  distance ) /
+                          exc->period ) * exc->period );
+      val = SUB_LONG( val, exc->phase );
+      if ( val > 0 )
+        val = -exc->phase;
+    }
+
+    return val;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Compute_Round
+   *
+   * @Description:
+   *   Sets the rounding mode.
+   *
+   * @Input:
+   *   round_mode ::
+   *     The rounding mode to be used.
+   */
+  static void
+  Compute_Round( TT_ExecContext  exc,
+                 FT_Byte         round_mode )
+  {
+    switch ( round_mode )
+    {
+    case TT_Round_Off:
+      exc->func_round = (TT_Round_Func)Round_None;
+      break;
+
+    case TT_Round_To_Grid:
+      exc->func_round = (TT_Round_Func)Round_To_Grid;
+      break;
+
+    case TT_Round_Up_To_Grid:
+      exc->func_round = (TT_Round_Func)Round_Up_To_Grid;
+      break;
+
+    case TT_Round_Down_To_Grid:
+      exc->func_round = (TT_Round_Func)Round_Down_To_Grid;
+      break;
+
+    case TT_Round_To_Half_Grid:
+      exc->func_round = (TT_Round_Func)Round_To_Half_Grid;
+      break;
+
+    case TT_Round_To_Double_Grid:
+      exc->func_round = (TT_Round_Func)Round_To_Double_Grid;
+      break;
+
+    case TT_Round_Super:
+      exc->func_round = (TT_Round_Func)Round_Super;
+      break;
+
+    case TT_Round_Super_45:
+      exc->func_round = (TT_Round_Func)Round_Super_45;
+      break;
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   SetSuperRound
+   *
+   * @Description:
+   *   Sets Super Round parameters.
+   *
+   * @Input:
+   *   GridPeriod ::
+   *     The grid period.
+   *
+   *   selector ::
+   *     The SROUND opcode.
+   */
+  static void
+  SetSuperRound( TT_ExecContext  exc,
+                 FT_F2Dot14      GridPeriod,
+                 FT_Long         selector )
+  {
+    switch ( (FT_Int)( selector & 0xC0 ) )
+    {
+      case 0:
+        exc->period = GridPeriod / 2;
+        break;
+
+      case 0x40:
+        exc->period = GridPeriod;
+        break;
+
+      case 0x80:
+        exc->period = GridPeriod * 2;
+        break;
+
+      /* This opcode is reserved, but... */
+      case 0xC0:
+        exc->period = GridPeriod;
+        break;
+    }
+
+    switch ( (FT_Int)( selector & 0x30 ) )
+    {
+    case 0:
+      exc->phase = 0;
+      break;
+
+    case 0x10:
+      exc->phase = exc->period / 4;
+      break;
+
+    case 0x20:
+      exc->phase = exc->period / 2;
+      break;
+
+    case 0x30:
+      exc->phase = exc->period * 3 / 4;
+      break;
+    }
+
+    if ( ( selector & 0x0F ) == 0 )
+      exc->threshold = exc->period - 1;
+    else
+      exc->threshold = ( (FT_Int)( selector & 0x0F ) - 4 ) * exc->period / 8;
+
+    /* convert to F26Dot6 format */
+    exc->period    >>= 8;
+    exc->phase     >>= 8;
+    exc->threshold >>= 8;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Project
+   *
+   * @Description:
+   *   Computes the projection of vector given by (v2-v1) along the
+   *   current projection vector.
+   *
+   * @Input:
+   *   v1 ::
+   *     First input vector.
+   *   v2 ::
+   *     Second input vector.
+   *
+   * @Return:
+   *   The distance in F26dot6 format.
+   */
+  static FT_F26Dot6
+  Project( TT_ExecContext  exc,
+           FT_Pos          dx,
+           FT_Pos          dy )
+  {
+    return TT_DotFix14( dx, dy,
+                        exc->GS.projVector.x,
+                        exc->GS.projVector.y );
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Dual_Project
+   *
+   * @Description:
+   *   Computes the projection of the vector given by (v2-v1) along the
+   *   current dual vector.
+   *
+   * @Input:
+   *   v1 ::
+   *     First input vector.
+   *   v2 ::
+   *     Second input vector.
+   *
+   * @Return:
+   *   The distance in F26dot6 format.
+   */
+  static FT_F26Dot6
+  Dual_Project( TT_ExecContext  exc,
+                FT_Pos          dx,
+                FT_Pos          dy )
+  {
+    return TT_DotFix14( dx, dy,
+                        exc->GS.dualVector.x,
+                        exc->GS.dualVector.y );
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Project_x
+   *
+   * @Description:
+   *   Computes the projection of the vector given by (v2-v1) along the
+   *   horizontal axis.
+   *
+   * @Input:
+   *   v1 ::
+   *     First input vector.
+   *   v2 ::
+   *     Second input vector.
+   *
+   * @Return:
+   *   The distance in F26dot6 format.
+   */
+  static FT_F26Dot6
+  Project_x( TT_ExecContext  exc,
+             FT_Pos          dx,
+             FT_Pos          dy )
+  {
+    FT_UNUSED( exc );
+    FT_UNUSED( dy );
+
+    return dx;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Project_y
+   *
+   * @Description:
+   *   Computes the projection of the vector given by (v2-v1) along the
+   *   vertical axis.
+   *
+   * @Input:
+   *   v1 ::
+   *     First input vector.
+   *   v2 ::
+   *     Second input vector.
+   *
+   * @Return:
+   *   The distance in F26dot6 format.
+   */
+  static FT_F26Dot6
+  Project_y( TT_ExecContext  exc,
+             FT_Pos          dx,
+             FT_Pos          dy )
+  {
+    FT_UNUSED( exc );
+    FT_UNUSED( dx );
+
+    return dy;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Compute_Funcs
+   *
+   * @Description:
+   *   Computes the projection and movement function pointers according
+   *   to the current graphics state.
+   */
+  static void
+  Compute_Funcs( TT_ExecContext  exc )
+  {
+    if ( exc->GS.freeVector.x == 0x4000 )
+      exc->F_dot_P = exc->GS.projVector.x;
+    else if ( exc->GS.freeVector.y == 0x4000 )
+      exc->F_dot_P = exc->GS.projVector.y;
+    else
+      exc->F_dot_P =
+        ( (FT_Long)exc->GS.projVector.x * exc->GS.freeVector.x +
+          (FT_Long)exc->GS.projVector.y * exc->GS.freeVector.y ) >> 14;
+
+    if ( exc->GS.projVector.x == 0x4000 )
+      exc->func_project = (TT_Project_Func)Project_x;
+    else if ( exc->GS.projVector.y == 0x4000 )
+      exc->func_project = (TT_Project_Func)Project_y;
+    else
+      exc->func_project = (TT_Project_Func)Project;
+
+    if ( exc->GS.dualVector.x == 0x4000 )
+      exc->func_dualproj = (TT_Project_Func)Project_x;
+    else if ( exc->GS.dualVector.y == 0x4000 )
+      exc->func_dualproj = (TT_Project_Func)Project_y;
+    else
+      exc->func_dualproj = (TT_Project_Func)Dual_Project;
+
+    exc->func_move      = (TT_Move_Func)Direct_Move;
+    exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig;
+
+    if ( exc->F_dot_P == 0x4000L )
+    {
+      if ( exc->GS.freeVector.x == 0x4000 )
+      {
+        exc->func_move      = (TT_Move_Func)Direct_Move_X;
+        exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_X;
+      }
+      else if ( exc->GS.freeVector.y == 0x4000 )
+      {
+        exc->func_move      = (TT_Move_Func)Direct_Move_Y;
+        exc->func_move_orig = (TT_Move_Func)Direct_Move_Orig_Y;
+      }
+    }
+
+    /* at small sizes, F_dot_P can become too small, resulting   */
+    /* in overflows and `spikes' in a number of glyphs like `w'. */
+
+    if ( FT_ABS( exc->F_dot_P ) < 0x400L )
+      exc->F_dot_P = 0x4000L;
+
+    /* Disable cached aspect ratio */
+    exc->tt_metrics.ratio = 0;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   Normalize
+   *
+   * @Description:
+   *   Norms a vector.
+   *
+   * @Input:
+   *   Vx ::
+   *     The horizontal input vector coordinate.
+   *   Vy ::
+   *     The vertical input vector coordinate.
+   *
+   * @Output:
+   *   R ::
+   *     The normed unit vector.
+   *
+   * @Return:
+   *   Returns FAILURE if a vector parameter is zero.
+   *
+   * @Note:
+   *   In case Vx and Vy are both zero, `Normalize' returns SUCCESS, and
+   *   R is undefined.
+   */
+  static FT_Bool
+  Normalize( FT_F26Dot6      Vx,
+             FT_F26Dot6      Vy,
+             FT_UnitVector*  R )
+  {
+    FT_Vector V;
+
+
+    if ( Vx == 0 && Vy == 0 )
+    {
+      /* XXX: UNDOCUMENTED! It seems that it is possible to try   */
+      /*      to normalize the vector (0,0).  Return immediately. */
+      return SUCCESS;
+    }
+
+    V.x = Vx;
+    V.y = Vy;
+
+    FT_Vector_NormLen( &V );
+
+    R->x = (FT_F2Dot14)( V.x / 4 );
+    R->y = (FT_F2Dot14)( V.y / 4 );
+
+    return SUCCESS;
+  }
+
+
+  /**************************************************************************
+   *
+   * Here we start with the implementation of the various opcodes.
+   *
+   */
+
+
+#define ARRAY_BOUND_ERROR                         \
+    do                                            \
+    {                                             \
+      exc->error = FT_THROW( Invalid_Reference ); \
+      return;                                     \
+    } while (0)
+
+
+  /**************************************************************************
+   *
+   * MPPEM[]:      Measure Pixel Per EM
+   * Opcode range: 0x4B
+   * Stack:        --> Euint16
+   */
+  static void
+  Ins_MPPEM( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    args[0] = exc->func_cur_ppem( exc );
+  }
+
+
+  /**************************************************************************
+   *
+   * MPS[]:        Measure Point Size
+   * Opcode range: 0x4C
+   * Stack:        --> Euint16
+   */
+  static void
+  Ins_MPS( TT_ExecContext  exc,
+           FT_Long*        args )
+  {
+    if ( NO_SUBPIXEL_HINTING )
+    {
+      /* Microsoft's GDI bytecode interpreter always returns value 12; */
+      /* we return the current PPEM value instead.                     */
+      args[0] = exc->func_cur_ppem( exc );
+    }
+    else
+    {
+      /* A possible practical application of the MPS instruction is to   */
+      /* implement optical scaling and similar features, which should be */
+      /* based on perceptual attributes, thus independent of the         */
+      /* resolution.                                                     */
+      args[0] = exc->pointSize;
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * DUP[]:        DUPlicate the stack's top element
+   * Opcode range: 0x20
+   * Stack:        StkElt --> StkElt StkElt
+   */
+  static void
+  Ins_DUP( FT_Long*  args )
+  {
+    args[1] = args[0];
+  }
+
+
+  /**************************************************************************
+   *
+   * POP[]:        POP the stack's top element
+   * Opcode range: 0x21
+   * Stack:        StkElt -->
+   */
+  static void
+  Ins_POP( void )
+  {
+    /* nothing to do */
+  }
+
+
+  /**************************************************************************
+   *
+   * CLEAR[]:      CLEAR the entire stack
+   * Opcode range: 0x22
+   * Stack:        StkElt... -->
+   */
+  static void
+  Ins_CLEAR( TT_ExecContext  exc )
+  {
+    exc->new_top = 0;
+  }
+
+
+  /**************************************************************************
+   *
+   * SWAP[]:       SWAP the stack's top two elements
+   * Opcode range: 0x23
+   * Stack:        2 * StkElt --> 2 * StkElt
+   */
+  static void
+  Ins_SWAP( FT_Long*  args )
+  {
+    FT_Long  L;
+
+
+    L       = args[0];
+    args[0] = args[1];
+    args[1] = L;
+  }
+
+
+  /**************************************************************************
+   *
+   * DEPTH[]:      return the stack DEPTH
+   * Opcode range: 0x24
+   * Stack:        --> uint32
+   */
+  static void
+  Ins_DEPTH( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    args[0] = exc->top;
+  }
+
+
+  /**************************************************************************
+   *
+   * LT[]:         Less Than
+   * Opcode range: 0x50
+   * Stack:        int32? int32? --> bool
+   */
+  static void
+  Ins_LT( FT_Long*  args )
+  {
+    args[0] = ( args[0] < args[1] );
+  }
+
+
+  /**************************************************************************
+   *
+   * LTEQ[]:       Less Than or EQual
+   * Opcode range: 0x51
+   * Stack:        int32? int32? --> bool
+   */
+  static void
+  Ins_LTEQ( FT_Long*  args )
+  {
+    args[0] = ( args[0] <= args[1] );
+  }
+
+
+  /**************************************************************************
+   *
+   * GT[]:         Greater Than
+   * Opcode range: 0x52
+   * Stack:        int32? int32? --> bool
+   */
+  static void
+  Ins_GT( FT_Long*  args )
+  {
+    args[0] = ( args[0] > args[1] );
+  }
+
+
+  /**************************************************************************
+   *
+   * GTEQ[]:       Greater Than or EQual
+   * Opcode range: 0x53
+   * Stack:        int32? int32? --> bool
+   */
+  static void
+  Ins_GTEQ( FT_Long*  args )
+  {
+    args[0] = ( args[0] >= args[1] );
+  }
+
+
+  /**************************************************************************
+   *
+   * EQ[]:         EQual
+   * Opcode range: 0x54
+   * Stack:        StkElt StkElt --> bool
+   */
+  static void
+  Ins_EQ( FT_Long*  args )
+  {
+    args[0] = ( args[0] == args[1] );
+  }
+
+
+  /**************************************************************************
+   *
+   * NEQ[]:        Not EQual
+   * Opcode range: 0x55
+   * Stack:        StkElt StkElt --> bool
+   */
+  static void
+  Ins_NEQ( FT_Long*  args )
+  {
+    args[0] = ( args[0] != args[1] );
+  }
+
+
+  /**************************************************************************
+   *
+   * ODD[]:        Is ODD
+   * Opcode range: 0x56
+   * Stack:        f26.6 --> bool
+   */
+  static void
+  Ins_ODD( TT_ExecContext  exc,
+           FT_Long*        args )
+  {
+    args[0] = ( ( exc->func_round( exc, args[0], 3 ) & 127 ) == 64 );
+  }
+
+
+  /**************************************************************************
+   *
+   * EVEN[]:       Is EVEN
+   * Opcode range: 0x57
+   * Stack:        f26.6 --> bool
+   */
+  static void
+  Ins_EVEN( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    args[0] = ( ( exc->func_round( exc, args[0], 3 ) & 127 ) == 0 );
+  }
+
+
+  /**************************************************************************
+   *
+   * AND[]:        logical AND
+   * Opcode range: 0x5A
+   * Stack:        uint32 uint32 --> uint32
+   */
+  static void
+  Ins_AND( FT_Long*  args )
+  {
+    args[0] = ( args[0] && args[1] );
+  }
+
+
+  /**************************************************************************
+   *
+   * OR[]:         logical OR
+   * Opcode range: 0x5B
+   * Stack:        uint32 uint32 --> uint32
+   */
+  static void
+  Ins_OR( FT_Long*  args )
+  {
+    args[0] = ( args[0] || args[1] );
+  }
+
+
+  /**************************************************************************
+   *
+   * NOT[]:        logical NOT
+   * Opcode range: 0x5C
+   * Stack:        StkElt --> uint32
+   */
+  static void
+  Ins_NOT( FT_Long*  args )
+  {
+    args[0] = !args[0];
+  }
+
+
+  /**************************************************************************
+   *
+   * ADD[]:        ADD
+   * Opcode range: 0x60
+   * Stack:        f26.6 f26.6 --> f26.6
+   */
+  static void
+  Ins_ADD( FT_Long*  args )
+  {
+    args[0] = ADD_LONG( args[0], args[1] );
+  }
+
+
+  /**************************************************************************
+   *
+   * SUB[]:        SUBtract
+   * Opcode range: 0x61
+   * Stack:        f26.6 f26.6 --> f26.6
+   */
+  static void
+  Ins_SUB( FT_Long*  args )
+  {
+    args[0] = SUB_LONG( args[0], args[1] );
+  }
+
+
+  /**************************************************************************
+   *
+   * DIV[]:        DIVide
+   * Opcode range: 0x62
+   * Stack:        f26.6 f26.6 --> f26.6
+   */
+  static void
+  Ins_DIV( TT_ExecContext  exc,
+           FT_Long*        args )
+  {
+    if ( args[1] == 0 )
+      exc->error = FT_THROW( Divide_By_Zero );
+    else
+      args[0] = FT_MulDiv_No_Round( args[0], 64L, args[1] );
+  }
+
+
+  /**************************************************************************
+   *
+   * MUL[]:        MULtiply
+   * Opcode range: 0x63
+   * Stack:        f26.6 f26.6 --> f26.6
+   */
+  static void
+  Ins_MUL( FT_Long*  args )
+  {
+    args[0] = FT_MulDiv( args[0], args[1], 64L );
+  }
+
+
+  /**************************************************************************
+   *
+   * ABS[]:        ABSolute value
+   * Opcode range: 0x64
+   * Stack:        f26.6 --> f26.6
+   */
+  static void
+  Ins_ABS( FT_Long*  args )
+  {
+    if ( args[0] < 0 )
+      args[0] = NEG_LONG( args[0] );
+  }
+
+
+  /**************************************************************************
+   *
+   * NEG[]:        NEGate
+   * Opcode range: 0x65
+   * Stack:        f26.6 --> f26.6
+   */
+  static void
+  Ins_NEG( FT_Long*  args )
+  {
+    args[0] = NEG_LONG( args[0] );
+  }
+
+
+  /**************************************************************************
+   *
+   * FLOOR[]:      FLOOR
+   * Opcode range: 0x66
+   * Stack:        f26.6 --> f26.6
+   */
+  static void
+  Ins_FLOOR( FT_Long*  args )
+  {
+    args[0] = FT_PIX_FLOOR( args[0] );
+  }
+
+
+  /**************************************************************************
+   *
+   * CEILING[]:    CEILING
+   * Opcode range: 0x67
+   * Stack:        f26.6 --> f26.6
+   */
+  static void
+  Ins_CEILING( FT_Long*  args )
+  {
+    args[0] = FT_PIX_CEIL_LONG( args[0] );
+  }
+
+
+  /**************************************************************************
+   *
+   * RS[]:         Read Store
+   * Opcode range: 0x43
+   * Stack:        uint32 --> uint32
+   */
+  static void
+  Ins_RS( TT_ExecContext  exc,
+          FT_Long*        args )
+  {
+    FT_ULong  I = (FT_ULong)args[0];
+
+
+    if ( BOUNDSL( I, exc->storeSize ) )
+    {
+      if ( exc->pedantic_hinting )
+        ARRAY_BOUND_ERROR;
+      else
+        args[0] = 0;
+    }
+    else
+      args[0] = exc->storage[I];
+  }
+
+
+  /**************************************************************************
+   *
+   * WS[]:         Write Store
+   * Opcode range: 0x42
+   * Stack:        uint32 uint32 -->
+   */
+  static void
+  Ins_WS( TT_ExecContext  exc,
+          FT_Long*        args )
+  {
+    FT_ULong  I = (FT_ULong)args[0];
+
+
+    if ( BOUNDSL( I, exc->storeSize ) )
+    {
+      if ( exc->pedantic_hinting )
+        ARRAY_BOUND_ERROR;
+    }
+    else
+    {
+      if ( exc->iniRange == tt_coderange_glyph &&
+           exc->storage != exc->glyfStorage    )
+      {
+        FT_Memory  memory = exc->memory;
+        FT_Error   error;
+
+
+        FT_MEM_QRENEW_ARRAY( exc->glyfStorage,
+                             exc->glyfStoreSize,
+                             exc->storeSize );
+        exc->error  = error;
+        if ( error )
+          return;
+
+        exc->glyfStoreSize = exc->storeSize;
+        FT_ARRAY_COPY( exc->glyfStorage, exc->storage, exc->glyfStoreSize );
+        exc->storage = exc->glyfStorage;
+      }
+
+      exc->storage[I] = args[1];
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * WCVTP[]:      Write CVT in Pixel units
+   * Opcode range: 0x44
+   * Stack:        f26.6 uint32 -->
+   */
+  static void
+  Ins_WCVTP( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    FT_ULong  I = (FT_ULong)args[0];
+
+
+    if ( BOUNDSL( I, exc->cvtSize ) )
+    {
+      if ( exc->pedantic_hinting )
+        ARRAY_BOUND_ERROR;
+    }
+    else
+      exc->func_write_cvt( exc, I, args[1] );
+  }
+
+
+  /**************************************************************************
+   *
+   * WCVTF[]:      Write CVT in Funits
+   * Opcode range: 0x70
+   * Stack:        uint32 uint32 -->
+   */
+  static void
+  Ins_WCVTF( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    FT_ULong  I = (FT_ULong)args[0];
+
+
+    if ( BOUNDSL( I, exc->cvtSize ) )
+    {
+      if ( exc->pedantic_hinting )
+        ARRAY_BOUND_ERROR;
+    }
+    else
+      exc->cvt[I] = FT_MulFix( args[1], exc->tt_metrics.scale );
+  }
+
+
+  /**************************************************************************
+   *
+   * RCVT[]:       Read CVT
+   * Opcode range: 0x45
+   * Stack:        uint32 --> f26.6
+   */
+  static void
+  Ins_RCVT( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    FT_ULong  I = (FT_ULong)args[0];
+
+
+    if ( BOUNDSL( I, exc->cvtSize ) )
+    {
+      if ( exc->pedantic_hinting )
+        ARRAY_BOUND_ERROR;
+      else
+        args[0] = 0;
+    }
+    else
+      args[0] = exc->func_read_cvt( exc, I );
+  }
+
+
+  /**************************************************************************
+   *
+   * AA[]:         Adjust Angle
+   * Opcode range: 0x7F
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_AA( void )
+  {
+    /* intentionally no longer supported */
+  }
+
+
+  /**************************************************************************
+   *
+   * DEBUG[]:      DEBUG.  Unsupported.
+   * Opcode range: 0x4F
+   * Stack:        uint32 -->
+   *
+   * Note: The original instruction pops a value from the stack.
+   */
+  static void
+  Ins_DEBUG( TT_ExecContext  exc )
+  {
+    exc->error = FT_THROW( Debug_OpCode );
+  }
+
+
+  /**************************************************************************
+   *
+   * ROUND[ab]:    ROUND value
+   * Opcode range: 0x68-0x6B
+   * Stack:        f26.6 --> f26.6
+   */
+  static void
+  Ins_ROUND( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    args[0] = exc->func_round( exc, args[0], exc->opcode & 3 );
+  }
+
+
+  /**************************************************************************
+   *
+   * NROUND[ab]:   No ROUNDing of value
+   * Opcode range: 0x6C-0x6F
+   * Stack:        f26.6 --> f26.6
+   */
+  static void
+  Ins_NROUND( TT_ExecContext  exc,
+              FT_Long*        args )
+  {
+    args[0] = Round_None( exc, args[0], exc->opcode & 3 );
+  }
+
+
+  /**************************************************************************
+   *
+   * MAX[]:        MAXimum
+   * Opcode range: 0x8B
+   * Stack:        int32? int32? --> int32
+   */
+  static void
+  Ins_MAX( FT_Long*  args )
+  {
+    if ( args[1] > args[0] )
+      args[0] = args[1];
+  }
+
+
+  /**************************************************************************
+   *
+   * MIN[]:        MINimum
+   * Opcode range: 0x8C
+   * Stack:        int32? int32? --> int32
+   */
+  static void
+  Ins_MIN( FT_Long*  args )
+  {
+    if ( args[1] < args[0] )
+      args[0] = args[1];
+  }
+
+
+  /**************************************************************************
+   *
+   * MINDEX[]:     Move INDEXed element
+   * Opcode range: 0x26
+   * Stack:        int32? --> StkElt
+   */
+  static void
+  Ins_MINDEX( TT_ExecContext  exc,
+              FT_Long*        args )
+  {
+    FT_Long  L, K;
+
+
+    L = args[0];
+
+    if ( L <= 0 || L > exc->args )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+    }
+    else
+    {
+      K = exc->stack[exc->args - L];
+
+      FT_ARRAY_MOVE( &exc->stack[exc->args - L    ],
+                     &exc->stack[exc->args - L + 1],
+                     ( L - 1 ) );
+
+      exc->stack[exc->args - 1] = K;
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * CINDEX[]:     Copy INDEXed element
+   * Opcode range: 0x25
+   * Stack:        int32 --> StkElt
+   */
+  static void
+  Ins_CINDEX( TT_ExecContext  exc,
+              FT_Long*        args )
+  {
+    FT_Long  L;
+
+
+    L = args[0];
+
+    if ( L <= 0 || L > exc->args )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      args[0] = 0;
+    }
+    else
+      args[0] = exc->stack[exc->args - L];
+  }
+
+
+  /**************************************************************************
+   *
+   * ROLL[]:       ROLL top three elements
+   * Opcode range: 0x8A
+   * Stack:        3 * StkElt --> 3 * StkElt
+   */
+  static void
+  Ins_ROLL( FT_Long*  args )
+  {
+    FT_Long  A, B, C;
+
+
+    A = args[2];
+    B = args[1];
+    C = args[0];
+
+    args[2] = C;
+    args[1] = A;
+    args[0] = B;
+  }
+
+
+  /**************************************************************************
+   *
+   * MANAGING THE FLOW OF CONTROL
+   *
+   */
+
+
+  /**************************************************************************
+   *
+   * SLOOP[]:      Set LOOP variable
+   * Opcode range: 0x17
+   * Stack:        int32? -->
+   */
+  static void
+  Ins_SLOOP( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    if ( args[0] < 0 )
+      exc->error = FT_THROW( Bad_Argument );
+    else
+    {
+      /* we heuristically limit the number of loops to 16 bits */
+      exc->GS.loop = args[0] > 0xFFFFL ? 0xFFFFL : args[0];
+    }
+  }
+
+
+  static FT_Bool
+  SkipCode( TT_ExecContext  exc )
+  {
+    exc->IP += exc->length;
+
+    if ( exc->IP < exc->codeSize )
+    {
+      exc->opcode = exc->code[exc->IP];
+
+      exc->length = opcode_length[exc->opcode];
+      if ( exc->length < 0 )
+      {
+        if ( exc->IP + 1 >= exc->codeSize )
+          goto Fail_Overflow;
+        exc->length = 2 - exc->length * exc->code[exc->IP + 1];
+      }
+
+      if ( exc->IP + exc->length <= exc->codeSize )
+        return SUCCESS;
+    }
+
+  Fail_Overflow:
+    exc->error = FT_THROW( Code_Overflow );
+    return FAILURE;
+  }
+
+
+  /**************************************************************************
+   *
+   * IF[]:         IF test
+   * Opcode range: 0x58
+   * Stack:        StkElt -->
+   */
+  static void
+  Ins_IF( TT_ExecContext  exc,
+          FT_Long*        args )
+  {
+    FT_Int   nIfs;
+    FT_Bool  Out;
+
+
+    if ( args[0] != 0 )
+      return;
+
+    nIfs = 1;
+    Out = 0;
+
+    do
+    {
+      if ( SkipCode( exc ) == FAILURE )
+        return;
+
+      switch ( exc->opcode )
+      {
+      case 0x58:      /* IF */
+        nIfs++;
+        break;
+
+      case 0x1B:      /* ELSE */
+        Out = FT_BOOL( nIfs == 1 );
+        break;
+
+      case 0x59:      /* EIF */
+        nIfs--;
+        Out = FT_BOOL( nIfs == 0 );
+        break;
+      }
+    } while ( Out == 0 );
+  }
+
+
+  /**************************************************************************
+   *
+   * ELSE[]:       ELSE
+   * Opcode range: 0x1B
+   * Stack:        -->
+   */
+  static void
+  Ins_ELSE( TT_ExecContext  exc )
+  {
+    FT_Int  nIfs;
+
+
+    nIfs = 1;
+
+    do
+    {
+      if ( SkipCode( exc ) == FAILURE )
+        return;
+
+      switch ( exc->opcode )
+      {
+      case 0x58:    /* IF */
+        nIfs++;
+        break;
+
+      case 0x59:    /* EIF */
+        nIfs--;
+        break;
+      }
+    } while ( nIfs != 0 );
+  }
+
+
+  /**************************************************************************
+   *
+   * EIF[]:        End IF
+   * Opcode range: 0x59
+   * Stack:        -->
+   */
+  static void
+  Ins_EIF( void )
+  {
+    /* nothing to do */
+  }
+
+
+  /**************************************************************************
+   *
+   * JMPR[]:       JuMP Relative
+   * Opcode range: 0x1C
+   * Stack:        int32 -->
+   */
+  static void
+  Ins_JMPR( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    if ( args[0] == 0 && exc->args == 0 )
+    {
+      exc->error = FT_THROW( Bad_Argument );
+      return;
+    }
+
+    exc->IP = ADD_LONG( exc->IP, args[0] );
+    if ( exc->IP < 0                                             ||
+         ( exc->callTop > 0                                    &&
+           exc->IP > exc->callStack[exc->callTop - 1].Def->end ) )
+    {
+      exc->error = FT_THROW( Bad_Argument );
+      return;
+    }
+
+    exc->step_ins = FALSE;
+
+    if ( args[0] < 0 )
+    {
+      if ( ++exc->neg_jump_counter > exc->neg_jump_counter_max )
+        exc->error = FT_THROW( Execution_Too_Long );
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * JROT[]:       Jump Relative On True
+   * Opcode range: 0x78
+   * Stack:        StkElt int32 -->
+   */
+  static void
+  Ins_JROT( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    if ( args[1] != 0 )
+      Ins_JMPR( exc, args );
+  }
+
+
+  /**************************************************************************
+   *
+   * JROF[]:       Jump Relative On False
+   * Opcode range: 0x79
+   * Stack:        StkElt int32 -->
+   */
+  static void
+  Ins_JROF( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    if ( args[1] == 0 )
+      Ins_JMPR( exc, args );
+  }
+
+
+  /**************************************************************************
+   *
+   * DEFINING AND USING FUNCTIONS AND INSTRUCTIONS
+   *
+   */
+
+
+  /**************************************************************************
+   *
+   * FDEF[]:       Function DEFinition
+   * Opcode range: 0x2C
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_FDEF( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    FT_ULong       n;
+    TT_DefRecord*  rec;
+    TT_DefRecord*  limit;
+
+
+    /* FDEF is only allowed in `prep' or `fpgm' */
+    if ( exc->iniRange == tt_coderange_glyph )
+    {
+      exc->error = FT_THROW( DEF_In_Glyf_Bytecode );
+      return;
+    }
+
+    /* some font programs are broken enough to redefine functions! */
+    /* We will then parse the current table.                       */
+
+    rec   = exc->FDefs;
+    limit = FT_OFFSET( rec, exc->numFDefs );
+    n     = (FT_ULong)args[0];
+
+    for ( ; rec < limit; rec++ )
+    {
+      if ( rec->opc == n )
+        break;
+    }
+
+    if ( rec == limit )
+    {
+      /* check that there is enough room for new functions */
+      if ( exc->numFDefs >= exc->maxFDefs )
+      {
+        exc->error = FT_THROW( Too_Many_Function_Defs );
+        return;
+      }
+      exc->numFDefs++;
+    }
+
+    /* Although FDEF takes unsigned 32-bit integer,  */
+    /* func # must be within unsigned 16-bit integer */
+    if ( n > 0xFFFFU )
+    {
+      exc->error = FT_THROW( Too_Many_Function_Defs );
+      return;
+    }
+
+    rec->range          = exc->curRange;
+    rec->opc            = (FT_UInt16)n;
+    rec->start          = exc->IP + 1;
+    rec->active         = TRUE;
+
+    if ( n > exc->maxFunc )
+      exc->maxFunc = (FT_UInt16)n;
+
+    /* Now skip the whole function definition. */
+    /* We don't allow nested IDEFS & FDEFs.    */
+
+    while ( SkipCode( exc ) == SUCCESS )
+    {
+      switch ( exc->opcode )
+      {
+      case 0x89:    /* IDEF */
+      case 0x2C:    /* FDEF */
+        exc->error = FT_THROW( Nested_DEFS );
+        return;
+
+      case 0x2D:   /* ENDF */
+        rec->end = exc->IP;
+        return;
+      }
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * ENDF[]:       END Function definition
+   * Opcode range: 0x2D
+   * Stack:        -->
+   */
+  static void
+  Ins_ENDF( TT_ExecContext  exc )
+  {
+    TT_CallRec*  pRec;
+
+
+    if ( exc->callTop <= 0 )     /* We encountered an ENDF without a call */
+    {
+      exc->error = FT_THROW( ENDF_In_Exec_Stream );
+      return;
+    }
+
+    exc->callTop--;
+
+    pRec = &exc->callStack[exc->callTop];
+
+    pRec->Cur_Count--;
+
+    exc->step_ins = FALSE;
+
+    if ( pRec->Cur_Count > 0 )
+    {
+      exc->callTop++;
+      exc->IP = pRec->Def->start;
+    }
+    else
+      /* Loop through the current function */
+      Ins_Goto_CodeRange( exc, pRec->Caller_Range, pRec->Caller_IP );
+
+    /* Exit the current call frame.                      */
+
+    /* NOTE: If the last instruction of a program is a   */
+    /*       CALL or LOOPCALL, the return address is     */
+    /*       always out of the code range.  This is a    */
+    /*       valid address, and it is why we do not test */
+    /*       the result of Ins_Goto_CodeRange() here!    */
+  }
+
+
+  /**************************************************************************
+   *
+   * CALL[]:       CALL function
+   * Opcode range: 0x2B
+   * Stack:        uint32? -->
+   */
+  static void
+  Ins_CALL( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    FT_ULong       F;
+    TT_CallRec*    pCrec;
+    TT_DefRecord*  def;
+
+
+    /* first of all, check the index */
+
+    F = (FT_ULong)args[0];
+    if ( BOUNDSL( F, exc->maxFunc + 1 ) )
+      goto Fail;
+
+    if ( !exc->FDefs )
+      goto Fail;
+
+    /* Except for some old Apple fonts, all functions in a TrueType */
+    /* font are defined in increasing order, starting from 0.  This */
+    /* means that we normally have                                  */
+    /*                                                              */
+    /*    exc->maxFunc+1 == exc->numFDefs                           */
+    /*    exc->FDefs[n].opc == n for n in 0..exc->maxFunc           */
+    /*                                                              */
+    /* If this isn't true, we need to look up the function table.   */
+
+    def = exc->FDefs + F;
+    if ( exc->maxFunc + 1 != exc->numFDefs || def->opc != F )
+    {
+      /* look up the FDefs table */
+      TT_DefRecord*  limit;
+
+
+      def   = exc->FDefs;
+      limit = def + exc->numFDefs;
+
+      while ( def < limit && def->opc != F )
+        def++;
+
+      if ( def == limit )
+        goto Fail;
+    }
+
+    /* check that the function is active */
+    if ( !def->active )
+      goto Fail;
+
+    /* check the call stack */
+    if ( exc->callTop >= exc->callSize )
+    {
+      exc->error = FT_THROW( Stack_Overflow );
+      return;
+    }
+
+    pCrec = exc->callStack + exc->callTop;
+
+    pCrec->Caller_Range = exc->curRange;
+    pCrec->Caller_IP    = exc->IP + 1;
+    pCrec->Cur_Count    = 1;
+    pCrec->Def          = def;
+
+    exc->callTop++;
+
+    Ins_Goto_CodeRange( exc, def->range, def->start );
+
+    exc->step_ins = FALSE;
+
+    return;
+
+  Fail:
+    exc->error = FT_THROW( Invalid_Reference );
+  }
+
+
+  /**************************************************************************
+   *
+   * LOOPCALL[]:   LOOP and CALL function
+   * Opcode range: 0x2A
+   * Stack:        uint32? Eint16? -->
+   */
+  static void
+  Ins_LOOPCALL( TT_ExecContext  exc,
+                FT_Long*        args )
+  {
+    FT_ULong       F;
+    TT_CallRec*    pCrec;
+    TT_DefRecord*  def;
+
+
+    /* first of all, check the index */
+    F = (FT_ULong)args[1];
+    if ( BOUNDSL( F, exc->maxFunc + 1 ) )
+      goto Fail;
+
+    /* Except for some old Apple fonts, all functions in a TrueType */
+    /* font are defined in increasing order, starting from 0.  This */
+    /* means that we normally have                                  */
+    /*                                                              */
+    /*    exc->maxFunc+1 == exc->numFDefs                           */
+    /*    exc->FDefs[n].opc == n for n in 0..exc->maxFunc           */
+    /*                                                              */
+    /* If this isn't true, we need to look up the function table.   */
+
+    def = FT_OFFSET( exc->FDefs, F );
+    if ( exc->maxFunc + 1 != exc->numFDefs || def->opc != F )
+    {
+      /* look up the FDefs table */
+      TT_DefRecord*  limit;
+
+
+      def   = exc->FDefs;
+      limit = FT_OFFSET( def, exc->numFDefs );
+
+      while ( def < limit && def->opc != F )
+        def++;
+
+      if ( def == limit )
+        goto Fail;
+    }
+
+    /* check that the function is active */
+    if ( !def->active )
+      goto Fail;
+
+    /* check stack */
+    if ( exc->callTop >= exc->callSize )
+    {
+      exc->error = FT_THROW( Stack_Overflow );
+      return;
+    }
+
+    if ( args[0] > 0 )
+    {
+      pCrec = exc->callStack + exc->callTop;
+
+      pCrec->Caller_Range = exc->curRange;
+      pCrec->Caller_IP    = exc->IP + 1;
+      pCrec->Cur_Count    = (FT_Int)args[0];
+      pCrec->Def          = def;
+
+      exc->callTop++;
+
+      Ins_Goto_CodeRange( exc, def->range, def->start );
+
+      exc->step_ins = FALSE;
+
+      exc->loopcall_counter += (FT_ULong)args[0];
+      if ( exc->loopcall_counter > exc->loopcall_counter_max )
+        exc->error = FT_THROW( Execution_Too_Long );
+    }
+
+    return;
+
+  Fail:
+    exc->error = FT_THROW( Invalid_Reference );
+  }
+
+
+  /**************************************************************************
+   *
+   * IDEF[]:       Instruction DEFinition
+   * Opcode range: 0x89
+   * Stack:        Eint8 -->
+   */
+  static void
+  Ins_IDEF( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    TT_DefRecord*  def;
+    TT_DefRecord*  limit;
+
+
+    /* we enable IDEF only in `prep' or `fpgm' */
+    if ( exc->iniRange == tt_coderange_glyph )
+    {
+      exc->error = FT_THROW( DEF_In_Glyf_Bytecode );
+      return;
+    }
+
+    /*  First of all, look for the same function in our table */
+
+    def   = exc->IDefs;
+    limit = FT_OFFSET( def, exc->numIDefs );
+
+    for ( ; def < limit; def++ )
+      if ( def->opc == (FT_ULong)args[0] )
+        break;
+
+    if ( def == limit )
+    {
+      /* check that there is enough room for a new instruction */
+      if ( exc->numIDefs >= exc->maxIDefs )
+      {
+        exc->error = FT_THROW( Too_Many_Instruction_Defs );
+        return;
+      }
+      exc->numIDefs++;
+    }
+
+    /* opcode must be unsigned 8-bit integer */
+    if ( 0 > args[0] || args[0] > 0x00FF )
+    {
+      exc->error = FT_THROW( Too_Many_Instruction_Defs );
+      return;
+    }
+
+    def->opc    = (FT_Byte)args[0];
+    def->start  = exc->IP + 1;
+    def->range  = exc->curRange;
+    def->active = TRUE;
+
+    if ( (FT_ULong)args[0] > exc->maxIns )
+      exc->maxIns = (FT_Byte)args[0];
+
+    /* Now skip the whole function definition. */
+    /* We don't allow nested IDEFs & FDEFs.    */
+
+    while ( SkipCode( exc ) == SUCCESS )
+    {
+      switch ( exc->opcode )
+      {
+      case 0x89:   /* IDEF */
+      case 0x2C:   /* FDEF */
+        exc->error = FT_THROW( Nested_DEFS );
+        return;
+      case 0x2D:   /* ENDF */
+        def->end = exc->IP;
+        return;
+      }
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * PUSHING DATA ONTO THE INTERPRETER STACK
+   *
+   */
+
+
+  /**************************************************************************
+   *
+   * NPUSHB[]:     PUSH N Bytes
+   * Opcode range: 0x40
+   * Stack:        --> uint32...
+   */
+  static void
+  Ins_NPUSHB( TT_ExecContext  exc,
+              FT_Long*        args )
+  {
+    FT_UShort  L, K;
+
+
+    L = (FT_UShort)exc->code[exc->IP + 1];
+
+    if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) )
+    {
+      exc->error = FT_THROW( Stack_Overflow );
+      return;
+    }
+
+    for ( K = 1; K <= L; K++ )
+      args[K - 1] = exc->code[exc->IP + K + 1];
+
+    exc->new_top += L;
+  }
+
+
+  /**************************************************************************
+   *
+   * NPUSHW[]:     PUSH N Words
+   * Opcode range: 0x41
+   * Stack:        --> int32...
+   */
+  static void
+  Ins_NPUSHW( TT_ExecContext  exc,
+              FT_Long*        args )
+  {
+    FT_UShort  L, K;
+
+
+    L = (FT_UShort)exc->code[exc->IP + 1];
+
+    if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) )
+    {
+      exc->error = FT_THROW( Stack_Overflow );
+      return;
+    }
+
+    exc->IP += 2;
+
+    for ( K = 0; K < L; K++ )
+      args[K] = GetShortIns( exc );
+
+    exc->step_ins = FALSE;
+    exc->new_top += L;
+  }
+
+
+  /**************************************************************************
+   *
+   * PUSHB[abc]:   PUSH Bytes
+   * Opcode range: 0xB0-0xB7
+   * Stack:        --> uint32...
+   */
+  static void
+  Ins_PUSHB( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    FT_UShort  L, K;
+
+
+    L = (FT_UShort)( exc->opcode - 0xB0 + 1 );
+
+    if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) )
+    {
+      exc->error = FT_THROW( Stack_Overflow );
+      return;
+    }
+
+    for ( K = 1; K <= L; K++ )
+      args[K - 1] = exc->code[exc->IP + K];
+  }
+
+
+  /**************************************************************************
+   *
+   * PUSHW[abc]:   PUSH Words
+   * Opcode range: 0xB8-0xBF
+   * Stack:        --> int32...
+   */
+  static void
+  Ins_PUSHW( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    FT_UShort  L, K;
+
+
+    L = (FT_UShort)( exc->opcode - 0xB8 + 1 );
+
+    if ( BOUNDS( L, exc->stackSize + 1 - exc->top ) )
+    {
+      exc->error = FT_THROW( Stack_Overflow );
+      return;
+    }
+
+    exc->IP++;
+
+    for ( K = 0; K < L; K++ )
+      args[K] = GetShortIns( exc );
+
+    exc->step_ins = FALSE;
+  }
+
+
+  /**************************************************************************
+   *
+   * MANAGING THE GRAPHICS STATE
+   *
+   */
+
+
+  static FT_Bool
+  Ins_SxVTL( TT_ExecContext  exc,
+             FT_UShort       aIdx1,
+             FT_UShort       aIdx2,
+             FT_UnitVector*  Vec )
+  {
+    FT_Long     A, B, C;
+    FT_Vector*  p1;
+    FT_Vector*  p2;
+
+    FT_Byte  opcode = exc->opcode;
+
+
+    if ( BOUNDS( aIdx1, exc->zp2.n_points ) ||
+         BOUNDS( aIdx2, exc->zp1.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return FAILURE;
+    }
+
+    p1 = exc->zp1.cur + aIdx2;
+    p2 = exc->zp2.cur + aIdx1;
+
+    A = SUB_LONG( p1->x, p2->x );
+    B = SUB_LONG( p1->y, p2->y );
+
+    /* If p1 == p2, SPvTL and SFvTL behave the same as */
+    /* SPvTCA[X] and SFvTCA[X], respectively.          */
+    /*                                                 */
+    /* Confirmed by Greg Hitchcock.                    */
+
+    if ( A == 0 && B == 0 )
+    {
+      A      = 0x4000;
+      opcode = 0;
+    }
+
+    if ( ( opcode & 1 ) != 0 )
+    {
+      C = B;   /* counter-clockwise rotation */
+      B = A;
+      A = NEG_LONG( C );
+    }
+
+    Normalize( A, B, Vec );
+
+    return SUCCESS;
+  }
+
+
+  /**************************************************************************
+   *
+   * SVTCA[a]:     Set (F and P) Vectors to Coordinate Axis
+   * Opcode range: 0x00-0x01
+   * Stack:        -->
+   *
+   * SPvTCA[a]:    Set PVector to Coordinate Axis
+   * Opcode range: 0x02-0x03
+   * Stack:        -->
+   *
+   * SFvTCA[a]:    Set FVector to Coordinate Axis
+   * Opcode range: 0x04-0x05
+   * Stack:        -->
+   */
+  static void
+  Ins_SxyTCA( TT_ExecContext  exc )
+  {
+    FT_Short  AA, BB;
+
+    FT_Byte  opcode = exc->opcode;
+
+
+    AA = (FT_Short)( ( opcode & 1 ) << 14 );
+    BB = (FT_Short)( AA ^ 0x4000 );
+
+    if ( opcode < 4 )
+    {
+      exc->GS.projVector.x = AA;
+      exc->GS.projVector.y = BB;
+
+      exc->GS.dualVector.x = AA;
+      exc->GS.dualVector.y = BB;
+    }
+
+    if ( ( opcode & 2 ) == 0 )
+    {
+      exc->GS.freeVector.x = AA;
+      exc->GS.freeVector.y = BB;
+    }
+
+    Compute_Funcs( exc );
+  }
+
+
+  /**************************************************************************
+   *
+   * SPvTL[a]:     Set PVector To Line
+   * Opcode range: 0x06-0x07
+   * Stack:        uint32 uint32 -->
+   */
+  static void
+  Ins_SPVTL( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    if ( Ins_SxVTL( exc,
+                    (FT_UShort)args[1],
+                    (FT_UShort)args[0],
+                    &exc->GS.projVector ) == SUCCESS )
+    {
+      exc->GS.dualVector = exc->GS.projVector;
+      Compute_Funcs( exc );
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * SFvTL[a]:     Set FVector To Line
+   * Opcode range: 0x08-0x09
+   * Stack:        uint32 uint32 -->
+   */
+  static void
+  Ins_SFVTL( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    if ( Ins_SxVTL( exc,
+                    (FT_UShort)args[1],
+                    (FT_UShort)args[0],
+                    &exc->GS.freeVector ) == SUCCESS )
+    {
+      Compute_Funcs( exc );
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * SFvTPv[]:     Set FVector To PVector
+   * Opcode range: 0x0E
+   * Stack:        -->
+   */
+  static void
+  Ins_SFVTPV( TT_ExecContext  exc )
+  {
+    exc->GS.freeVector = exc->GS.projVector;
+    Compute_Funcs( exc );
+  }
+
+
+  /**************************************************************************
+   *
+   * SPvFS[]:      Set PVector From Stack
+   * Opcode range: 0x0A
+   * Stack:        f2.14 f2.14 -->
+   */
+  static void
+  Ins_SPVFS( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    FT_Short  S;
+    FT_Long   X, Y;
+
+
+    /* Only use low 16bits, then sign extend */
+    S = (FT_Short)args[1];
+    Y = (FT_Long)S;
+    S = (FT_Short)args[0];
+    X = (FT_Long)S;
+
+    Normalize( X, Y, &exc->GS.projVector );
+
+    exc->GS.dualVector = exc->GS.projVector;
+    Compute_Funcs( exc );
+  }
+
+
+  /**************************************************************************
+   *
+   * SFvFS[]:      Set FVector From Stack
+   * Opcode range: 0x0B
+   * Stack:        f2.14 f2.14 -->
+   */
+  static void
+  Ins_SFVFS( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    FT_Short  S;
+    FT_Long   X, Y;
+
+
+    /* Only use low 16bits, then sign extend */
+    S = (FT_Short)args[1];
+    Y = (FT_Long)S;
+    S = (FT_Short)args[0];
+    X = S;
+
+    Normalize( X, Y, &exc->GS.freeVector );
+    Compute_Funcs( exc );
+  }
+
+
+  /**************************************************************************
+   *
+   * GPv[]:        Get Projection Vector
+   * Opcode range: 0x0C
+   * Stack:        ef2.14 --> ef2.14
+   */
+  static void
+  Ins_GPV( TT_ExecContext  exc,
+           FT_Long*        args )
+  {
+    args[0] = exc->GS.projVector.x;
+    args[1] = exc->GS.projVector.y;
+  }
+
+
+  /**************************************************************************
+   *
+   * GFv[]:        Get Freedom Vector
+   * Opcode range: 0x0D
+   * Stack:        ef2.14 --> ef2.14
+   */
+  static void
+  Ins_GFV( TT_ExecContext  exc,
+           FT_Long*        args )
+  {
+    args[0] = exc->GS.freeVector.x;
+    args[1] = exc->GS.freeVector.y;
+  }
+
+
+  /**************************************************************************
+   *
+   * SRP0[]:       Set Reference Point 0
+   * Opcode range: 0x10
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_SRP0( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    exc->GS.rp0 = (FT_UShort)args[0];
+  }
+
+
+  /**************************************************************************
+   *
+   * SRP1[]:       Set Reference Point 1
+   * Opcode range: 0x11
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_SRP1( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    exc->GS.rp1 = (FT_UShort)args[0];
+  }
+
+
+  /**************************************************************************
+   *
+   * SRP2[]:       Set Reference Point 2
+   * Opcode range: 0x12
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_SRP2( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    exc->GS.rp2 = (FT_UShort)args[0];
+  }
+
+
+  /**************************************************************************
+   *
+   * SMD[]:        Set Minimum Distance
+   * Opcode range: 0x1A
+   * Stack:        f26.6 -->
+   */
+  static void
+  Ins_SMD( TT_ExecContext  exc,
+           FT_Long*        args )
+  {
+    exc->GS.minimum_distance = args[0];
+  }
+
+
+  /**************************************************************************
+   *
+   * SCVTCI[]:     Set Control Value Table Cut In
+   * Opcode range: 0x1D
+   * Stack:        f26.6 -->
+   */
+  static void
+  Ins_SCVTCI( TT_ExecContext  exc,
+              FT_Long*        args )
+  {
+    exc->GS.control_value_cutin = (FT_F26Dot6)args[0];
+  }
+
+
+  /**************************************************************************
+   *
+   * SSWCI[]:      Set Single Width Cut In
+   * Opcode range: 0x1E
+   * Stack:        f26.6 -->
+   */
+  static void
+  Ins_SSWCI( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    exc->GS.single_width_cutin = (FT_F26Dot6)args[0];
+  }
+
+
+  /**************************************************************************
+   *
+   * SSW[]:        Set Single Width
+   * Opcode range: 0x1F
+   * Stack:        int32? -->
+   */
+  static void
+  Ins_SSW( TT_ExecContext  exc,
+           FT_Long*        args )
+  {
+    exc->GS.single_width_value = FT_MulFix( args[0],
+                                            exc->tt_metrics.scale );
+  }
+
+
+  /**************************************************************************
+   *
+   * FLIPON[]:     Set auto-FLIP to ON
+   * Opcode range: 0x4D
+   * Stack:        -->
+   */
+  static void
+  Ins_FLIPON( TT_ExecContext  exc )
+  {
+    exc->GS.auto_flip = TRUE;
+  }
+
+
+  /**************************************************************************
+   *
+   * FLIPOFF[]:    Set auto-FLIP to OFF
+   * Opcode range: 0x4E
+   * Stack:        -->
+   */
+  static void
+  Ins_FLIPOFF( TT_ExecContext  exc )
+  {
+    exc->GS.auto_flip = FALSE;
+  }
+
+
+  /**************************************************************************
+   *
+   * SANGW[]:      Set ANGle Weight
+   * Opcode range: 0x7E
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_SANGW( void )
+  {
+    /* instruction not supported anymore */
+  }
+
+
+  /**************************************************************************
+   *
+   * SDB[]:        Set Delta Base
+   * Opcode range: 0x5E
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_SDB( TT_ExecContext  exc,
+           FT_Long*        args )
+  {
+    exc->GS.delta_base = (FT_UShort)args[0];
+  }
+
+
+  /**************************************************************************
+   *
+   * SDS[]:        Set Delta Shift
+   * Opcode range: 0x5F
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_SDS( TT_ExecContext  exc,
+           FT_Long*        args )
+  {
+    if ( (FT_ULong)args[0] > 6UL )
+      exc->error = FT_THROW( Bad_Argument );
+    else
+      exc->GS.delta_shift = (FT_UShort)args[0];
+  }
+
+
+  /**************************************************************************
+   *
+   * RTHG[]:       Round To Half Grid
+   * Opcode range: 0x19
+   * Stack:        -->
+   */
+  static void
+  Ins_RTHG( TT_ExecContext  exc )
+  {
+    exc->GS.round_state = TT_Round_To_Half_Grid;
+    exc->func_round     = (TT_Round_Func)Round_To_Half_Grid;
+  }
+
+
+  /**************************************************************************
+   *
+   * RTG[]:        Round To Grid
+   * Opcode range: 0x18
+   * Stack:        -->
+   */
+  static void
+  Ins_RTG( TT_ExecContext  exc )
+  {
+    exc->GS.round_state = TT_Round_To_Grid;
+    exc->func_round     = (TT_Round_Func)Round_To_Grid;
+  }
+
+
+  /**************************************************************************
+   * RTDG[]:       Round To Double Grid
+   * Opcode range: 0x3D
+   * Stack:        -->
+   */
+  static void
+  Ins_RTDG( TT_ExecContext  exc )
+  {
+    exc->GS.round_state = TT_Round_To_Double_Grid;
+    exc->func_round     = (TT_Round_Func)Round_To_Double_Grid;
+  }
+
+
+  /**************************************************************************
+   * RUTG[]:       Round Up To Grid
+   * Opcode range: 0x7C
+   * Stack:        -->
+   */
+  static void
+  Ins_RUTG( TT_ExecContext  exc )
+  {
+    exc->GS.round_state = TT_Round_Up_To_Grid;
+    exc->func_round     = (TT_Round_Func)Round_Up_To_Grid;
+  }
+
+
+  /**************************************************************************
+   *
+   * RDTG[]:       Round Down To Grid
+   * Opcode range: 0x7D
+   * Stack:        -->
+   */
+  static void
+  Ins_RDTG( TT_ExecContext  exc )
+  {
+    exc->GS.round_state = TT_Round_Down_To_Grid;
+    exc->func_round     = (TT_Round_Func)Round_Down_To_Grid;
+  }
+
+
+  /**************************************************************************
+   *
+   * ROFF[]:       Round OFF
+   * Opcode range: 0x7A
+   * Stack:        -->
+   */
+  static void
+  Ins_ROFF( TT_ExecContext  exc )
+  {
+    exc->GS.round_state = TT_Round_Off;
+    exc->func_round     = (TT_Round_Func)Round_None;
+  }
+
+
+  /**************************************************************************
+   *
+   * SROUND[]:     Super ROUND
+   * Opcode range: 0x76
+   * Stack:        Eint8 -->
+   */
+  static void
+  Ins_SROUND( TT_ExecContext  exc,
+              FT_Long*        args )
+  {
+    SetSuperRound( exc, 0x4000, args[0] );
+
+    exc->GS.round_state = TT_Round_Super;
+    exc->func_round     = (TT_Round_Func)Round_Super;
+  }
+
+
+  /**************************************************************************
+   *
+   * S45ROUND[]:   Super ROUND 45 degrees
+   * Opcode range: 0x77
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_S45ROUND( TT_ExecContext  exc,
+                FT_Long*        args )
+  {
+    SetSuperRound( exc, 0x2D41, args[0] );
+
+    exc->GS.round_state = TT_Round_Super_45;
+    exc->func_round     = (TT_Round_Func)Round_Super_45;
+  }
+
+
+  /**************************************************************************
+   *
+   * GC[a]:        Get Coordinate projected onto
+   * Opcode range: 0x46-0x47
+   * Stack:        uint32 --> f26.6
+   *
+   * XXX: UNDOCUMENTED: Measures from the original glyph must be taken
+   *      along the dual projection vector!
+   */
+  static void
+  Ins_GC( TT_ExecContext  exc,
+          FT_Long*        args )
+  {
+    FT_ULong    L;
+    FT_F26Dot6  R;
+
+
+    L = (FT_ULong)args[0];
+
+    if ( BOUNDSL( L, exc->zp2.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      R = 0;
+    }
+    else
+    {
+      if ( exc->opcode & 1 )
+        R = FAST_DUALPROJ( &exc->zp2.org[L] );
+      else
+        R = FAST_PROJECT( &exc->zp2.cur[L] );
+    }
+
+    args[0] = R;
+  }
+
+
+  /**************************************************************************
+   *
+   * SCFS[]:       Set Coordinate From Stack
+   * Opcode range: 0x48
+   * Stack:        f26.6 uint32 -->
+   *
+   * Formula:
+   *
+   *   OA := OA + ( value - OA.p )/( f.p ) * f
+   */
+  static void
+  Ins_SCFS( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    FT_Long    K;
+    FT_UShort  L;
+
+
+    L = (FT_UShort)args[0];
+
+    if ( BOUNDS( L, exc->zp2.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    K = FAST_PROJECT( &exc->zp2.cur[L] );
+
+    exc->func_move( exc, &exc->zp2, L, SUB_LONG( args[1], K ) );
+
+    /* UNDOCUMENTED!  The MS rasterizer does that with */
+    /* twilight points (confirmed by Greg Hitchcock)   */
+    if ( exc->GS.gep2 == 0 )
+      exc->zp2.org[L] = exc->zp2.cur[L];
+  }
+
+
+  /**************************************************************************
+   *
+   * MD[a]:        Measure Distance
+   * Opcode range: 0x49-0x4A
+   * Stack:        uint32 uint32 --> f26.6
+   *
+   * XXX: UNDOCUMENTED: Measure taken in the original glyph must be along
+   *                    the dual projection vector.
+   *
+   * XXX: UNDOCUMENTED: Flag attributes are inverted!
+   *                      0 => measure distance in original outline
+   *                      1 => measure distance in grid-fitted outline
+   *
+   * XXX: UNDOCUMENTED: `zp0 - zp1', and not `zp2 - zp1!
+   */
+  static void
+  Ins_MD( TT_ExecContext  exc,
+          FT_Long*        args )
+  {
+    FT_UShort   K, L;
+    FT_F26Dot6  D;
+
+
+    K = (FT_UShort)args[1];
+    L = (FT_UShort)args[0];
+
+    if ( BOUNDS( L, exc->zp0.n_points ) ||
+         BOUNDS( K, exc->zp1.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      D = 0;
+    }
+    else
+    {
+      if ( exc->opcode & 1 )
+        D = PROJECT( exc->zp0.cur + L, exc->zp1.cur + K );
+      else
+      {
+        /* XXX: UNDOCUMENTED: twilight zone special case */
+
+        if ( exc->GS.gep0 == 0 || exc->GS.gep1 == 0 )
+        {
+          FT_Vector*  vec1 = exc->zp0.org + L;
+          FT_Vector*  vec2 = exc->zp1.org + K;
+
+
+          D = DUALPROJ( vec1, vec2 );
+        }
+        else
+        {
+          FT_Vector*  vec1 = exc->zp0.orus + L;
+          FT_Vector*  vec2 = exc->zp1.orus + K;
+
+
+          if ( exc->metrics.x_scale == exc->metrics.y_scale )
+          {
+            /* this should be faster */
+            D = DUALPROJ( vec1, vec2 );
+            D = FT_MulFix( D, exc->metrics.x_scale );
+          }
+          else
+          {
+            FT_Vector  vec;
+
+
+            vec.x = FT_MulFix( vec1->x - vec2->x, exc->metrics.x_scale );
+            vec.y = FT_MulFix( vec1->y - vec2->y, exc->metrics.y_scale );
+
+            D = FAST_DUALPROJ( &vec );
+          }
+        }
+      }
+    }
+
+    args[0] = D;
+  }
+
+
+  /**************************************************************************
+   *
+   * SDPvTL[a]:    Set Dual PVector to Line
+   * Opcode range: 0x86-0x87
+   * Stack:        uint32 uint32 -->
+   */
+  static void
+  Ins_SDPVTL( TT_ExecContext  exc,
+              FT_Long*        args )
+  {
+    FT_Long    A, B, C;
+    FT_UShort  p1, p2;            /* was FT_Int in pas type ERROR */
+
+    FT_Byte  opcode = exc->opcode;
+
+
+    p1 = (FT_UShort)args[1];
+    p2 = (FT_UShort)args[0];
+
+    if ( BOUNDS( p2, exc->zp1.n_points ) ||
+         BOUNDS( p1, exc->zp2.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    {
+      FT_Vector*  v1 = exc->zp1.org + p2;
+      FT_Vector*  v2 = exc->zp2.org + p1;
+
+
+      A = SUB_LONG( v1->x, v2->x );
+      B = SUB_LONG( v1->y, v2->y );
+
+      /* If v1 == v2, SDPvTL behaves the same as */
+      /* SVTCA[X], respectively.                 */
+      /*                                         */
+      /* Confirmed by Greg Hitchcock.            */
+
+      if ( A == 0 && B == 0 )
+      {
+        A      = 0x4000;
+        opcode = 0;
+      }
+    }
+
+    if ( ( opcode & 1 ) != 0 )
+    {
+      C = B;   /* counter-clockwise rotation */
+      B = A;
+      A = NEG_LONG( C );
+    }
+
+    Normalize( A, B, &exc->GS.dualVector );
+
+    {
+      FT_Vector*  v1 = exc->zp1.cur + p2;
+      FT_Vector*  v2 = exc->zp2.cur + p1;
+
+
+      A = SUB_LONG( v1->x, v2->x );
+      B = SUB_LONG( v1->y, v2->y );
+
+      if ( A == 0 && B == 0 )
+      {
+        A      = 0x4000;
+        opcode = 0;
+      }
+    }
+
+    if ( ( opcode & 1 ) != 0 )
+    {
+      C = B;   /* counter-clockwise rotation */
+      B = A;
+      A = NEG_LONG( C );
+    }
+
+    Normalize( A, B, &exc->GS.projVector );
+    Compute_Funcs( exc );
+  }
+
+
+  /**************************************************************************
+   *
+   * SZP0[]:       Set Zone Pointer 0
+   * Opcode range: 0x13
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_SZP0( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    switch ( (FT_Int)args[0] )
+    {
+    case 0:
+      exc->zp0 = exc->twilight;
+      break;
+
+    case 1:
+      exc->zp0 = exc->pts;
+      break;
+
+    default:
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    exc->GS.gep0 = (FT_UShort)args[0];
+  }
+
+
+  /**************************************************************************
+   *
+   * SZP1[]:       Set Zone Pointer 1
+   * Opcode range: 0x14
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_SZP1( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    switch ( (FT_Int)args[0] )
+    {
+    case 0:
+      exc->zp1 = exc->twilight;
+      break;
+
+    case 1:
+      exc->zp1 = exc->pts;
+      break;
+
+    default:
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    exc->GS.gep1 = (FT_UShort)args[0];
+  }
+
+
+  /**************************************************************************
+   *
+   * SZP2[]:       Set Zone Pointer 2
+   * Opcode range: 0x15
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_SZP2( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    switch ( (FT_Int)args[0] )
+    {
+    case 0:
+      exc->zp2 = exc->twilight;
+      break;
+
+    case 1:
+      exc->zp2 = exc->pts;
+      break;
+
+    default:
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    exc->GS.gep2 = (FT_UShort)args[0];
+  }
+
+
+  /**************************************************************************
+   *
+   * SZPS[]:       Set Zone PointerS
+   * Opcode range: 0x16
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_SZPS( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    switch ( (FT_Int)args[0] )
+    {
+    case 0:
+      exc->zp0 = exc->twilight;
+      break;
+
+    case 1:
+      exc->zp0 = exc->pts;
+      break;
+
+    default:
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    exc->zp1 = exc->zp0;
+    exc->zp2 = exc->zp0;
+
+    exc->GS.gep0 = (FT_UShort)args[0];
+    exc->GS.gep1 = (FT_UShort)args[0];
+    exc->GS.gep2 = (FT_UShort)args[0];
+  }
+
+
+  /**************************************************************************
+   *
+   * INSTCTRL[]:   INSTruction ConTRoL
+   * Opcode range: 0x8E
+   * Stack:        int32 int32 -->
+   */
+  static void
+  Ins_INSTCTRL( TT_ExecContext  exc,
+                FT_Long*        args )
+  {
+    FT_ULong  K, L, Kf;
+
+
+    K = (FT_ULong)args[1];
+    L = (FT_ULong)args[0];
+
+    /* selector values cannot be `OR'ed;                 */
+    /* they are indices starting with index 1, not flags */
+    if ( K < 1 || K > 3 )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    /* convert index to flag value */
+    Kf = 1 << ( K - 1 );
+
+    if ( L != 0 )
+    {
+      /* arguments to selectors look like flag values */
+      if ( L != Kf )
+      {
+        if ( exc->pedantic_hinting )
+          exc->error = FT_THROW( Invalid_Reference );
+        return;
+      }
+    }
+
+    /* INSTCTRL should only be used in the CVT program */
+    if ( exc->iniRange == tt_coderange_cvt )
+    {
+      exc->GS.instruct_control &= ~(FT_Byte)Kf;
+      exc->GS.instruct_control |= (FT_Byte)L;
+    }
+
+    /* except to change the subpixel flags temporarily */
+    else if ( exc->iniRange == tt_coderange_glyph && K == 3 )
+    {
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+      /* Native ClearType fonts sign a waiver that turns off all backward  */
+      /* compatibility hacks and lets them program points to the grid like */
+      /* it's 1996.  They might sign a waiver for just one glyph, though.  */
+      if ( SUBPIXEL_HINTING_MINIMAL )
+        exc->backward_compatibility = !FT_BOOL( L == 4 );
+#endif
+    }
+    else if ( exc->pedantic_hinting )
+      exc->error = FT_THROW( Invalid_Reference );
+  }
+
+
+  /**************************************************************************
+   *
+   * SCANCTRL[]:   SCAN ConTRoL
+   * Opcode range: 0x85
+   * Stack:        uint32? -->
+   */
+  static void
+  Ins_SCANCTRL( TT_ExecContext  exc,
+                FT_Long*        args )
+  {
+    FT_Int  A;
+
+
+    /* Get Threshold */
+    A = (FT_Int)( args[0] & 0xFF );
+
+    if ( A == 0xFF )
+    {
+      exc->GS.scan_control = TRUE;
+      return;
+    }
+    else if ( A == 0 )
+    {
+      exc->GS.scan_control = FALSE;
+      return;
+    }
+
+    if ( ( args[0] & 0x100 ) != 0 && exc->tt_metrics.ppem <= A )
+      exc->GS.scan_control = TRUE;
+
+    if ( ( args[0] & 0x200 ) != 0 && exc->tt_metrics.rotated )
+      exc->GS.scan_control = TRUE;
+
+    if ( ( args[0] & 0x400 ) != 0 && exc->tt_metrics.stretched )
+      exc->GS.scan_control = TRUE;
+
+    if ( ( args[0] & 0x800 ) != 0 && exc->tt_metrics.ppem > A )
+      exc->GS.scan_control = FALSE;
+
+    if ( ( args[0] & 0x1000 ) != 0 && exc->tt_metrics.rotated )
+      exc->GS.scan_control = FALSE;
+
+    if ( ( args[0] & 0x2000 ) != 0 && exc->tt_metrics.stretched )
+      exc->GS.scan_control = FALSE;
+  }
+
+
+  /**************************************************************************
+   *
+   * SCANTYPE[]:   SCAN TYPE
+   * Opcode range: 0x8D
+   * Stack:        uint16 -->
+   */
+  static void
+  Ins_SCANTYPE( TT_ExecContext  exc,
+                FT_Long*        args )
+  {
+    if ( args[0] >= 0 )
+      exc->GS.scan_type = (FT_Int)args[0] & 0xFFFF;
+  }
+
+
+  /**************************************************************************
+   *
+   * MANAGING OUTLINES
+   *
+   */
+
+
+  /**************************************************************************
+   *
+   * FLIPPT[]:     FLIP PoinT
+   * Opcode range: 0x80
+   * Stack:        uint32... -->
+   */
+  static void
+  Ins_FLIPPT( TT_ExecContext  exc )
+  {
+    FT_UShort  point;
+
+
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    /* See `ttinterp.h' for details on backward compatibility mode. */
+    if ( SUBPIXEL_HINTING_MINIMAL    &&
+         exc->backward_compatibility &&
+         exc->iupx_called            &&
+         exc->iupy_called            )
+      goto Fail;
+#endif
+
+    if ( exc->top < exc->GS.loop )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Too_Few_Arguments );
+      goto Fail;
+    }
+
+    while ( exc->GS.loop > 0 )
+    {
+      exc->args--;
+
+      point = (FT_UShort)exc->stack[exc->args];
+
+      if ( BOUNDS( point, exc->pts.n_points ) )
+      {
+        if ( exc->pedantic_hinting )
+        {
+          exc->error = FT_THROW( Invalid_Reference );
+          return;
+        }
+      }
+      else
+        exc->pts.tags[point] ^= FT_CURVE_TAG_ON;
+
+      exc->GS.loop--;
+    }
+
+  Fail:
+    exc->GS.loop = 1;
+    exc->new_top = exc->args;
+  }
+
+
+  /**************************************************************************
+   *
+   * FLIPRGON[]:   FLIP RanGe ON
+   * Opcode range: 0x81
+   * Stack:        uint32 uint32 -->
+   */
+  static void
+  Ins_FLIPRGON( TT_ExecContext  exc,
+                FT_Long*        args )
+  {
+    FT_UShort  I, K, L;
+
+
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    /* See `ttinterp.h' for details on backward compatibility mode. */
+    if ( SUBPIXEL_HINTING_MINIMAL    &&
+         exc->backward_compatibility &&
+         exc->iupx_called            &&
+         exc->iupy_called            )
+      return;
+#endif
+
+    K = (FT_UShort)args[1];
+    L = (FT_UShort)args[0];
+
+    if ( BOUNDS( K, exc->pts.n_points ) ||
+         BOUNDS( L, exc->pts.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    for ( I = L; I <= K; I++ )
+      exc->pts.tags[I] |= FT_CURVE_TAG_ON;
+  }
+
+
+  /**************************************************************************
+   *
+   * FLIPRGOFF:    FLIP RanGe OFF
+   * Opcode range: 0x82
+   * Stack:        uint32 uint32 -->
+   */
+  static void
+  Ins_FLIPRGOFF( TT_ExecContext  exc,
+                 FT_Long*        args )
+  {
+    FT_UShort  I, K, L;
+
+
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    /* See `ttinterp.h' for details on backward compatibility mode. */
+    if ( SUBPIXEL_HINTING_MINIMAL    &&
+         exc->backward_compatibility &&
+         exc->iupx_called            &&
+         exc->iupy_called            )
+      return;
+#endif
+
+    K = (FT_UShort)args[1];
+    L = (FT_UShort)args[0];
+
+    if ( BOUNDS( K, exc->pts.n_points ) ||
+         BOUNDS( L, exc->pts.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    for ( I = L; I <= K; I++ )
+      exc->pts.tags[I] &= ~FT_CURVE_TAG_ON;
+  }
+
+
+  static FT_Bool
+  Compute_Point_Displacement( TT_ExecContext  exc,
+                              FT_F26Dot6*     x,
+                              FT_F26Dot6*     y,
+                              TT_GlyphZone    zone,
+                              FT_UShort*      refp )
+  {
+    TT_GlyphZoneRec  zp;
+    FT_UShort        p;
+    FT_F26Dot6       d;
+
+
+    if ( exc->opcode & 1 )
+    {
+      zp = exc->zp0;
+      p  = exc->GS.rp1;
+    }
+    else
+    {
+      zp = exc->zp1;
+      p  = exc->GS.rp2;
+    }
+
+    if ( BOUNDS( p, zp.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      *refp = 0;
+      return FAILURE;
+    }
+
+    *zone = zp;
+    *refp = p;
+
+    d = PROJECT( zp.cur + p, zp.org + p );
+
+    *x = FT_MulDiv( d, (FT_Long)exc->GS.freeVector.x, exc->F_dot_P );
+    *y = FT_MulDiv( d, (FT_Long)exc->GS.freeVector.y, exc->F_dot_P );
+
+    return SUCCESS;
+  }
+
+
+  /* See `ttinterp.h' for details on backward compatibility mode. */
+  static void
+  Move_Zp2_Point( TT_ExecContext  exc,
+                  FT_UShort       point,
+                  FT_F26Dot6      dx,
+                  FT_F26Dot6      dy,
+                  FT_Bool         touch )
+  {
+    if ( exc->GS.freeVector.x != 0 )
+    {
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+      if ( !( SUBPIXEL_HINTING_MINIMAL    &&
+              exc->backward_compatibility ) )
+#endif
+        exc->zp2.cur[point].x = ADD_LONG( exc->zp2.cur[point].x, dx );
+
+      if ( touch )
+        exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_X;
+    }
+
+    if ( exc->GS.freeVector.y != 0 )
+    {
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+      if ( !( SUBPIXEL_HINTING_MINIMAL    &&
+              exc->backward_compatibility &&
+              exc->iupx_called            &&
+              exc->iupy_called            ) )
+#endif
+        exc->zp2.cur[point].y = ADD_LONG( exc->zp2.cur[point].y, dy );
+
+      if ( touch )
+        exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_Y;
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * SHP[a]:       SHift Point by the last point
+   * Opcode range: 0x32-0x33
+   * Stack:        uint32... -->
+   */
+  static void
+  Ins_SHP( TT_ExecContext  exc )
+  {
+    TT_GlyphZoneRec  zp;
+    FT_UShort        refp;
+
+    FT_F26Dot6       dx, dy;
+    FT_UShort        point;
+
+
+    if ( exc->top < exc->GS.loop )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      goto Fail;
+    }
+
+    if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) )
+      return;
+
+    while ( exc->GS.loop > 0 )
+    {
+      exc->args--;
+      point = (FT_UShort)exc->stack[exc->args];
+
+      if ( BOUNDS( point, exc->zp2.n_points ) )
+      {
+        if ( exc->pedantic_hinting )
+        {
+          exc->error = FT_THROW( Invalid_Reference );
+          return;
+        }
+      }
+      else
+        Move_Zp2_Point( exc, point, dx, dy, TRUE );
+
+      exc->GS.loop--;
+    }
+
+  Fail:
+    exc->GS.loop = 1;
+    exc->new_top = exc->args;
+  }
+
+
+  /**************************************************************************
+   *
+   * SHC[a]:       SHift Contour
+   * Opcode range: 0x34-35
+   * Stack:        uint32 -->
+   *
+   * UNDOCUMENTED: According to Greg Hitchcock, there is one (virtual)
+   *               contour in the twilight zone, namely contour number
+   *               zero which includes all points of it.
+   */
+  static void
+  Ins_SHC( TT_ExecContext  exc,
+           FT_Long*        args )
+  {
+    TT_GlyphZoneRec  zp;
+    FT_UShort        refp;
+    FT_F26Dot6       dx, dy;
+
+    FT_Short         contour, bounds;
+    FT_UShort        start, limit, i;
+
+
+    contour = (FT_Short)args[0];
+    bounds  = ( exc->GS.gep2 == 0 ) ? 1 : exc->zp2.n_contours;
+
+    if ( BOUNDS( contour, bounds ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) )
+      return;
+
+    if ( contour == 0 )
+      start = 0;
+    else
+      start = (FT_UShort)( exc->zp2.contours[contour - 1] + 1 -
+                           exc->zp2.first_point );
+
+    /* we use the number of points if in the twilight zone */
+    if ( exc->GS.gep2 == 0 )
+      limit = exc->zp2.n_points;
+    else
+      limit = (FT_UShort)( exc->zp2.contours[contour] -
+                           exc->zp2.first_point + 1 );
+
+    for ( i = start; i < limit; i++ )
+    {
+      if ( zp.cur != exc->zp2.cur || refp != i )
+        Move_Zp2_Point( exc, i, dx, dy, TRUE );
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * SHZ[a]:       SHift Zone
+   * Opcode range: 0x36-37
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_SHZ( TT_ExecContext  exc,
+           FT_Long*        args )
+  {
+    TT_GlyphZoneRec  zp;
+    FT_UShort        refp;
+    FT_F26Dot6       dx,
+                     dy;
+
+    FT_UShort        limit, i;
+
+
+    if ( BOUNDS( args[0], 2 ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    if ( Compute_Point_Displacement( exc, &dx, &dy, &zp, &refp ) )
+      return;
+
+    /* XXX: UNDOCUMENTED! SHZ doesn't move the phantom points.     */
+    /*      Twilight zone has no real contours, so use `n_points'. */
+    /*      Normal zone's `n_points' includes phantoms, so must    */
+    /*      use end of last contour.                               */
+    if ( exc->GS.gep2 == 0 )
+      limit = (FT_UShort)exc->zp2.n_points;
+    else if ( exc->GS.gep2 == 1 && exc->zp2.n_contours > 0 )
+      limit = (FT_UShort)( exc->zp2.contours[exc->zp2.n_contours - 1] + 1 );
+    else
+      limit = 0;
+
+    /* XXX: UNDOCUMENTED! SHZ doesn't touch the points */
+    for ( i = 0; i < limit; i++ )
+    {
+      if ( zp.cur != exc->zp2.cur || refp != i )
+        Move_Zp2_Point( exc, i, dx, dy, FALSE );
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * SHPIX[]:      SHift points by a PIXel amount
+   * Opcode range: 0x38
+   * Stack:        f26.6 uint32... -->
+   */
+  static void
+  Ins_SHPIX( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    FT_F26Dot6  dx, dy;
+    FT_UShort   point;
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    FT_Bool     in_twilight = FT_BOOL( exc->GS.gep0 == 0 ||
+                                       exc->GS.gep1 == 0 ||
+                                       exc->GS.gep2 == 0 );
+#endif
+
+
+
+    if ( exc->top < exc->GS.loop + 1 )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      goto Fail;
+    }
+
+    dx = TT_MulFix14( args[0], exc->GS.freeVector.x );
+    dy = TT_MulFix14( args[0], exc->GS.freeVector.y );
+
+    while ( exc->GS.loop > 0 )
+    {
+      exc->args--;
+
+      point = (FT_UShort)exc->stack[exc->args];
+
+      if ( BOUNDS( point, exc->zp2.n_points ) )
+      {
+        if ( exc->pedantic_hinting )
+        {
+          exc->error = FT_THROW( Invalid_Reference );
+          return;
+        }
+      }
+      else
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+      if ( SUBPIXEL_HINTING_MINIMAL    &&
+           exc->backward_compatibility )
+      {
+        /* Special case: allow SHPIX to move points in the twilight zone.  */
+        /* Otherwise, treat SHPIX the same as DELTAP.  Unbreaks various    */
+        /* fonts such as older versions of Rokkitt and DTL Argo T Light    */
+        /* that would glitch severely after calling ALIGNRP after a        */
+        /* blocked SHPIX.                                                  */
+        if ( in_twilight                                                ||
+             ( !( exc->iupx_called && exc->iupy_called )              &&
+               ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) ||
+                 ( exc->zp2.tags[point] & FT_CURVE_TAG_TOUCH_Y )    ) ) )
+          Move_Zp2_Point( exc, point, 0, dy, TRUE );
+      }
+      else
+#endif
+        Move_Zp2_Point( exc, point, dx, dy, TRUE );
+
+      exc->GS.loop--;
+    }
+
+  Fail:
+    exc->GS.loop = 1;
+    exc->new_top = exc->args;
+  }
+
+
+  /**************************************************************************
+   *
+   * MSIRP[a]:     Move Stack Indirect Relative Position
+   * Opcode range: 0x3A-0x3B
+   * Stack:        f26.6 uint32 -->
+   */
+  static void
+  Ins_MSIRP( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    FT_UShort   point = 0;
+    FT_F26Dot6  distance;
+
+
+    point = (FT_UShort)args[0];
+
+    if ( BOUNDS( point,       exc->zp1.n_points ) ||
+         BOUNDS( exc->GS.rp0, exc->zp0.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    /* UNDOCUMENTED!  The MS rasterizer does that with */
+    /* twilight points (confirmed by Greg Hitchcock)   */
+    if ( exc->GS.gep1 == 0 )
+    {
+      exc->zp1.org[point] = exc->zp0.org[exc->GS.rp0];
+      exc->func_move_orig( exc, &exc->zp1, point, args[1] );
+      exc->zp1.cur[point] = exc->zp1.org[point];
+    }
+
+    distance = PROJECT( exc->zp1.cur + point, exc->zp0.cur + exc->GS.rp0 );
+
+    exc->func_move( exc,
+                    &exc->zp1,
+                    point,
+                    SUB_LONG( args[1], distance ) );
+
+    exc->GS.rp1 = exc->GS.rp0;
+    exc->GS.rp2 = point;
+
+    if ( ( exc->opcode & 1 ) != 0 )
+      exc->GS.rp0 = point;
+  }
+
+
+  /**************************************************************************
+   *
+   * MDAP[a]:      Move Direct Absolute Point
+   * Opcode range: 0x2E-0x2F
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_MDAP( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    FT_UShort   point;
+    FT_F26Dot6  cur_dist;
+    FT_F26Dot6  distance;
+
+
+    point = (FT_UShort)args[0];
+
+    if ( BOUNDS( point, exc->zp0.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    if ( ( exc->opcode & 1 ) != 0 )
+    {
+      cur_dist = FAST_PROJECT( &exc->zp0.cur[point] );
+      distance = SUB_LONG( exc->func_round( exc, cur_dist, 3 ), cur_dist );
+    }
+    else
+      distance = 0;
+
+    exc->func_move( exc, &exc->zp0, point, distance );
+
+    exc->GS.rp0 = point;
+    exc->GS.rp1 = point;
+  }
+
+
+  /**************************************************************************
+   *
+   * MIAP[a]:      Move Indirect Absolute Point
+   * Opcode range: 0x3E-0x3F
+   * Stack:        uint32 uint32 -->
+   */
+  static void
+  Ins_MIAP( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    FT_ULong    cvtEntry;
+    FT_UShort   point;
+    FT_F26Dot6  distance;
+    FT_F26Dot6  org_dist;
+
+
+    cvtEntry = (FT_ULong)args[1];
+    point    = (FT_UShort)args[0];
+
+    if ( BOUNDS( point,     exc->zp0.n_points ) ||
+         BOUNDSL( cvtEntry, exc->cvtSize )      )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      goto Fail;
+    }
+
+    /* UNDOCUMENTED!                                                      */
+    /*                                                                    */
+    /* The behaviour of an MIAP instruction is quite different when used  */
+    /* in the twilight zone.                                              */
+    /*                                                                    */
+    /* First, no control value cut-in test is performed as it would fail  */
+    /* anyway.  Second, the original point, i.e. (org_x,org_y) of         */
+    /* zp0.point, is set to the absolute, unrounded distance found in the */
+    /* CVT.                                                               */
+    /*                                                                    */
+    /* This is used in the CVT programs of the Microsoft fonts Arial,     */
+    /* Times, etc., in order to re-adjust some key font heights.  It      */
+    /* allows the use of the IP instruction in the twilight zone, which   */
+    /* otherwise would be invalid according to the specification.         */
+    /*                                                                    */
+    /* We implement it with a special sequence for the twilight zone.     */
+    /* This is a bad hack, but it seems to work.                          */
+    /*                                                                    */
+    /* Confirmed by Greg Hitchcock.                                       */
+
+    distance = exc->func_read_cvt( exc, cvtEntry );
+
+    if ( exc->GS.gep0 == 0 )   /* If in twilight zone */
+    {
+      exc->zp0.org[point].x = TT_MulFix14( distance,
+                                             exc->GS.freeVector.x );
+      exc->zp0.org[point].y = TT_MulFix14( distance,
+                                           exc->GS.freeVector.y );
+      exc->zp0.cur[point]   = exc->zp0.org[point];
+    }
+
+    org_dist = FAST_PROJECT( &exc->zp0.cur[point] );
+
+    if ( ( exc->opcode & 1 ) != 0 )   /* rounding and control cut-in flag */
+    {
+      FT_F26Dot6  control_value_cutin = exc->GS.control_value_cutin;
+      FT_F26Dot6  delta;
+
+
+      delta = SUB_LONG( distance, org_dist );
+      if ( delta < 0 )
+        delta = NEG_LONG( delta );
+
+      if ( delta > control_value_cutin )
+        distance = org_dist;
+
+      distance = exc->func_round( exc, distance, 3 );
+    }
+
+    exc->func_move( exc, &exc->zp0, point, SUB_LONG( distance, org_dist ) );
+
+  Fail:
+    exc->GS.rp0 = point;
+    exc->GS.rp1 = point;
+  }
+
+
+  /**************************************************************************
+   *
+   * MDRP[abcde]:  Move Direct Relative Point
+   * Opcode range: 0xC0-0xDF
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_MDRP( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    FT_UShort   point = 0;
+    FT_F26Dot6  org_dist, distance;
+
+
+    point = (FT_UShort)args[0];
+
+    if ( BOUNDS( point,       exc->zp1.n_points ) ||
+         BOUNDS( exc->GS.rp0, exc->zp0.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      goto Fail;
+    }
+
+    /* XXX: Is there some undocumented feature while in the */
+    /*      twilight zone?                                  */
+
+    /* XXX: UNDOCUMENTED: twilight zone special case */
+
+    if ( exc->GS.gep0 == 0 || exc->GS.gep1 == 0 )
+    {
+      FT_Vector*  vec1 = &exc->zp1.org[point];
+      FT_Vector*  vec2 = &exc->zp0.org[exc->GS.rp0];
+
+
+      org_dist = DUALPROJ( vec1, vec2 );
+    }
+    else
+    {
+      FT_Vector*  vec1 = &exc->zp1.orus[point];
+      FT_Vector*  vec2 = &exc->zp0.orus[exc->GS.rp0];
+
+
+      if ( exc->metrics.x_scale == exc->metrics.y_scale )
+      {
+        /* this should be faster */
+        org_dist = DUALPROJ( vec1, vec2 );
+        org_dist = FT_MulFix( org_dist, exc->metrics.x_scale );
+      }
+      else
+      {
+        FT_Vector  vec;
+
+
+        vec.x = FT_MulFix( SUB_LONG( vec1->x, vec2->x ),
+                           exc->metrics.x_scale );
+        vec.y = FT_MulFix( SUB_LONG( vec1->y, vec2->y ),
+                           exc->metrics.y_scale );
+
+        org_dist = FAST_DUALPROJ( &vec );
+      }
+    }
+
+    /* single width cut-in test */
+
+    /* |org_dist - single_width_value| < single_width_cutin */
+    if ( exc->GS.single_width_cutin > 0          &&
+         org_dist < exc->GS.single_width_value +
+                      exc->GS.single_width_cutin &&
+         org_dist > exc->GS.single_width_value -
+                      exc->GS.single_width_cutin )
+    {
+      if ( org_dist >= 0 )
+        org_dist = exc->GS.single_width_value;
+      else
+        org_dist = -exc->GS.single_width_value;
+    }
+
+    /* round flag */
+
+    if ( ( exc->opcode & 4 ) != 0 )
+    {
+      distance = exc->func_round( exc, org_dist, exc->opcode & 3 );
+    }
+    else
+      distance = Round_None( exc, org_dist, exc->opcode & 3 );
+
+    /* minimum distance flag */
+
+    if ( ( exc->opcode & 8 ) != 0 )
+    {
+      FT_F26Dot6  minimum_distance = exc->GS.minimum_distance;
+
+
+      if ( org_dist >= 0 )
+      {
+        if ( distance < minimum_distance )
+          distance = minimum_distance;
+      }
+      else
+      {
+        if ( distance > NEG_LONG( minimum_distance ) )
+          distance = NEG_LONG( minimum_distance );
+      }
+    }
+
+    /* now move the point */
+
+    org_dist = PROJECT( exc->zp1.cur + point, exc->zp0.cur + exc->GS.rp0 );
+
+    exc->func_move( exc, &exc->zp1, point, SUB_LONG( distance, org_dist ) );
+
+  Fail:
+    exc->GS.rp1 = exc->GS.rp0;
+    exc->GS.rp2 = point;
+
+    if ( ( exc->opcode & 16 ) != 0 )
+      exc->GS.rp0 = point;
+  }
+
+
+  /**************************************************************************
+   *
+   * MIRP[abcde]:  Move Indirect Relative Point
+   * Opcode range: 0xE0-0xFF
+   * Stack:        int32? uint32 -->
+   */
+  static void
+  Ins_MIRP( TT_ExecContext  exc,
+            FT_Long*        args )
+  {
+    FT_UShort   point;
+    FT_ULong    cvtEntry;
+
+    FT_F26Dot6  cvt_dist,
+                distance,
+                cur_dist,
+                org_dist;
+
+    FT_F26Dot6  delta;
+
+
+    point    = (FT_UShort)args[0];
+    cvtEntry = (FT_ULong)( ADD_LONG( args[1], 1 ) );
+
+    /* XXX: UNDOCUMENTED! cvt[-1] = 0 always */
+
+    if ( BOUNDS( point,       exc->zp1.n_points ) ||
+         BOUNDSL( cvtEntry,   exc->cvtSize + 1 )  ||
+         BOUNDS( exc->GS.rp0, exc->zp0.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      goto Fail;
+    }
+
+    if ( !cvtEntry )
+      cvt_dist = 0;
+    else
+      cvt_dist = exc->func_read_cvt( exc, cvtEntry - 1 );
+
+    /* single width test */
+
+    delta = SUB_LONG( cvt_dist, exc->GS.single_width_value );
+    if ( delta < 0 )
+      delta = NEG_LONG( delta );
+
+    if ( delta < exc->GS.single_width_cutin )
+    {
+      if ( cvt_dist >= 0 )
+        cvt_dist =  exc->GS.single_width_value;
+      else
+        cvt_dist = -exc->GS.single_width_value;
+    }
+
+    /* UNDOCUMENTED!  The MS rasterizer does that with */
+    /* twilight points (confirmed by Greg Hitchcock)   */
+    if ( exc->GS.gep1 == 0 )
+    {
+      exc->zp1.org[point].x = ADD_LONG(
+                                exc->zp0.org[exc->GS.rp0].x,
+                                TT_MulFix14( cvt_dist,
+                                             exc->GS.freeVector.x ) );
+      exc->zp1.org[point].y = ADD_LONG(
+                                exc->zp0.org[exc->GS.rp0].y,
+                                TT_MulFix14( cvt_dist,
+                                             exc->GS.freeVector.y ) );
+      exc->zp1.cur[point]   = exc->zp1.org[point];
+    }
+
+    org_dist = DUALPROJ( &exc->zp1.org[point], &exc->zp0.org[exc->GS.rp0] );
+    cur_dist = PROJECT ( &exc->zp1.cur[point], &exc->zp0.cur[exc->GS.rp0] );
+
+    /* auto-flip test */
+
+    if ( exc->GS.auto_flip )
+    {
+      if ( ( org_dist ^ cvt_dist ) < 0 )
+        cvt_dist = NEG_LONG( cvt_dist );
+    }
+
+    /* control value cut-in and round */
+
+    if ( ( exc->opcode & 4 ) != 0 )
+    {
+      /* XXX: UNDOCUMENTED!  Only perform cut-in test when both points */
+      /*      refer to the same zone.                                  */
+
+      if ( exc->GS.gep0 == exc->GS.gep1 )
+      {
+        FT_F26Dot6  control_value_cutin = exc->GS.control_value_cutin;
+
+
+        /* XXX: According to Greg Hitchcock, the following wording is */
+        /*      the right one:                                        */
+        /*                                                            */
+        /*        When the absolute difference between the value in   */
+        /*        the table [CVT] and the measurement directly from   */
+        /*        the outline is _greater_ than the cut_in value, the */
+        /*        outline measurement is used.                        */
+        /*                                                            */
+        /*      This is from `instgly.doc'.  The description in       */
+        /*      `ttinst2.doc', version 1.66, is thus incorrect since  */
+        /*      it implies `>=' instead of `>'.                       */
+
+        delta = SUB_LONG( cvt_dist, org_dist );
+        if ( delta < 0 )
+          delta = NEG_LONG( delta );
+
+        if ( delta > control_value_cutin )
+          cvt_dist = org_dist;
+      }
+
+      distance = exc->func_round( exc, cvt_dist, exc->opcode & 3 );
+    }
+    else
+      distance = Round_None( exc, cvt_dist, exc->opcode & 3 );
+
+    /* minimum distance test */
+
+    if ( ( exc->opcode & 8 ) != 0 )
+    {
+      FT_F26Dot6  minimum_distance    = exc->GS.minimum_distance;
+
+
+      if ( org_dist >= 0 )
+      {
+        if ( distance < minimum_distance )
+          distance = minimum_distance;
+      }
+      else
+      {
+        if ( distance > NEG_LONG( minimum_distance ) )
+          distance = NEG_LONG( minimum_distance );
+      }
+    }
+
+    exc->func_move( exc,
+                    &exc->zp1,
+                    point,
+                    SUB_LONG( distance, cur_dist ) );
+
+  Fail:
+    exc->GS.rp1 = exc->GS.rp0;
+
+    if ( ( exc->opcode & 16 ) != 0 )
+      exc->GS.rp0 = point;
+
+    exc->GS.rp2 = point;
+  }
+
+
+  /**************************************************************************
+   *
+   * ALIGNRP[]:    ALIGN Relative Point
+   * Opcode range: 0x3C
+   * Stack:        uint32 uint32... -->
+   */
+  static void
+  Ins_ALIGNRP( TT_ExecContext  exc )
+  {
+    FT_UShort   point;
+    FT_F26Dot6  distance;
+
+
+    if ( exc->top < exc->GS.loop                  ||
+         BOUNDS( exc->GS.rp0, exc->zp0.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      goto Fail;
+    }
+
+    while ( exc->GS.loop > 0 )
+    {
+      exc->args--;
+
+      point = (FT_UShort)exc->stack[exc->args];
+
+      if ( BOUNDS( point, exc->zp1.n_points ) )
+      {
+        if ( exc->pedantic_hinting )
+        {
+          exc->error = FT_THROW( Invalid_Reference );
+          return;
+        }
+      }
+      else
+      {
+        distance = PROJECT( exc->zp1.cur + point,
+                            exc->zp0.cur + exc->GS.rp0 );
+
+        exc->func_move( exc, &exc->zp1, point, NEG_LONG( distance ) );
+      }
+
+      exc->GS.loop--;
+    }
+
+  Fail:
+    exc->GS.loop = 1;
+    exc->new_top = exc->args;
+  }
+
+
+  /**************************************************************************
+   *
+   * ISECT[]:      moves point to InterSECTion
+   * Opcode range: 0x0F
+   * Stack:        5 * uint32 -->
+   */
+  static void
+  Ins_ISECT( TT_ExecContext  exc,
+             FT_Long*        args )
+  {
+    FT_UShort   point,
+                a0, a1,
+                b0, b1;
+
+    FT_F26Dot6  discriminant, dotproduct;
+
+    FT_F26Dot6  dx,  dy,
+                dax, day,
+                dbx, dby;
+
+    FT_F26Dot6  val;
+
+    FT_Vector   R;
+
+
+    point = (FT_UShort)args[0];
+
+    a0 = (FT_UShort)args[1];
+    a1 = (FT_UShort)args[2];
+    b0 = (FT_UShort)args[3];
+    b1 = (FT_UShort)args[4];
+
+    if ( BOUNDS( b0,    exc->zp0.n_points ) ||
+         BOUNDS( b1,    exc->zp0.n_points ) ||
+         BOUNDS( a0,    exc->zp1.n_points ) ||
+         BOUNDS( a1,    exc->zp1.n_points ) ||
+         BOUNDS( point, exc->zp2.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    /* Cramer's rule */
+
+    dbx = SUB_LONG( exc->zp0.cur[b1].x, exc->zp0.cur[b0].x );
+    dby = SUB_LONG( exc->zp0.cur[b1].y, exc->zp0.cur[b0].y );
+
+    dax = SUB_LONG( exc->zp1.cur[a1].x, exc->zp1.cur[a0].x );
+    day = SUB_LONG( exc->zp1.cur[a1].y, exc->zp1.cur[a0].y );
+
+    dx = SUB_LONG( exc->zp0.cur[b0].x, exc->zp1.cur[a0].x );
+    dy = SUB_LONG( exc->zp0.cur[b0].y, exc->zp1.cur[a0].y );
+
+    discriminant = ADD_LONG( FT_MulDiv( dax, NEG_LONG( dby ), 0x40 ),
+                             FT_MulDiv( day, dbx, 0x40 ) );
+    dotproduct   = ADD_LONG( FT_MulDiv( dax, dbx, 0x40 ),
+                             FT_MulDiv( day, dby, 0x40 ) );
+
+    /* The discriminant above is actually a cross product of vectors     */
+    /* da and db. Together with the dot product, they can be used as     */
+    /* surrogates for sine and cosine of the angle between the vectors.  */
+    /* Indeed,                                                           */
+    /*       dotproduct   = |da||db|cos(angle)                           */
+    /*       discriminant = |da||db|sin(angle)     .                     */
+    /* We use these equations to reject grazing intersections by         */
+    /* thresholding abs(tan(angle)) at 1/19, corresponding to 3 degrees. */
+    if ( MUL_LONG( 19, FT_ABS( discriminant ) ) > FT_ABS( dotproduct ) )
+    {
+      val = ADD_LONG( FT_MulDiv( dx, NEG_LONG( dby ), 0x40 ),
+                      FT_MulDiv( dy, dbx, 0x40 ) );
+
+      R.x = FT_MulDiv( val, dax, discriminant );
+      R.y = FT_MulDiv( val, day, discriminant );
+
+      /* XXX: Block in backward_compatibility and/or post-IUP? */
+      exc->zp2.cur[point].x = ADD_LONG( exc->zp1.cur[a0].x, R.x );
+      exc->zp2.cur[point].y = ADD_LONG( exc->zp1.cur[a0].y, R.y );
+    }
+    else
+    {
+      /* else, take the middle of the middles of A and B */
+
+      /* XXX: Block in backward_compatibility and/or post-IUP? */
+      exc->zp2.cur[point].x =
+        ADD_LONG( ADD_LONG( exc->zp1.cur[a0].x, exc->zp1.cur[a1].x ),
+                  ADD_LONG( exc->zp0.cur[b0].x, exc->zp0.cur[b1].x ) ) / 4;
+      exc->zp2.cur[point].y =
+        ADD_LONG( ADD_LONG( exc->zp1.cur[a0].y, exc->zp1.cur[a1].y ),
+                  ADD_LONG( exc->zp0.cur[b0].y, exc->zp0.cur[b1].y ) ) / 4;
+    }
+
+    exc->zp2.tags[point] |= FT_CURVE_TAG_TOUCH_BOTH;
+  }
+
+
+  /**************************************************************************
+   *
+   * ALIGNPTS[]:   ALIGN PoinTS
+   * Opcode range: 0x27
+   * Stack:        uint32 uint32 -->
+   */
+  static void
+  Ins_ALIGNPTS( TT_ExecContext  exc,
+                FT_Long*        args )
+  {
+    FT_UShort   p1, p2;
+    FT_F26Dot6  distance;
+
+
+    p1 = (FT_UShort)args[0];
+    p2 = (FT_UShort)args[1];
+
+    if ( BOUNDS( p1, exc->zp1.n_points ) ||
+         BOUNDS( p2, exc->zp0.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    distance = PROJECT( exc->zp0.cur + p2, exc->zp1.cur + p1 ) / 2;
+
+    exc->func_move( exc, &exc->zp1, p1, distance );
+    exc->func_move( exc, &exc->zp0, p2, NEG_LONG( distance ) );
+  }
+
+
+  /**************************************************************************
+   *
+   * IP[]:         Interpolate Point
+   * Opcode range: 0x39
+   * Stack:        uint32... -->
+   */
+
+  /* SOMETIMES, DUMBER CODE IS BETTER CODE */
+
+  static void
+  Ins_IP( TT_ExecContext  exc )
+  {
+    FT_F26Dot6  old_range, cur_range;
+    FT_Vector*  orus_base;
+    FT_Vector*  cur_base;
+    FT_Int      twilight;
+
+
+    if ( exc->top < exc->GS.loop )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      goto Fail;
+    }
+
+    /*
+     * We need to deal in a special way with the twilight zone.
+     * Otherwise, by definition, the value of exc->twilight.orus[n] is (0,0),
+     * for every n.
+     */
+    twilight = ( exc->GS.gep0 == 0 ||
+                 exc->GS.gep1 == 0 ||
+                 exc->GS.gep2 == 0 );
+
+    if ( BOUNDS( exc->GS.rp1, exc->zp0.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      goto Fail;
+    }
+
+    if ( twilight )
+      orus_base = &exc->zp0.org[exc->GS.rp1];
+    else
+      orus_base = &exc->zp0.orus[exc->GS.rp1];
+
+    cur_base = &exc->zp0.cur[exc->GS.rp1];
+
+    /* XXX: There are some glyphs in some braindead but popular */
+    /*      fonts out there (e.g. [aeu]grave in monotype.ttf)   */
+    /*      calling IP[] with bad values of rp[12].             */
+    /*      Do something sane when this odd thing happens.      */
+    if ( BOUNDS( exc->GS.rp1, exc->zp0.n_points ) ||
+         BOUNDS( exc->GS.rp2, exc->zp1.n_points ) )
+    {
+      old_range = 0;
+      cur_range = 0;
+    }
+    else
+    {
+      if ( twilight )
+        old_range = DUALPROJ( &exc->zp1.org[exc->GS.rp2], orus_base );
+      else if ( exc->metrics.x_scale == exc->metrics.y_scale )
+        old_range = DUALPROJ( &exc->zp1.orus[exc->GS.rp2], orus_base );
+      else
+      {
+        FT_Vector  vec;
+
+
+        vec.x = FT_MulFix( SUB_LONG( exc->zp1.orus[exc->GS.rp2].x,
+                                     orus_base->x ),
+                           exc->metrics.x_scale );
+        vec.y = FT_MulFix( SUB_LONG( exc->zp1.orus[exc->GS.rp2].y,
+                                     orus_base->y ),
+                           exc->metrics.y_scale );
+
+        old_range = FAST_DUALPROJ( &vec );
+      }
+
+      cur_range = PROJECT( &exc->zp1.cur[exc->GS.rp2], cur_base );
+    }
+
+    for ( ; exc->GS.loop > 0; exc->GS.loop-- )
+    {
+      FT_UInt     point = (FT_UInt)exc->stack[--exc->args];
+      FT_F26Dot6  org_dist, cur_dist, new_dist;
+
+
+      /* check point bounds */
+      if ( BOUNDS( point, exc->zp2.n_points ) )
+      {
+        if ( exc->pedantic_hinting )
+        {
+          exc->error = FT_THROW( Invalid_Reference );
+          return;
+        }
+        continue;
+      }
+
+      if ( twilight )
+        org_dist = DUALPROJ( &exc->zp2.org[point], orus_base );
+      else if ( exc->metrics.x_scale == exc->metrics.y_scale )
+        org_dist = DUALPROJ( &exc->zp2.orus[point], orus_base );
+      else
+      {
+        FT_Vector  vec;
+
+
+        vec.x = FT_MulFix( SUB_LONG( exc->zp2.orus[point].x,
+                                     orus_base->x ),
+                           exc->metrics.x_scale );
+        vec.y = FT_MulFix( SUB_LONG( exc->zp2.orus[point].y,
+                                     orus_base->y ),
+                           exc->metrics.y_scale );
+
+        org_dist = FAST_DUALPROJ( &vec );
+      }
+
+      cur_dist = PROJECT( &exc->zp2.cur[point], cur_base );
+
+      if ( org_dist )
+      {
+        if ( old_range )
+          new_dist = FT_MulDiv( org_dist, cur_range, old_range );
+        else
+        {
+          /* This is the same as what MS does for the invalid case:  */
+          /*                                                         */
+          /*   delta = (Original_Pt - Original_RP1) -                */
+          /*           (Current_Pt - Current_RP1)         ;          */
+          /*                                                         */
+          /* In FreeType speak:                                      */
+          /*                                                         */
+          /*   delta = org_dist - cur_dist          .                */
+          /*                                                         */
+          /* We move `point' by `new_dist - cur_dist' after leaving  */
+          /* this block, thus we have                                */
+          /*                                                         */
+          /*   new_dist - cur_dist = delta                   ,       */
+          /*   new_dist - cur_dist = org_dist - cur_dist     ,       */
+          /*              new_dist = org_dist                .       */
+
+          new_dist = org_dist;
+        }
+      }
+      else
+        new_dist = 0;
+
+      exc->func_move( exc,
+                      &exc->zp2,
+                      (FT_UShort)point,
+                      SUB_LONG( new_dist, cur_dist ) );
+    }
+
+  Fail:
+    exc->GS.loop = 1;
+    exc->new_top = exc->args;
+  }
+
+
+  /**************************************************************************
+   *
+   * UTP[a]:       UnTouch Point
+   * Opcode range: 0x29
+   * Stack:        uint32 -->
+   */
+  static void
+  Ins_UTP( TT_ExecContext  exc,
+           FT_Long*        args )
+  {
+    FT_UShort  point;
+    FT_Byte    mask;
+
+
+    point = (FT_UShort)args[0];
+
+    if ( BOUNDS( point, exc->zp0.n_points ) )
+    {
+      if ( exc->pedantic_hinting )
+        exc->error = FT_THROW( Invalid_Reference );
+      return;
+    }
+
+    mask = 0xFF;
+
+    if ( exc->GS.freeVector.x != 0 )
+      mask &= ~FT_CURVE_TAG_TOUCH_X;
+
+    if ( exc->GS.freeVector.y != 0 )
+      mask &= ~FT_CURVE_TAG_TOUCH_Y;
+
+    exc->zp0.tags[point] &= mask;
+  }
+
+
+  /* Local variables for Ins_IUP: */
+  typedef struct  IUP_WorkerRec_
+  {
+    FT_Vector*  orgs;   /* original and current coordinate */
+    FT_Vector*  curs;   /* arrays                          */
+    FT_Vector*  orus;
+    FT_UInt     max_points;
+
+  } IUP_WorkerRec, *IUP_Worker;
+
+
+  static void
+  iup_worker_shift_( IUP_Worker  worker,
+                     FT_UInt     p1,
+                     FT_UInt     p2,
+                     FT_UInt     p )
+  {
+    FT_UInt     i;
+    FT_F26Dot6  dx;
+
+
+    dx = SUB_LONG( worker->curs[p].x, worker->orgs[p].x );
+    if ( dx != 0 )
+    {
+      for ( i = p1; i < p; i++ )
+        worker->curs[i].x = ADD_LONG( worker->curs[i].x, dx );
+
+      for ( i = p + 1; i <= p2; i++ )
+        worker->curs[i].x = ADD_LONG( worker->curs[i].x, dx );
+    }
+  }
+
+
+  static void
+  iup_worker_interpolate_( IUP_Worker  worker,
+                           FT_UInt     p1,
+                           FT_UInt     p2,
+                           FT_UInt     ref1,
+                           FT_UInt     ref2 )
+  {
+    FT_UInt     i;
+    FT_F26Dot6  orus1, orus2, org1, org2, cur1, cur2, delta1, delta2;
+
+
+    if ( p1 > p2 )
+      return;
+
+    if ( BOUNDS( ref1, worker->max_points ) ||
+         BOUNDS( ref2, worker->max_points ) )
+      return;
+
+    orus1 = worker->orus[ref1].x;
+    orus2 = worker->orus[ref2].x;
+
+    if ( orus1 > orus2 )
+    {
+      FT_F26Dot6  tmp_o;
+      FT_UInt     tmp_r;
+
+
+      tmp_o = orus1;
+      orus1 = orus2;
+      orus2 = tmp_o;
+
+      tmp_r = ref1;
+      ref1  = ref2;
+      ref2  = tmp_r;
+    }
+
+    org1   = worker->orgs[ref1].x;
+    org2   = worker->orgs[ref2].x;
+    cur1   = worker->curs[ref1].x;
+    cur2   = worker->curs[ref2].x;
+    delta1 = SUB_LONG( cur1, org1 );
+    delta2 = SUB_LONG( cur2, org2 );
+
+    if ( cur1 == cur2 || orus1 == orus2 )
+    {
+
+      /* trivial snap or shift of untouched points */
+      for ( i = p1; i <= p2; i++ )
+      {
+        FT_F26Dot6  x = worker->orgs[i].x;
+
+
+        if ( x <= org1 )
+          x = ADD_LONG( x, delta1 );
+
+        else if ( x >= org2 )
+          x = ADD_LONG( x, delta2 );
+
+        else
+          x = cur1;
+
+        worker->curs[i].x = x;
+      }
+    }
+    else
+    {
+      FT_Fixed  scale       = 0;
+      FT_Bool   scale_valid = 0;
+
+
+      /* interpolation */
+      for ( i = p1; i <= p2; i++ )
+      {
+        FT_F26Dot6  x = worker->orgs[i].x;
+
+
+        if ( x <= org1 )
+          x = ADD_LONG( x, delta1 );
+
+        else if ( x >= org2 )
+          x = ADD_LONG( x, delta2 );
+
+        else
+        {
+          if ( !scale_valid )
+          {
+            scale_valid = 1;
+            scale       = FT_DivFix( SUB_LONG( cur2, cur1 ),
+                                     SUB_LONG( orus2, orus1 ) );
+          }
+
+          x = ADD_LONG( cur1,
+                        FT_MulFix( SUB_LONG( worker->orus[i].x, orus1 ),
+                                   scale ) );
+        }
+        worker->curs[i].x = x;
+      }
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * IUP[a]:       Interpolate Untouched Points
+   * Opcode range: 0x30-0x31
+   * Stack:        -->
+   */
+  static void
+  Ins_IUP( TT_ExecContext  exc )
+  {
+    IUP_WorkerRec  V;
+    FT_Byte        mask;
+
+    FT_UInt   first_point;   /* first point of contour        */
+    FT_UInt   end_point;     /* end point (last+1) of contour */
+
+    FT_UInt   first_touched; /* first touched point in contour   */
+    FT_UInt   cur_touched;   /* current touched point in contour */
+
+    FT_UInt   point;         /* current point   */
+    FT_Short  contour;       /* current contour */
+
+
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    /* See `ttinterp.h' for details on backward compatibility mode.  */
+    /* Allow IUP until it has been called on both axes.  Immediately */
+    /* return on subsequent ones.                                    */
+    if ( SUBPIXEL_HINTING_MINIMAL    &&
+         exc->backward_compatibility )
+    {
+      if ( exc->iupx_called && exc->iupy_called )
+        return;
+
+      if ( exc->opcode & 1 )
+        exc->iupx_called = TRUE;
+      else
+        exc->iupy_called = TRUE;
+    }
+#endif
+
+    /* ignore empty outlines */
+    if ( exc->pts.n_contours == 0 )
+      return;
+
+    if ( exc->opcode & 1 )
+    {
+      mask   = FT_CURVE_TAG_TOUCH_X;
+      V.orgs = exc->pts.org;
+      V.curs = exc->pts.cur;
+      V.orus = exc->pts.orus;
+    }
+    else
+    {
+      mask   = FT_CURVE_TAG_TOUCH_Y;
+      V.orgs = (FT_Vector*)( (FT_Pos*)exc->pts.org + 1 );
+      V.curs = (FT_Vector*)( (FT_Pos*)exc->pts.cur + 1 );
+      V.orus = (FT_Vector*)( (FT_Pos*)exc->pts.orus + 1 );
+    }
+    V.max_points = exc->pts.n_points;
+
+    contour = 0;
+    point   = 0;
+
+    do
+    {
+      end_point   = exc->pts.contours[contour] - exc->pts.first_point;
+      first_point = point;
+
+      if ( BOUNDS( end_point, exc->pts.n_points ) )
+        end_point = exc->pts.n_points - 1;
+
+      while ( point <= end_point && ( exc->pts.tags[point] & mask ) == 0 )
+        point++;
+
+      if ( point <= end_point )
+      {
+        first_touched = point;
+        cur_touched   = point;
+
+        point++;
+
+        while ( point <= end_point )
+        {
+          if ( ( exc->pts.tags[point] & mask ) != 0 )
+          {
+            iup_worker_interpolate_( &V,
+                                     cur_touched + 1,
+                                     point - 1,
+                                     cur_touched,
+                                     point );
+            cur_touched = point;
+          }
+
+          point++;
+        }
+
+        if ( cur_touched == first_touched )
+          iup_worker_shift_( &V, first_point, end_point, cur_touched );
+        else
+        {
+          iup_worker_interpolate_( &V,
+                                   (FT_UShort)( cur_touched + 1 ),
+                                   end_point,
+                                   cur_touched,
+                                   first_touched );
+
+          if ( first_touched > 0 )
+            iup_worker_interpolate_( &V,
+                                     first_point,
+                                     first_touched - 1,
+                                     cur_touched,
+                                     first_touched );
+        }
+      }
+      contour++;
+    } while ( contour < exc->pts.n_contours );
+  }
+
+
+  /**************************************************************************
+   *
+   * DELTAPn[]:    DELTA exceptions P1, P2, P3
+   * Opcode range: 0x5D,0x71,0x72
+   * Stack:        uint32 (2 * uint32)... -->
+   */
+  static void
+  Ins_DELTAP( TT_ExecContext  exc,
+              FT_Long*        args )
+  {
+    FT_ULong   nump, k;
+    FT_UShort  A;
+    FT_ULong   C, P;
+    FT_Long    B;
+
+
+    P    = (FT_ULong)exc->func_cur_ppem( exc );
+    nump = (FT_ULong)args[0];   /* some points theoretically may occur more
+                                   than once, thus UShort isn't enough */
+
+    for ( k = 1; k <= nump; k++ )
+    {
+      if ( exc->args < 2 )
+      {
+        if ( exc->pedantic_hinting )
+          exc->error = FT_THROW( Too_Few_Arguments );
+        exc->args = 0;
+        goto Fail;
+      }
+
+      exc->args -= 2;
+
+      A = (FT_UShort)exc->stack[exc->args + 1];
+      B = exc->stack[exc->args];
+
+      /* XXX: Because some popular fonts contain some invalid DeltaP */
+      /*      instructions, we simply ignore them when the stacked   */
+      /*      point reference is off limit, rather than returning an */
+      /*      error.  As a delta instruction doesn't change a glyph  */
+      /*      in great ways, this shouldn't be a problem.            */
+
+      if ( !BOUNDS( A, exc->zp0.n_points ) )
+      {
+        C = ( (FT_ULong)B & 0xF0 ) >> 4;
+
+        switch ( exc->opcode )
+        {
+        case 0x5D:
+          break;
+
+        case 0x71:
+          C += 16;
+          break;
+
+        case 0x72:
+          C += 32;
+          break;
+        }
+
+        C += exc->GS.delta_base;
+
+        if ( P == C )
+        {
+          B = ( (FT_ULong)B & 0xF ) - 8;
+          if ( B >= 0 )
+            B++;
+          B *= 1L << ( 6 - exc->GS.delta_shift );
+
+
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+          /* See `ttinterp.h' for details on backward compatibility */
+          /* mode.                                                  */
+          if ( SUBPIXEL_HINTING_MINIMAL    &&
+               exc->backward_compatibility )
+          {
+            if ( !( exc->iupx_called && exc->iupy_called )              &&
+                 ( ( exc->is_composite && exc->GS.freeVector.y != 0 ) ||
+                   ( exc->zp0.tags[A] & FT_CURVE_TAG_TOUCH_Y )        ) )
+              exc->func_move( exc, &exc->zp0, A, B );
+          }
+          else
+#endif
+            exc->func_move( exc, &exc->zp0, A, B );
+        }
+      }
+      else
+        if ( exc->pedantic_hinting )
+          exc->error = FT_THROW( Invalid_Reference );
+    }
+
+  Fail:
+    exc->new_top = exc->args;
+  }
+
+
+  /**************************************************************************
+   *
+   * DELTACn[]:    DELTA exceptions C1, C2, C3
+   * Opcode range: 0x73,0x74,0x75
+   * Stack:        uint32 (2 * uint32)... -->
+   */
+  static void
+  Ins_DELTAC( TT_ExecContext  exc,
+              FT_Long*        args )
+  {
+    FT_ULong  nump, k;
+    FT_ULong  A, C, P;
+    FT_Long   B;
+
+
+    P    = (FT_ULong)exc->func_cur_ppem( exc );
+    nump = (FT_ULong)args[0];
+
+    for ( k = 1; k <= nump; k++ )
+    {
+      if ( exc->args < 2 )
+      {
+        if ( exc->pedantic_hinting )
+          exc->error = FT_THROW( Too_Few_Arguments );
+        exc->args = 0;
+        goto Fail;
+      }
+
+      exc->args -= 2;
+
+      A = (FT_ULong)exc->stack[exc->args + 1];
+      B = exc->stack[exc->args];
+
+      if ( BOUNDSL( A, exc->cvtSize ) )
+      {
+        if ( exc->pedantic_hinting )
+        {
+          exc->error = FT_THROW( Invalid_Reference );
+          return;
+        }
+      }
+      else
+      {
+        C = ( (FT_ULong)B & 0xF0 ) >> 4;
+
+        switch ( exc->opcode )
+        {
+        case 0x73:
+          break;
+
+        case 0x74:
+          C += 16;
+          break;
+
+        case 0x75:
+          C += 32;
+          break;
+        }
+
+        C += exc->GS.delta_base;
+
+        if ( P == C )
+        {
+          B = ( (FT_ULong)B & 0xF ) - 8;
+          if ( B >= 0 )
+            B++;
+          B *= 1L << ( 6 - exc->GS.delta_shift );
+
+          exc->func_move_cvt( exc, A, B );
+        }
+      }
+    }
+
+  Fail:
+    exc->new_top = exc->args;
+  }
+
+
+  /**************************************************************************
+   *
+   * MISC. INSTRUCTIONS
+   *
+   */
+
+
+  /**************************************************************************
+   *
+   * GETINFO[]:    GET INFOrmation
+   * Opcode range: 0x88
+   * Stack:        uint32 --> uint32
+   */
+  static void
+  Ins_GETINFO( TT_ExecContext  exc,
+               FT_Long*        args )
+  {
+    FT_Long    K;
+    TT_Driver  driver = (TT_Driver)FT_FACE_DRIVER( exc->face );
+
+
+    K = 0;
+
+    if ( ( args[0] & 1 ) != 0 )
+      K = driver->interpreter_version;
+
+    /*********************************
+     * GLYPH ROTATED
+     * Selector Bit:  1
+     * Return Bit(s): 8
+     */
+    if ( ( args[0] & 2 ) != 0 && exc->tt_metrics.rotated )
+      K |= 1 << 8;
+
+    /*********************************
+     * GLYPH STRETCHED
+     * Selector Bit:  2
+     * Return Bit(s): 9
+     */
+    if ( ( args[0] & 4 ) != 0 && exc->tt_metrics.stretched )
+      K |= 1 << 9;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+    /*********************************
+     * VARIATION GLYPH
+     * Selector Bit:  3
+     * Return Bit(s): 10
+     */
+    if ( (args[0] & 8 ) != 0 && exc->face->blend )
+      K |= 1 << 10;
+#endif
+
+    /*********************************
+     * BI-LEVEL HINTING AND
+     * GRAYSCALE RENDERING
+     * Selector Bit:  5
+     * Return Bit(s): 12
+     */
+    if ( ( args[0] & 32 ) != 0 && exc->grayscale )
+      K |= 1 << 12;
+
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    /* Toggle the following flags only outside of monochrome mode.      */
+    /* Otherwise, instructions may behave weirdly and rendering results */
+    /* may differ between v35 and v40 mode, e.g., in `Times New Roman   */
+    /* Bold Italic'. */
+    if ( SUBPIXEL_HINTING_MINIMAL && exc->subpixel_hinting_lean )
+    {
+      /*********************************
+       * HINTING FOR SUBPIXEL
+       * Selector Bit:  6
+       * Return Bit(s): 13
+       *
+       * v40 does subpixel hinting by default.
+       */
+      if ( ( args[0] & 64 ) != 0 )
+        K |= 1 << 13;
+
+      /*********************************
+       * VERTICAL LCD SUBPIXELS?
+       * Selector Bit:  8
+       * Return Bit(s): 15
+       */
+      if ( ( args[0] & 256 ) != 0 && exc->vertical_lcd_lean )
+        K |= 1 << 15;
+
+      /*********************************
+       * SUBPIXEL POSITIONED?
+       * Selector Bit:  10
+       * Return Bit(s): 17
+       *
+       * XXX: FreeType supports it, dependent on what client does?
+       */
+      if ( ( args[0] & 1024 ) != 0 )
+        K |= 1 << 17;
+
+      /*********************************
+       * SYMMETRICAL SMOOTHING
+       * Selector Bit:  11
+       * Return Bit(s): 18
+       *
+       * The only smoothing method FreeType supports unless someone sets
+       * FT_LOAD_TARGET_MONO.
+       */
+      if ( ( args[0] & 2048 ) != 0 && exc->subpixel_hinting_lean )
+        K |= 1 << 18;
+
+      /*********************************
+       * CLEARTYPE HINTING AND
+       * GRAYSCALE RENDERING
+       * Selector Bit:  12
+       * Return Bit(s): 19
+       *
+       * Grayscale rendering is what FreeType does anyway unless someone
+       * sets FT_LOAD_TARGET_MONO or FT_LOAD_TARGET_LCD(_V)
+       */
+      if ( ( args[0] & 4096 ) != 0 && exc->grayscale_cleartype )
+        K |= 1 << 19;
+    }
+#endif
+
+    args[0] = K;
+  }
+
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+
+  /**************************************************************************
+   *
+   * GETVARIATION[]: get normalized variation (blend) coordinates
+   * Opcode range: 0x91
+   * Stack:        --> f2.14...
+   *
+   * XXX: UNDOCUMENTED!  There is no official documentation from Apple for
+   *      this bytecode instruction.  Active only if a font has GX
+   *      variation axes.
+   */
+  static void
+  Ins_GETVARIATION( TT_ExecContext  exc,
+                    FT_Long*        args )
+  {
+    FT_UInt    num_axes = exc->face->blend->num_axis;
+    FT_Fixed*  coords   = exc->face->blend->normalizedcoords;
+
+    FT_UInt  i;
+
+
+    if ( BOUNDS( num_axes, exc->stackSize + 1 - exc->top ) )
+    {
+      exc->error = FT_THROW( Stack_Overflow );
+      return;
+    }
+
+    if ( coords )
+    {
+      for ( i = 0; i < num_axes; i++ )
+        args[i] = coords[i] >> 2; /* convert 16.16 to 2.14 format */
+    }
+    else
+    {
+      for ( i = 0; i < num_axes; i++ )
+        args[i] = 0;
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * GETDATA[]:    no idea what this is good for
+   * Opcode range: 0x92
+   * Stack:        --> 17
+   *
+   * XXX: UNDOCUMENTED!  There is no documentation from Apple for this
+   *      very weird bytecode instruction.
+   */
+  static void
+  Ins_GETDATA( FT_Long*  args )
+  {
+    args[0] = 17;
+  }
+
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+
+  static void
+  Ins_UNKNOWN( TT_ExecContext  exc )
+  {
+    TT_DefRecord*  def   = exc->IDefs;
+    TT_DefRecord*  limit = FT_OFFSET( def, exc->numIDefs );
+
+
+    for ( ; def < limit; def++ )
+    {
+      if ( (FT_Byte)def->opc == exc->opcode && def->active )
+      {
+        TT_CallRec*  call;
+
+
+        if ( exc->callTop >= exc->callSize )
+        {
+          exc->error = FT_THROW( Stack_Overflow );
+          return;
+        }
+
+        call = exc->callStack + exc->callTop++;
+
+        call->Caller_Range = exc->curRange;
+        call->Caller_IP    = exc->IP + 1;
+        call->Cur_Count    = 1;
+        call->Def          = def;
+
+        Ins_Goto_CodeRange( exc, def->range, def->start );
+
+        exc->step_ins = FALSE;
+        return;
+      }
+    }
+
+    exc->error = FT_THROW( Invalid_Opcode );
+  }
+
+
+  /**************************************************************************
+   *
+   * RUN
+   *
+   * This function executes a run of opcodes.  It will exit in the
+   * following cases:
+   *
+   * - Errors (in which case it returns FALSE).
+   *
+   * - Reaching the end of the main code range (returns TRUE).
+   *   Reaching the end of a code range within a function call is an
+   *   error.
+   *
+   * - After executing one single opcode, if the flag `Instruction_Trap'
+   *   is set to TRUE (returns TRUE).
+   *
+   * On exit with TRUE, test IP < CodeSize to know whether it comes from
+   * an instruction trap or a normal termination.
+   *
+   *
+   * Note: The documented DEBUG opcode pops a value from the stack.  This
+   *       behaviour is unsupported; here a DEBUG opcode is always an
+   *       error.
+   *
+   *
+   * THIS IS THE INTERPRETER'S MAIN LOOP.
+   *
+   */
+
+
+  /* documentation is in ttinterp.h */
+
+  FT_EXPORT_DEF( FT_Error )
+  TT_RunIns( void*  exec )
+  {
+    TT_ExecContext  exc = (TT_ExecContext)exec;
+
+    FT_ULong   ins_counter = 0;  /* executed instructions counter */
+    FT_ULong   num_twilight_points;
+    FT_UShort  i;
+
+
+    /* We restrict the number of twilight points to a reasonable,     */
+    /* heuristic value to avoid slow execution of malformed bytecode. */
+    num_twilight_points = FT_MAX( 30,
+                                  2 * ( exc->pts.n_points + exc->cvtSize ) );
+    if ( exc->twilight.n_points > num_twilight_points )
+    {
+      if ( num_twilight_points > 0xFFFFU )
+        num_twilight_points = 0xFFFFU;
+
+      FT_TRACE5(( "TT_RunIns: Resetting number of twilight points\n" ));
+      FT_TRACE5(( "           from %d to the more reasonable value %ld\n",
+                  exc->twilight.n_points,
+                  num_twilight_points ));
+      exc->twilight.n_points = (FT_UShort)num_twilight_points;
+    }
+
+    /* Set up loop detectors.  We restrict the number of LOOPCALL loops */
+    /* and the number of JMPR, JROT, and JROF calls with a negative     */
+    /* argument to values that depend on various parameters like the    */
+    /* size of the CVT table or the number of points in the current     */
+    /* glyph (if applicable).                                           */
+    /*                                                                  */
+    /* The idea is that in real-world bytecode you either iterate over  */
+    /* all CVT entries (in the `prep' table), or over all points (or    */
+    /* contours, in the `glyf' table) of a glyph, and such iterations   */
+    /* don't happen very often.                                         */
+    exc->loopcall_counter = 0;
+    exc->neg_jump_counter = 0;
+
+    /* The maximum values are heuristic. */
+    if ( exc->pts.n_points )
+      exc->loopcall_counter_max = FT_MAX( 50,
+                                          10 * exc->pts.n_points ) +
+                                  FT_MAX( 50,
+                                          exc->cvtSize / 10 );
+    else
+      exc->loopcall_counter_max = 300 + 22 * exc->cvtSize;
+
+    /* as a protection against an unreasonable number of CVT entries  */
+    /* we assume at most 100 control values per glyph for the counter */
+    if ( exc->loopcall_counter_max >
+         100 * (FT_ULong)exc->face->root.num_glyphs )
+      exc->loopcall_counter_max = 100 * (FT_ULong)exc->face->root.num_glyphs;
+
+    FT_TRACE5(( "TT_RunIns: Limiting total number of loops in LOOPCALL"
+                " to %ld\n", exc->loopcall_counter_max ));
+
+    exc->neg_jump_counter_max = exc->loopcall_counter_max;
+    FT_TRACE5(( "TT_RunIns: Limiting total number of backward jumps"
+                " to %ld\n", exc->neg_jump_counter_max ));
+
+    /* set PPEM and CVT functions */
+    exc->tt_metrics.ratio = 0;
+    if ( exc->metrics.x_ppem != exc->metrics.y_ppem )
+    {
+      /* non-square pixels, use the stretched routines */
+      exc->func_cur_ppem  = Current_Ppem_Stretched;
+      exc->func_read_cvt  = Read_CVT_Stretched;
+      exc->func_write_cvt = Write_CVT_Stretched;
+      exc->func_move_cvt  = Move_CVT_Stretched;
+    }
+    else
+    {
+      /* square pixels, use normal routines */
+      exc->func_cur_ppem  = Current_Ppem;
+      exc->func_read_cvt  = Read_CVT;
+      exc->func_write_cvt = Write_CVT;
+      exc->func_move_cvt  = Move_CVT;
+    }
+
+    exc->iniRange    = exc->curRange;
+
+    Compute_Funcs( exc );
+    Compute_Round( exc, (FT_Byte)exc->GS.round_state );
+
+    /* These flags cancel execution of some opcodes after IUP is called */
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    exc->iupx_called = FALSE;
+    exc->iupy_called = FALSE;
+#endif
+
+    do
+    {
+      exc->opcode = exc->code[exc->IP];
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+      if ( ft_trace_levels[trace_ttinterp] >= 6 )
+      {
+        FT_Long  cnt = FT_MIN( 8, exc->top );
+        FT_Long  n;
+
+
+        /* if tracing level is 7, show current code position */
+        /* and the first few stack elements also             */
+        FT_TRACE6(( "  " ));
+        FT_TRACE7(( "%06ld ", exc->IP ));
+        FT_TRACE6(( "%s", opcode_name[exc->opcode] + 2 ));
+        FT_TRACE7(( "%*s", *opcode_name[exc->opcode] == 'A'
+                              ? 2
+                              : 12 - ( *opcode_name[exc->opcode] - '0' ),
+                              "#" ));
+        for ( n = 1; n <= cnt; n++ )
+          FT_TRACE7(( " %ld", exc->stack[exc->top - n] ));
+        FT_TRACE6(( "\n" ));
+      }
+#endif /* FT_DEBUG_LEVEL_TRACE */
+
+      if ( ( exc->length = opcode_length[exc->opcode] ) < 0 )
+      {
+        if ( exc->IP + 1 >= exc->codeSize )
+          goto LErrorCodeOverflow_;
+
+        exc->length = 2 - exc->length * exc->code[exc->IP + 1];
+      }
+
+      if ( exc->IP + exc->length > exc->codeSize )
+        goto LErrorCodeOverflow_;
+
+      /* First, let's check for empty stack and overflow */
+      exc->args = exc->top - ( Pop_Push_Count[exc->opcode] >> 4 );
+
+      /* `args' is the top of the stack once arguments have been popped. */
+      /* One can also interpret it as the index of the last argument.    */
+      if ( exc->args < 0 )
+      {
+        if ( exc->pedantic_hinting )
+        {
+          exc->error = FT_THROW( Too_Few_Arguments );
+          goto LErrorLabel_;
+        }
+
+        /* push zeroes onto the stack */
+        for ( i = 0; i < Pop_Push_Count[exc->opcode] >> 4; i++ )
+          exc->stack[i] = 0;
+        exc->args = 0;
+      }
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+      if ( exc->opcode == 0x91 )
+      {
+        /* this is very special: GETVARIATION returns */
+        /* a variable number of arguments             */
+
+        /* it is the job of the application to `activate' GX handling, */
+        /* that is, calling any of the GX API functions on the current */
+        /* font to select a variation instance                         */
+        if ( exc->face->blend )
+          exc->new_top = exc->args + exc->face->blend->num_axis;
+      }
+      else
+#endif
+        exc->new_top = exc->args + ( Pop_Push_Count[exc->opcode] & 15 );
+
+      /* `new_top' is the new top of the stack, after the instruction's */
+      /* execution.  `top' will be set to `new_top' after the `switch'  */
+      /* statement.                                                     */
+      if ( exc->new_top > exc->stackSize )
+      {
+        exc->error = FT_THROW( Stack_Overflow );
+        goto LErrorLabel_;
+      }
+
+      exc->step_ins = TRUE;
+      exc->error    = FT_Err_Ok;
+
+      {
+        FT_Long*  args   = exc->stack + exc->args;
+        FT_Byte   opcode = exc->opcode;
+
+
+        switch ( opcode )
+        {
+        case 0x00:  /* SVTCA y  */
+        case 0x01:  /* SVTCA x  */
+        case 0x02:  /* SPvTCA y */
+        case 0x03:  /* SPvTCA x */
+        case 0x04:  /* SFvTCA y */
+        case 0x05:  /* SFvTCA x */
+          Ins_SxyTCA( exc );
+          break;
+
+        case 0x06:  /* SPvTL // */
+        case 0x07:  /* SPvTL +  */
+          Ins_SPVTL( exc, args );
+          break;
+
+        case 0x08:  /* SFvTL // */
+        case 0x09:  /* SFvTL +  */
+          Ins_SFVTL( exc, args );
+          break;
+
+        case 0x0A:  /* SPvFS */
+          Ins_SPVFS( exc, args );
+          break;
+
+        case 0x0B:  /* SFvFS */
+          Ins_SFVFS( exc, args );
+          break;
+
+        case 0x0C:  /* GPv */
+          Ins_GPV( exc, args );
+          break;
+
+        case 0x0D:  /* GFv */
+          Ins_GFV( exc, args );
+          break;
+
+        case 0x0E:  /* SFvTPv */
+          Ins_SFVTPV( exc );
+          break;
+
+        case 0x0F:  /* ISECT  */
+          Ins_ISECT( exc, args );
+          break;
+
+        case 0x10:  /* SRP0 */
+          Ins_SRP0( exc, args );
+          break;
+
+        case 0x11:  /* SRP1 */
+          Ins_SRP1( exc, args );
+          break;
+
+        case 0x12:  /* SRP2 */
+          Ins_SRP2( exc, args );
+          break;
+
+        case 0x13:  /* SZP0 */
+          Ins_SZP0( exc, args );
+          break;
+
+        case 0x14:  /* SZP1 */
+          Ins_SZP1( exc, args );
+          break;
+
+        case 0x15:  /* SZP2 */
+          Ins_SZP2( exc, args );
+          break;
+
+        case 0x16:  /* SZPS */
+          Ins_SZPS( exc, args );
+          break;
+
+        case 0x17:  /* SLOOP */
+          Ins_SLOOP( exc, args );
+          break;
+
+        case 0x18:  /* RTG */
+          Ins_RTG( exc );
+          break;
+
+        case 0x19:  /* RTHG */
+          Ins_RTHG( exc );
+          break;
+
+        case 0x1A:  /* SMD */
+          Ins_SMD( exc, args );
+          break;
+
+        case 0x1B:  /* ELSE */
+          Ins_ELSE( exc );
+          break;
+
+        case 0x1C:  /* JMPR */
+          Ins_JMPR( exc, args );
+          break;
+
+        case 0x1D:  /* SCVTCI */
+          Ins_SCVTCI( exc, args );
+          break;
+
+        case 0x1E:  /* SSWCI */
+          Ins_SSWCI( exc, args );
+          break;
+
+        case 0x1F:  /* SSW */
+          Ins_SSW( exc, args );
+          break;
+
+        case 0x20:  /* DUP */
+          Ins_DUP( args );
+          break;
+
+        case 0x21:  /* POP */
+          Ins_POP();
+          break;
+
+        case 0x22:  /* CLEAR */
+          Ins_CLEAR( exc );
+          break;
+
+        case 0x23:  /* SWAP */
+          Ins_SWAP( args );
+          break;
+
+        case 0x24:  /* DEPTH */
+          Ins_DEPTH( exc, args );
+          break;
+
+        case 0x25:  /* CINDEX */
+          Ins_CINDEX( exc, args );
+          break;
+
+        case 0x26:  /* MINDEX */
+          Ins_MINDEX( exc, args );
+          break;
+
+        case 0x27:  /* ALIGNPTS */
+          Ins_ALIGNPTS( exc, args );
+          break;
+
+        case 0x28:  /* RAW */
+          Ins_UNKNOWN( exc );
+          break;
+
+        case 0x29:  /* UTP */
+          Ins_UTP( exc, args );
+          break;
+
+        case 0x2A:  /* LOOPCALL */
+          Ins_LOOPCALL( exc, args );
+          break;
+
+        case 0x2B:  /* CALL */
+          Ins_CALL( exc, args );
+          break;
+
+        case 0x2C:  /* FDEF */
+          Ins_FDEF( exc, args );
+          break;
+
+        case 0x2D:  /* ENDF */
+          Ins_ENDF( exc );
+          break;
+
+        case 0x2E:  /* MDAP */
+        case 0x2F:  /* MDAP */
+          Ins_MDAP( exc, args );
+          break;
+
+        case 0x30:  /* IUP */
+        case 0x31:  /* IUP */
+          Ins_IUP( exc );
+          break;
+
+        case 0x32:  /* SHP */
+        case 0x33:  /* SHP */
+          Ins_SHP( exc );
+          break;
+
+        case 0x34:  /* SHC */
+        case 0x35:  /* SHC */
+          Ins_SHC( exc, args );
+          break;
+
+        case 0x36:  /* SHZ */
+        case 0x37:  /* SHZ */
+          Ins_SHZ( exc, args );
+          break;
+
+        case 0x38:  /* SHPIX */
+          Ins_SHPIX( exc, args );
+          break;
+
+        case 0x39:  /* IP    */
+          Ins_IP( exc );
+          break;
+
+        case 0x3A:  /* MSIRP */
+        case 0x3B:  /* MSIRP */
+          Ins_MSIRP( exc, args );
+          break;
+
+        case 0x3C:  /* AlignRP */
+          Ins_ALIGNRP( exc );
+          break;
+
+        case 0x3D:  /* RTDG */
+          Ins_RTDG( exc );
+          break;
+
+        case 0x3E:  /* MIAP */
+        case 0x3F:  /* MIAP */
+          Ins_MIAP( exc, args );
+          break;
+
+        case 0x40:  /* NPUSHB */
+          Ins_NPUSHB( exc, args );
+          break;
+
+        case 0x41:  /* NPUSHW */
+          Ins_NPUSHW( exc, args );
+          break;
+
+        case 0x42:  /* WS */
+          Ins_WS( exc, args );
+          break;
+
+        case 0x43:  /* RS */
+          Ins_RS( exc, args );
+          break;
+
+        case 0x44:  /* WCVTP */
+          Ins_WCVTP( exc, args );
+          break;
+
+        case 0x45:  /* RCVT */
+          Ins_RCVT( exc, args );
+          break;
+
+        case 0x46:  /* GC */
+        case 0x47:  /* GC */
+          Ins_GC( exc, args );
+          break;
+
+        case 0x48:  /* SCFS */
+          Ins_SCFS( exc, args );
+          break;
+
+        case 0x49:  /* MD */
+        case 0x4A:  /* MD */
+          Ins_MD( exc, args );
+          break;
+
+        case 0x4B:  /* MPPEM */
+          Ins_MPPEM( exc, args );
+          break;
+
+        case 0x4C:  /* MPS */
+          Ins_MPS( exc, args );
+          break;
+
+        case 0x4D:  /* FLIPON */
+          Ins_FLIPON( exc );
+          break;
+
+        case 0x4E:  /* FLIPOFF */
+          Ins_FLIPOFF( exc );
+          break;
+
+        case 0x4F:  /* DEBUG */
+          Ins_DEBUG( exc );
+          break;
+
+        case 0x50:  /* LT */
+          Ins_LT( args );
+          break;
+
+        case 0x51:  /* LTEQ */
+          Ins_LTEQ( args );
+          break;
+
+        case 0x52:  /* GT */
+          Ins_GT( args );
+          break;
+
+        case 0x53:  /* GTEQ */
+          Ins_GTEQ( args );
+          break;
+
+        case 0x54:  /* EQ */
+          Ins_EQ( args );
+          break;
+
+        case 0x55:  /* NEQ */
+          Ins_NEQ( args );
+          break;
+
+        case 0x56:  /* ODD */
+          Ins_ODD( exc, args );
+          break;
+
+        case 0x57:  /* EVEN */
+          Ins_EVEN( exc, args );
+          break;
+
+        case 0x58:  /* IF */
+          Ins_IF( exc, args );
+          break;
+
+        case 0x59:  /* EIF */
+          Ins_EIF();
+          break;
+
+        case 0x5A:  /* AND */
+          Ins_AND( args );
+          break;
+
+        case 0x5B:  /* OR */
+          Ins_OR( args );
+          break;
+
+        case 0x5C:  /* NOT */
+          Ins_NOT( args );
+          break;
+
+        case 0x5D:  /* DELTAP1 */
+          Ins_DELTAP( exc, args );
+          break;
+
+        case 0x5E:  /* SDB */
+          Ins_SDB( exc, args );
+          break;
+
+        case 0x5F:  /* SDS */
+          Ins_SDS( exc, args );
+          break;
+
+        case 0x60:  /* ADD */
+          Ins_ADD( args );
+          break;
+
+        case 0x61:  /* SUB */
+          Ins_SUB( args );
+          break;
+
+        case 0x62:  /* DIV */
+          Ins_DIV( exc, args );
+          break;
+
+        case 0x63:  /* MUL */
+          Ins_MUL( args );
+          break;
+
+        case 0x64:  /* ABS */
+          Ins_ABS( args );
+          break;
+
+        case 0x65:  /* NEG */
+          Ins_NEG( args );
+          break;
+
+        case 0x66:  /* FLOOR */
+          Ins_FLOOR( args );
+          break;
+
+        case 0x67:  /* CEILING */
+          Ins_CEILING( args );
+          break;
+
+        case 0x68:  /* ROUND */
+        case 0x69:  /* ROUND */
+        case 0x6A:  /* ROUND */
+        case 0x6B:  /* ROUND */
+          Ins_ROUND( exc, args );
+          break;
+
+        case 0x6C:  /* NROUND */
+        case 0x6D:  /* NROUND */
+        case 0x6E:  /* NRRUND */
+        case 0x6F:  /* NROUND */
+          Ins_NROUND( exc, args );
+          break;
+
+        case 0x70:  /* WCVTF */
+          Ins_WCVTF( exc, args );
+          break;
+
+        case 0x71:  /* DELTAP2 */
+        case 0x72:  /* DELTAP3 */
+          Ins_DELTAP( exc, args );
+          break;
+
+        case 0x73:  /* DELTAC0 */
+        case 0x74:  /* DELTAC1 */
+        case 0x75:  /* DELTAC2 */
+          Ins_DELTAC( exc, args );
+          break;
+
+        case 0x76:  /* SROUND */
+          Ins_SROUND( exc, args );
+          break;
+
+        case 0x77:  /* S45Round */
+          Ins_S45ROUND( exc, args );
+          break;
+
+        case 0x78:  /* JROT */
+          Ins_JROT( exc, args );
+          break;
+
+        case 0x79:  /* JROF */
+          Ins_JROF( exc, args );
+          break;
+
+        case 0x7A:  /* ROFF */
+          Ins_ROFF( exc );
+          break;
+
+        case 0x7B:  /* ???? */
+          Ins_UNKNOWN( exc );
+          break;
+
+        case 0x7C:  /* RUTG */
+          Ins_RUTG( exc );
+          break;
+
+        case 0x7D:  /* RDTG */
+          Ins_RDTG( exc );
+          break;
+
+        case 0x7E:  /* SANGW */
+          Ins_SANGW();
+          break;
+
+        case 0x7F:  /* AA */
+          Ins_AA();
+          break;
+
+        case 0x80:  /* FLIPPT */
+          Ins_FLIPPT( exc );
+          break;
+
+        case 0x81:  /* FLIPRGON */
+          Ins_FLIPRGON( exc, args );
+          break;
+
+        case 0x82:  /* FLIPRGOFF */
+          Ins_FLIPRGOFF( exc, args );
+          break;
+
+        case 0x83:  /* UNKNOWN */
+        case 0x84:  /* UNKNOWN */
+          Ins_UNKNOWN( exc );
+          break;
+
+        case 0x85:  /* SCANCTRL */
+          Ins_SCANCTRL( exc, args );
+          break;
+
+        case 0x86:  /* SDPvTL */
+        case 0x87:  /* SDPvTL */
+          Ins_SDPVTL( exc, args );
+          break;
+
+        case 0x88:  /* GETINFO */
+          Ins_GETINFO( exc, args );
+          break;
+
+        case 0x89:  /* IDEF */
+          Ins_IDEF( exc, args );
+          break;
+
+        case 0x8A:  /* ROLL */
+          Ins_ROLL( args );
+          break;
+
+        case 0x8B:  /* MAX */
+          Ins_MAX( args );
+          break;
+
+        case 0x8C:  /* MIN */
+          Ins_MIN( args );
+          break;
+
+        case 0x8D:  /* SCANTYPE */
+          Ins_SCANTYPE( exc, args );
+          break;
+
+        case 0x8E:  /* INSTCTRL */
+          Ins_INSTCTRL( exc, args );
+          break;
+
+        case 0x8F:  /* ADJUST */
+        case 0x90:  /* ADJUST */
+          Ins_UNKNOWN( exc );
+          break;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+        case 0x91:
+          /* it is the job of the application to `activate' GX handling, */
+          /* that is, calling any of the GX API functions on the current */
+          /* font to select a variation instance                         */
+          if ( exc->face->blend )
+            Ins_GETVARIATION( exc, args );
+          else
+            Ins_UNKNOWN( exc );
+          break;
+
+        case 0x92:
+          /* there is at least one MS font (LaoUI.ttf version 5.01) that */
+          /* uses IDEFs for 0x91 and 0x92; for this reason we activate   */
+          /* GETDATA for GX fonts only, similar to GETVARIATION          */
+          if ( exc->face->blend )
+            Ins_GETDATA( args );
+          else
+            Ins_UNKNOWN( exc );
+          break;
+#endif
+
+        default:
+          if ( opcode >= 0xE0 )
+            Ins_MIRP( exc, args );
+          else if ( opcode >= 0xC0 )
+            Ins_MDRP( exc, args );
+          else if ( opcode >= 0xB8 )
+            Ins_PUSHW( exc, args );
+          else if ( opcode >= 0xB0 )
+            Ins_PUSHB( exc, args );
+          else
+            Ins_UNKNOWN( exc );
+        }
+      }
+
+      if ( exc->error )
+      {
+        switch ( exc->error )
+        {
+          /* looking for redefined instructions */
+        case FT_ERR( Invalid_Opcode ):
+          {
+            TT_DefRecord*  def   = exc->IDefs;
+            TT_DefRecord*  limit = FT_OFFSET( def, exc->numIDefs );
+
+
+            for ( ; def < limit; def++ )
+            {
+              if ( def->active && exc->opcode == (FT_Byte)def->opc )
+              {
+                TT_CallRec*  callrec;
+
+
+                if ( exc->callTop >= exc->callSize )
+                {
+                  exc->error = FT_THROW( Invalid_Reference );
+                  goto LErrorLabel_;
+                }
+
+                callrec = &exc->callStack[exc->callTop];
+
+                callrec->Caller_Range = exc->curRange;
+                callrec->Caller_IP    = exc->IP + 1;
+                callrec->Cur_Count    = 1;
+                callrec->Def          = def;
+
+                if ( Ins_Goto_CodeRange( exc,
+                                         def->range,
+                                         def->start ) == FAILURE )
+                  goto LErrorLabel_;
+
+                goto LSuiteLabel_;
+              }
+            }
+          }
+
+          exc->error = FT_THROW( Invalid_Opcode );
+          goto LErrorLabel_;
+
+#if 0
+          break;   /* Unreachable code warning suppression.             */
+                   /* Leave to remind in case a later change the editor */
+                   /* to consider break;                                */
+#endif
+
+        default:
+          goto LErrorLabel_;
+
+#if 0
+        break;
+#endif
+        }
+      }
+
+      exc->top = exc->new_top;
+
+      if ( exc->step_ins )
+        exc->IP += exc->length;
+
+      /* increment instruction counter and check if we didn't */
+      /* run this program for too long (e.g. infinite loops). */
+      if ( ++ins_counter > TT_CONFIG_OPTION_MAX_RUNNABLE_OPCODES )
+      {
+        exc->error = FT_THROW( Execution_Too_Long );
+        goto LErrorLabel_;
+      }
+
+    LSuiteLabel_:
+      if ( exc->IP >= exc->codeSize )
+      {
+        if ( exc->callTop > 0 )
+        {
+          exc->error = FT_THROW( Code_Overflow );
+          goto LErrorLabel_;
+        }
+        else
+          goto LNo_Error_;
+      }
+    } while ( !exc->instruction_trap );
+
+  LNo_Error_:
+    FT_TRACE4(( "  %ld instruction%s executed\n",
+                ins_counter,
+                ins_counter == 1 ? "" : "s" ));
+
+    return FT_Err_Ok;
+
+  LErrorCodeOverflow_:
+    exc->error = FT_THROW( Code_Overflow );
+
+  LErrorLabel_:
+    if ( exc->error && !exc->instruction_trap )
+      FT_TRACE1(( "  The interpreter returned error 0x%x\n", exc->error ));
+
+    return exc->error;
+  }
+
+#else /* !TT_USE_BYTECODE_INTERPRETER */
+
+  /* ANSI C doesn't like empty source files */
+  typedef int  tt_interp_dummy_;
+
+#endif /* !TT_USE_BYTECODE_INTERPRETER */
+
+
+/* END */
diff --git a/src/preload/ttinterp.h b/src/preload/ttinterp.h
new file mode 100644
index 000000000..e98e258fe
--- /dev/null
+++ b/src/preload/ttinterp.h
@@ -0,0 +1,465 @@
+/****************************************************************************
+ *
+ * ttinterp.h
+ *
+ *   TrueType bytecode interpreter (specification).
+ *
+ * Copyright (C) 1996-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+#ifndef TTINTERP_H_
+#define TTINTERP_H_
+
+#include "ttobjs.h"
+
+
+FT_BEGIN_HEADER
+
+
+  /**************************************************************************
+   *
+   * Rounding mode constants.
+   */
+#define TT_Round_Off             5
+#define TT_Round_To_Half_Grid    0
+#define TT_Round_To_Grid         1
+#define TT_Round_To_Double_Grid  2
+#define TT_Round_Up_To_Grid      4
+#define TT_Round_Down_To_Grid    3
+#define TT_Round_Super           6
+#define TT_Round_Super_45        7
+
+
+  /**************************************************************************
+   *
+   * Function types used by the interpreter, depending on various modes
+   * (e.g. the rounding mode, whether to render a vertical or horizontal
+   * line etc).
+   *
+   */
+
+  /* Rounding function */
+  typedef FT_F26Dot6
+  (*TT_Round_Func)( TT_ExecContext  exc,
+                    FT_F26Dot6      distance,
+                    FT_Int          color );
+
+  /* Point displacement along the freedom vector routine */
+  typedef void
+  (*TT_Move_Func)( TT_ExecContext  exc,
+                   TT_GlyphZone    zone,
+                   FT_UShort       point,
+                   FT_F26Dot6      distance );
+
+  /* Distance projection along one of the projection vectors */
+  typedef FT_F26Dot6
+  (*TT_Project_Func)( TT_ExecContext  exc,
+                      FT_Pos          dx,
+                      FT_Pos          dy );
+
+  /* getting current ppem.  Take care of non-square pixels if necessary */
+  typedef FT_Long
+  (*TT_Cur_Ppem_Func)( TT_ExecContext  exc );
+
+  /* reading a cvt value.  Take care of non-square pixels if necessary */
+  typedef FT_F26Dot6
+  (*TT_Get_CVT_Func)( TT_ExecContext  exc,
+                      FT_ULong        idx );
+
+  /* setting or moving a cvt value.  Take care of non-square pixels  */
+  /* if necessary                                                    */
+  typedef void
+  (*TT_Set_CVT_Func)( TT_ExecContext  exc,
+                      FT_ULong        idx,
+                      FT_F26Dot6      value );
+
+
+  /**************************************************************************
+   *
+   * This structure defines a call record, used to manage function calls.
+   */
+  typedef struct  TT_CallRec_
+  {
+    FT_Int   Caller_Range;
+    FT_Long  Caller_IP;
+    FT_Long  Cur_Count;
+
+    TT_DefRecord  *Def; /* either FDEF or IDEF */
+
+  } TT_CallRec, *TT_CallStack;
+
+
+  /**************************************************************************
+   *
+   * The main structure for the interpreter which collects all necessary
+   * variables and states.
+   *
+   * Members that are initialized by `TT_Load_Context` are marked with '!'.
+   * Members that are initialized by `TT_Run_Context` are marked with '@'.
+   */
+  typedef struct  TT_ExecContextRec_
+  {
+    TT_Face            face;       /* ! */
+    TT_Size            size;       /* ! */
+    FT_Memory          memory;
+
+    /* instructions state */
+
+    FT_Error           error;      /* last execution error */
+
+    FT_Long            top;        /* @ top of exec. stack */
+
+    FT_Long            stackSize;  /* ! size of exec. stack */
+    FT_Long*           stack;      /* ! current exec. stack */
+
+    FT_Long            args;
+    FT_Long            new_top;    /* new top after exec. */
+
+    TT_GlyphZoneRec    zp0,        /* @! zone records */
+                       zp1,        /* @!              */
+                       zp2,        /* @!              */
+                       pts,        /*  !              */
+                       twilight;   /*  !              */
+
+    FT_Long            pointSize;  /* ! in 26.6 format */
+    FT_Size_Metrics    metrics;    /* !                */
+    TT_Size_Metrics    tt_metrics; /* ! size metrics   */
+
+    TT_GraphicsState   GS;         /* !@ current graphics state */
+
+    FT_Int             iniRange;  /* initial code range number   */
+    FT_Int             curRange;  /* current code range number   */
+    FT_Byte*           code;      /* current code range          */
+    FT_Long            IP;        /* current instruction pointer */
+    FT_Long            codeSize;  /* size of current range       */
+
+    FT_Byte            opcode;    /* current opcode              */
+    FT_Int             length;    /* length of current opcode    */
+
+    FT_Bool            step_ins;  /* true if the interpreter must */
+                                  /* increment IP after ins. exec */
+    FT_ULong           cvtSize;   /* ! */
+    FT_Long*           cvt;       /* ! */
+    FT_ULong           glyfCvtSize;
+    FT_Long*           glyfCvt;   /* cvt working copy for glyph */
+
+    FT_UInt            glyphSize; /* ! glyph instructions buffer size */
+    FT_Byte*           glyphIns;  /* ! glyph instructions buffer      */
+
+    FT_UInt            numFDefs;  /* ! number of function defs         */
+    FT_UInt            maxFDefs;  /* ! maximum number of function defs */
+    TT_DefArray        FDefs;     /*   table of FDefs entries          */
+
+    FT_UInt            numIDefs;  /* ! number of instruction defs */
+    FT_UInt            maxIDefs;  /* ! maximum number of ins defs */
+    TT_DefArray        IDefs;     /*   table of IDefs entries     */
+
+    FT_UInt            maxFunc;   /* ! maximum function index    */
+    FT_UInt            maxIns;    /* ! maximum instruction index */
+
+    FT_Int             callTop,    /* @ top of call stack during execution */
+                       callSize;   /*   size of call stack                 */
+    TT_CallStack       callStack;  /*   call stack                         */
+
+    FT_UShort          maxPoints;    /* capacity of this context's `pts' */
+    FT_Short           maxContours;  /* record, expressed in points and  */
+                                     /* contours.                        */
+
+    TT_CodeRangeTable  codeRangeTable;  /* ! table of valid code ranges */
+                                        /*   useful for the debugger    */
+
+    FT_UShort          storeSize;    /* ! size of current storage */
+    FT_Long*           storage;      /* ! storage area            */
+    FT_UShort          glyfStoreSize;
+    FT_Long*           glyfStorage;  /* storage working copy for glyph */
+
+    FT_F26Dot6         period;     /* values used for the */
+    FT_F26Dot6         phase;      /* `SuperRounding'     */
+    FT_F26Dot6         threshold;
+
+    FT_Bool            instruction_trap; /* ! If `True', the interpreter   */
+                                         /*   exits after each instruction */
+
+    TT_GraphicsState   default_GS;       /* graphics state resulting from   */
+                                         /* the prep program                */
+    FT_Bool            is_composite;     /* true if the glyph is composite  */
+    FT_Bool            pedantic_hinting; /* true if pedantic interpretation */
+
+    /* latest interpreter additions */
+
+    FT_Long            F_dot_P;    /* dot product of freedom and projection */
+                                   /* vectors                               */
+    TT_Round_Func      func_round; /* current rounding function             */
+
+    TT_Project_Func    func_project,   /* current projection function */
+                       func_dualproj,  /* current dual proj. function */
+                       func_freeProj;  /* current freedom proj. func  */
+
+    TT_Move_Func       func_move;      /* current point move function     */
+    TT_Move_Func       func_move_orig; /* move original position function */
+
+    TT_Cur_Ppem_Func   func_cur_ppem;  /* get current proj. ppem value  */
+
+    TT_Get_CVT_Func    func_read_cvt;  /* read a cvt entry              */
+    TT_Set_CVT_Func    func_write_cvt; /* write a cvt entry (in pixels) */
+    TT_Set_CVT_Func    func_move_cvt;  /* incr a cvt entry (in pixels)  */
+
+    FT_Bool            grayscale;      /* bi-level hinting and */
+                                       /* grayscale rendering  */
+
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    /*
+     * FreeType supports ClearType-like hinting of TrueType fonts through
+     * the version 40 interpreter.  This is achieved through several hacks
+     * in the base (v35) interpreter, as detailed below.
+     *
+     * ClearType is an umbrella term for several rendering techniques
+     * employed by Microsoft's various GUI and rendering toolkit
+     * implementations, most importantly: subpixel rendering for using the
+     * RGB subpixels of LCDs to approximately triple the perceived
+     * resolution on the x-axis and subpixel hinting for positioning stems
+     * on subpixel borders.  TrueType programming is explicit, i.e., fonts
+     * must be programmed to take advantage of ClearType's possibilities.
+     *
+     * When ClearType was introduced, it seemed unlikely that all fonts
+     * would be reprogrammed, so Microsoft decided to implement a backward
+     * compatibility mode.  It employs several simple to complicated
+     * assumptions and tricks, many of them font-dependent, that modify the
+     * interpretation of the bytecode contained in these fonts to retrofit
+     * them into a ClearType-y look.  The quality of the results varies.
+     * Most (web)fonts that were released since then have come to rely on
+     * these hacks to render correctly, even some of Microsoft's flagship
+     * fonts (e.g., Calibri, Cambria, Segoe UI).
+     *
+     * FreeType's minimal subpixel hinting code (interpreter version 40)
+     * employs a small list of font-agnostic hacks loosely based on the
+     * public information available on Microsoft's compatibility mode[2].
+     * The focus is on modern (web)fonts rather than legacy fonts that were
+     * made for monochrome rendering.  It will not match ClearType rendering
+     * exactly.  Unlike the `Infinality' code (interpreter version 38) that
+     * came before, it will not try to toggle hacks for specific fonts for
+     * performance and complexity reasons.  It will fall back to version 35
+     * behavior for tricky fonts[1] or when monochrome rendering is
+     * requested.
+     *
+     * Major hacks
+     *
+     * - Any point movement on the x axis is ignored (cf. `Direct_Move' and
+     *   `Direct_Move_X').  This has the smallest code footprint and single
+     *   biggest effect.  The ClearType way to increase resolution is
+     *   supersampling the x axis, the FreeType way is ignoring instructions
+     *   on the x axis, which gives the same result in the majority of
+     *   cases.
+     *
+     * - Points are not moved post-IUP (neither on the x nor on the y axis),
+     *   except the x component of diagonal moves post-IUP (cf.
+     *   `Direct_Move', `Direct_Move_Y', `Move_Zp2_Point').  Post-IUP
+     *   changes are commonly used to `fix' pixel patterns which has little
+     *   use outside monochrome rendering.
+     *
+     * - SHPIX and DELTAP don't execute unless moving a composite on the
+     *   y axis or moving a previously y touched point.  SHPIX additionally
+     *   denies movement on the x axis (cf. `Ins_SHPIX' and `Ins_DELTAP').
+     *   Both instructions are commonly used to `fix' pixel patterns for
+     *   monochrome or Windows's GDI rendering but make little sense for
+     *   FreeType rendering.  Both can distort the outline.  See [2] for
+     *   details.
+     *
+     * - The hdmx table and modifications to phantom points are ignored.
+     *   Bearings and advance widths remain unchanged (except rounding them
+     *   outside the interpreter!), cf. `compute_glyph_metrics' and
+     *   `TT_Hint_Glyph'.  Letting non-native-ClearType fonts modify spacing
+     *   might mess up spacing.
+     *
+     * Minor hacks
+     *
+     * - FLIPRGON, FLIPRGOFF, and FLIPPT don't execute post-IUP.  This
+     *   prevents dents in e.g. Arial-Regular's `D' and `G' glyphs at
+     *   various sizes.
+     *
+     * (Post-IUP is the state after both IUP[x] and IUP[y] have been
+     * executed.)
+     *
+     * The best results are achieved for fonts that were from the outset
+     * designed with ClearType in mind, meaning they leave the x axis mostly
+     * alone and don't mess with the `final' outline to produce more
+     * pleasing pixel patterns.  The harder the designer tried to produce
+     * very specific patterns (`superhinting') for pre-ClearType-displays,
+     * the worse the results.
+     *
+     * Microsoft defines a way to turn off backward compatibility and
+     * interpret instructions as before (called `native ClearType')[2][3].
+     * The font designer then regains full control and is responsible for
+     * making the font work correctly with ClearType without any
+     * hand-holding by the interpreter or rasterizer[4].  The v40
+     * interpreter assumes backward compatibility by default, which can be
+     * turned off the same way by executing the following in the control
+     * program (cf. `Ins_INSTCTRL').
+     *
+     *   #PUSH 4,3
+     *   INSTCTRL[]
+     *
+     * [1] Tricky fonts as FreeType defines them rely on the bytecode
+     *     interpreter to display correctly.  Hacks can interfere with them,
+     *     so they get treated like native ClearType fonts (v40 with
+     *     backward compatibility turned off).  Cf. `TT_RunIns'.
+     *
+     * [2] Proposed by Microsoft's Greg Hitchcock in
+     *     
https://www.microsoft.com/typography/cleartype/truetypecleartype.aspx
+     *
+     * [3] Beat Stamm describes it in more detail:
+     *     http://rastertragedy.com/RTRCh4.htm#Sec12.
+     *
+     * [4] The list of `native ClearType' fonts is small at the time of this
+     *     writing; I found the following on a Windows 10 Update 1511
+     *     installation: Constantia, Corbel, Sitka, Malgun Gothic, Microsoft
+     *     JhengHei (Bold and UI Bold), Microsoft YaHei (Bold and UI Bold),
+     *     SimSun, NSimSun, and Yu Gothic.
+     *
+     */
+
+    /* Using v40 implies subpixel hinting, unless FT_RENDER_MODE_MONO has been
+     * requested.  Used to detect interpreter */
+    /* version switches.  `_lean' to differentiate from the Infinality */
+    /* `subpixel_hinting', which is managed differently.               */
+    FT_Bool            subpixel_hinting_lean;
+
+    /* Long side of a LCD subpixel is vertical (e.g., screen is rotated). */
+    /* `_lean' to differentiate from the Infinality `vertical_lcd', which */
+    /* is managed differently.                                            */
+    FT_Bool            vertical_lcd_lean;
+
+    /* Default to backward compatibility mode in v40 interpreter.  If   */
+    /* this is false, it implies the interpreter is in v35 or in native */
+    /* ClearType mode.                                                  */
+    FT_Bool            backward_compatibility;
+
+    /* Useful for detecting and denying post-IUP trickery that is usually */
+    /* used to fix pixel patterns (`superhinting').                       */
+    FT_Bool            iupx_called;
+    FT_Bool            iupy_called;
+
+    /* ClearType hinting and grayscale rendering, as used by Universal */
+    /* Windows Platform apps (Windows 8 and above).  Like the standard */
+    /* colorful ClearType mode, it utilizes a vastly increased virtual */
+    /* resolution on the x axis.  Different from bi-level hinting and  */
+    /* grayscale rendering, the old mode from Win9x days that roughly  */
+    /* adheres to the physical pixel grid on both axes.                */
+    FT_Bool            grayscale_cleartype;
+#endif /* TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL */
+
+    /* We maintain two counters (in addition to the instruction counter) */
+    /* that act as loop detectors for LOOPCALL and jump opcodes with     */
+    /* negative arguments.                                               */
+    FT_ULong           loopcall_counter;
+    FT_ULong           loopcall_counter_max;
+    FT_ULong           neg_jump_counter;
+    FT_ULong           neg_jump_counter_max;
+
+  } TT_ExecContextRec;
+
+
+  extern const TT_GraphicsState  tt_default_graphics_state;
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+  FT_LOCAL( void )
+  TT_Goto_CodeRange( TT_ExecContext  exec,
+                     FT_Int          range,
+                     FT_Long         IP );
+
+  FT_LOCAL( void )
+  TT_Set_CodeRange( TT_ExecContext  exec,
+                    FT_Int          range,
+                    void*           base,
+                    FT_Long         length );
+
+  FT_LOCAL( void )
+  TT_Clear_CodeRange( TT_ExecContext  exec,
+                      FT_Int          range );
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_New_Context
+   *
+   * @Description:
+   *   Create a `TT_ExecContext`.  Note that there is now an execution
+   *   context per `TT_Size` that is not shared among faces.
+   *
+   * @Input:
+   *   driver ::
+   *     A handle to the driver, used for memory allocation.
+   *
+   * @Return:
+   *   A handle to a new empty execution context.
+   *
+   * @Note:
+   *   Only the glyph loader and debugger should call this function.
+   *   (And right now only the glyph loader uses it.)
+   */
+  FT_EXPORT( TT_ExecContext )
+  TT_New_Context( TT_Driver  driver );
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+  FT_LOCAL( void )
+  TT_Done_Context( TT_ExecContext  exec );
+
+  FT_LOCAL( FT_Error )
+  TT_Load_Context( TT_ExecContext  exec,
+                   TT_Face         face,
+                   TT_Size         size );
+
+  FT_LOCAL( void )
+  TT_Save_Context( TT_ExecContext  exec,
+                   TT_Size         ins );
+
+  FT_LOCAL( FT_Error )
+  TT_Run_Context( TT_ExecContext  exec );
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   TT_RunIns
+   *
+   * @Description:
+   *   Executes one or more instruction in the execution context.  This
+   *   is the main function of the TrueType opcode interpreter.
+   *
+   * @Input:
+   *   exec ::
+   *     A handle to the target execution context.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   *
+   * @Note:
+   *   Only the object manager and debugger should call this function.
+   *
+   *   This function is publicly exported because it is directly
+   *   invoked by the TrueType debugger.
+   */
+  FT_EXPORT( FT_Error )
+  TT_RunIns( void*  exec );
+
+
+FT_END_HEADER
+
+#endif /* TTINTERP_H_ */
+
+
+/* END */
diff --git a/src/preload/ttobjs.c b/src/preload/ttobjs.c
new file mode 100644
index 000000000..5b56af711
--- /dev/null
+++ b/src/preload/ttobjs.c
@@ -0,0 +1,1539 @@
+/****************************************************************************
+ *
+ * ttobjs.c
+ *
+ *   Objects manager (body).
+ *
+ * Copyright (C) 1996-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/tttags.h>
+#include <freetype/internal/sfnt.h>
+#include <freetype/ftdriver.h>
+
+#include "ttgload.h"
+#include "ttpload.h"
+
+#include "tterrors.h"
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+#include "ttinterp.h"
+#endif
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include "ttgxvar.h"
+#endif
+
+  /**************************************************************************
+   *
+   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
+   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
+   * messages during execution.
+   */
+#undef  FT_COMPONENT
+#define FT_COMPONENT  ttobjs
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+  /**************************************************************************
+   *
+   *                      GLYPH ZONE FUNCTIONS
+   *
+   */
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_glyphzone_done
+   *
+   * @Description:
+   *   Deallocate a glyph zone.
+   *
+   * @Input:
+   *   zone ::
+   *     A pointer to the target glyph zone.
+   */
+  FT_LOCAL_DEF( void )
+  tt_glyphzone_done( TT_GlyphZone  zone )
+  {
+    FT_Memory  memory = zone->memory;
+
+
+    if ( memory )
+    {
+      FT_FREE( zone->contours );
+      FT_FREE( zone->tags );
+      FT_FREE( zone->cur );
+      FT_FREE( zone->org );
+      FT_FREE( zone->orus );
+
+      zone->max_points   = zone->n_points   = 0;
+      zone->max_contours = zone->n_contours = 0;
+      zone->memory       = NULL;
+    }
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_glyphzone_new
+   *
+   * @Description:
+   *   Allocate a new glyph zone.
+   *
+   * @Input:
+   *   memory ::
+   *     A handle to the current memory object.
+   *
+   *   maxPoints ::
+   *     The capacity of glyph zone in points.
+   *
+   *   maxContours ::
+   *     The capacity of glyph zone in contours.
+   *
+   * @Output:
+   *   zone ::
+   *     A pointer to the target glyph zone record.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_glyphzone_new( FT_Memory     memory,
+                    FT_UShort     maxPoints,
+                    FT_Short      maxContours,
+                    TT_GlyphZone  zone )
+  {
+    FT_Error  error;
+
+
+    FT_ZERO( zone );
+    zone->memory = memory;
+
+    if ( FT_NEW_ARRAY( zone->org,      maxPoints   ) ||
+         FT_NEW_ARRAY( zone->cur,      maxPoints   ) ||
+         FT_NEW_ARRAY( zone->orus,     maxPoints   ) ||
+         FT_NEW_ARRAY( zone->tags,     maxPoints   ) ||
+         FT_NEW_ARRAY( zone->contours, maxContours ) )
+    {
+      tt_glyphzone_done( zone );
+    }
+    else
+    {
+      zone->max_points   = maxPoints;
+      zone->max_contours = maxContours;
+    }
+
+    return error;
+  }
+
+
+  /*
+   * Fonts embedded in PDFs are made unique by prepending randomization
+   * prefixes to their names: as defined in Section 5.5.3, 'Font Subsets',
+   * of the PDF Reference, they consist of 6 uppercase letters followed by
+   * the `+` sign.  For safety, we do not skip prefixes violating this rule.
+   */
+
+  static const FT_String*
+  tt_skip_pdffont_random_tag( const FT_String*  name )
+  {
+    unsigned int  i;
+
+
+    if ( ft_strlen( name ) < 8 || name[6] != '+' )
+      return name;
+
+    for ( i = 0; i < 6; i++ )
+      if ( !ft_isupper( name[i] ) )
+        return name;
+
+    FT_TRACE7(( "name without randomization tag: %s\n", name + 7 ));
+    return name + 7;
+  }
+
+
+  /* Compare the face with a list of well-known `tricky' fonts. */
+  /* This list shall be expanded as we find more of them.       */
+
+  static FT_Bool
+  tt_check_trickyness_family( const FT_String*  name )
+  {
+
+#define TRICK_NAMES_MAX_CHARACTERS  19
+#define TRICK_NAMES_COUNT           20
+
+    static const char trick_names[TRICK_NAMES_COUNT]
+                                 [TRICK_NAMES_MAX_CHARACTERS + 1] =
+    {
+      /*
+         PostScript names are given in brackets if they differ from the
+         family name.  The version numbers, together with the copyright or
+         release year data, are taken from fonts available to the
+         developers.
+
+         Note that later versions of the fonts might be no longer tricky;
+         for example, `MingLiU' version 7.00 (file `mingliu.ttc' from
+         Windows 7) is an ordinary TTC with non-tricky subfonts.
+       */
+
+      "cpop",               /* dftt-p7.ttf; version 1.00, 1992 
[DLJGyShoMedium] */
+      "DFGirl-W6-WIN-BF",   /* dftt-h6.ttf; version 1.00, 1993 */
+      "DFGothic-EB",        /* DynaLab Inc. 1992-1995 */
+      "DFGyoSho-Lt",        /* DynaLab Inc. 1992-1995 */
+      "DFHei",              /* DynaLab Inc. 1992-1995 [DFHei-Bd-WIN-HK-BF] */
+                            /* covers "DFHei-Md-HK-BF", maybe DynaLab Inc. */
+
+      "DFHSGothic-W5",      /* DynaLab Inc. 1992-1995 */
+      "DFHSMincho-W3",      /* DynaLab Inc. 1992-1995 */
+      "DFHSMincho-W7",      /* DynaLab Inc. 1992-1995 */
+      "DFKaiSho-SB",        /* dfkaisb.ttf */
+      "DFKaiShu",           /* covers "DFKaiShu-Md-HK-BF", maybe DynaLab Inc. 
*/
+      "DFKai-SB",           /* kaiu.ttf; version 3.00, 1998 
[DFKaiShu-SB-Estd-BF] */
+
+      "DFMing",             /* DynaLab Inc. 1992-1995 [DFMing-Md-WIN-HK-BF] */
+                            /* covers "DFMing-Bd-HK-BF", maybe DynaLab Inc. */
+
+      "DLC",                /* dftt-m7.ttf; version 1.00, 1993 [DLCMingBold] */
+                            /* dftt-f5.ttf; version 1.00, 1993 [DLCFongSung] */
+                            /* covers following */
+                            /* "DLCHayMedium", dftt-b5.ttf; version 1.00, 1993 
*/
+                            /* "DLCHayBold",   dftt-b7.ttf; version 1.00, 1993 
*/
+                            /* "DLCKaiMedium", dftt-k5.ttf; version 1.00, 1992 
*/
+                            /* "DLCLiShu",     dftt-l5.ttf; version 1.00, 1992 
*/
+                            /* "DLCRoundBold", dftt-r7.ttf; version 1.00, 1993 
*/
+
+      "HuaTianKaiTi?",      /* htkt2.ttf */
+      "HuaTianSongTi?",     /* htst3.ttf */
+      "Ming(for ISO10646)", /* hkscsiic.ttf; version 0.12, 2007 [Ming] */
+                            /* iicore.ttf; version 0.07, 2007 [Ming] */
+      "MingLiU",            /* mingliu.ttf */
+                            /* mingliu.ttc; version 3.21, 2001 */
+      "MingMedium",         /* dftt-m5.ttf; version 1.00, 1993 [DLCMingMedium] 
*/
+      "PMingLiU",           /* mingliu.ttc; version 3.21, 2001 */
+      "MingLi43",           /* mingli.ttf; version 1.00, 1992 */
+    };
+
+    int  nn;
+    const FT_String*  name_without_tag;
+
+
+    name_without_tag = tt_skip_pdffont_random_tag( name );
+    for ( nn = 0; nn < TRICK_NAMES_COUNT; nn++ )
+      if ( ft_strstr( name_without_tag, trick_names[nn] ) )
+        return TRUE;
+
+    return FALSE;
+  }
+
+
+  /* XXX: This function should be in the `sfnt' module. */
+
+  /* Some PDF generators clear the checksums in the TrueType header table. */
+  /* For example, Quartz ContextPDF clears all entries, or Bullzip PDF     */
+  /* Printer clears the entries for subsetted subtables.  We thus have to  */
+  /* recalculate the checksums  where necessary.                           */
+
+  static FT_UInt32
+  tt_synth_sfnt_checksum( FT_Stream  stream,
+                          FT_ULong   length )
+  {
+    FT_Error   error;
+    FT_UInt32  checksum = 0;
+    FT_UInt    i;
+
+
+    if ( FT_FRAME_ENTER( length ) )
+      return 0;
+
+    for ( ; length > 3; length -= 4 )
+      checksum += (FT_UInt32)FT_GET_ULONG();
+
+    for ( i = 3; length > 0; length--, i-- )
+      checksum += (FT_UInt32)FT_GET_BYTE() << ( i * 8 );
+
+    FT_FRAME_EXIT();
+
+    return checksum;
+  }
+
+
+  /* XXX: This function should be in the `sfnt' module. */
+
+  static FT_ULong
+  tt_get_sfnt_checksum( TT_Face    face,
+                        FT_UShort  i )
+  {
+#if 0 /* if we believe the written value, use following part. */
+    if ( face->dir_tables[i].CheckSum )
+      return face->dir_tables[i].CheckSum;
+#endif
+
+    if ( !face->goto_table )
+      return 0;
+
+    if ( face->goto_table( face,
+                           face->dir_tables[i].Tag,
+                           face->root.stream,
+                           NULL ) )
+      return 0;
+
+    return (FT_ULong)tt_synth_sfnt_checksum( face->root.stream,
+                                             face->dir_tables[i].Length );
+  }
+
+
+  typedef struct tt_sfnt_id_rec_
+  {
+    FT_ULong  CheckSum;
+    FT_ULong  Length;
+
+  } tt_sfnt_id_rec;
+
+
+  static FT_Bool
+  tt_check_trickyness_sfnt_ids( TT_Face  face )
+  {
+#define TRICK_SFNT_IDS_PER_FACE   3
+#define TRICK_SFNT_IDS_NUM_FACES  31
+
+    static const tt_sfnt_id_rec sfnt_id[TRICK_SFNT_IDS_NUM_FACES]
+                                       [TRICK_SFNT_IDS_PER_FACE] =
+    {
+
+#define TRICK_SFNT_ID_cvt   0
+#define TRICK_SFNT_ID_fpgm  1
+#define TRICK_SFNT_ID_prep  2
+
+      { /* MingLiU 1995 */
+        { 0x05BCF058UL, 0x000002E4UL }, /* cvt  */
+        { 0x28233BF1UL, 0x000087C4UL }, /* fpgm */
+        { 0xA344A1EAUL, 0x000001E1UL }  /* prep */
+      },
+      { /* MingLiU 1996- */
+        { 0x05BCF058UL, 0x000002E4UL }, /* cvt  */
+        { 0x28233BF1UL, 0x000087C4UL }, /* fpgm */
+        { 0xA344A1EBUL, 0x000001E1UL }  /* prep */
+      },
+      { /* DFGothic-EB */
+        { 0x12C3EBB2UL, 0x00000350UL }, /* cvt  */
+        { 0xB680EE64UL, 0x000087A7UL }, /* fpgm */
+        { 0xCE939563UL, 0x00000758UL }  /* prep */
+      },
+      { /* DFGyoSho-Lt */
+        { 0x11E5EAD4UL, 0x00000350UL }, /* cvt  */
+        { 0xCE5956E9UL, 0x0000BC85UL }, /* fpgm */
+        { 0x8272F416UL, 0x00000045UL }  /* prep */
+      },
+      { /* DFHei-Md-HK-BF */
+        { 0x1257EB46UL, 0x00000350UL }, /* cvt  */
+        { 0xF699D160UL, 0x0000715FUL }, /* fpgm */
+        { 0xD222F568UL, 0x000003BCUL }  /* prep */
+      },
+      { /* DFHSGothic-W5 */
+        { 0x1262EB4EUL, 0x00000350UL }, /* cvt  */
+        { 0xE86A5D64UL, 0x00007940UL }, /* fpgm */
+        { 0x7850F729UL, 0x000005FFUL }  /* prep */
+      },
+      { /* DFHSMincho-W3 */
+        { 0x122DEB0AUL, 0x00000350UL }, /* cvt  */
+        { 0x3D16328AUL, 0x0000859BUL }, /* fpgm */
+        { 0xA93FC33BUL, 0x000002CBUL }  /* prep */
+      },
+      { /* DFHSMincho-W7 */
+        { 0x125FEB26UL, 0x00000350UL }, /* cvt  */
+        { 0xA5ACC982UL, 0x00007EE1UL }, /* fpgm */
+        { 0x90999196UL, 0x0000041FUL }  /* prep */
+      },
+      { /* DFKaiShu */
+        { 0x11E5EAD4UL, 0x00000350UL }, /* cvt  */
+        { 0x5A30CA3BUL, 0x00009063UL }, /* fpgm */
+        { 0x13A42602UL, 0x0000007EUL }  /* prep */
+      },
+      { /* DFKaiShu, variant */
+        { 0x11E5EAD4UL, 0x00000350UL }, /* cvt  */
+        { 0xA6E78C01UL, 0x00008998UL }, /* fpgm */
+        { 0x13A42602UL, 0x0000007EUL }  /* prep */
+      },
+      { /* DFKaiShu-Md-HK-BF */
+        { 0x11E5EAD4UL, 0x00000360UL }, /* cvt  */
+        { 0x9DB282B2UL, 0x0000C06EUL }, /* fpgm */
+        { 0x53E6D7CAUL, 0x00000082UL }  /* prep */
+      },
+      { /* DFMing-Bd-HK-BF */
+        { 0x1243EB18UL, 0x00000350UL }, /* cvt  */
+        { 0xBA0A8C30UL, 0x000074ADUL }, /* fpgm */
+        { 0xF3D83409UL, 0x0000037BUL }  /* prep */
+      },
+      { /* DLCLiShu */
+        { 0x07DCF546UL, 0x00000308UL }, /* cvt  */
+        { 0x40FE7C90UL, 0x00008E2AUL }, /* fpgm */
+        { 0x608174B5UL, 0x0000007AUL }  /* prep */
+      },
+      { /* DLCHayBold */
+        { 0xEB891238UL, 0x00000308UL }, /* cvt  */
+        { 0xD2E4DCD4UL, 0x0000676FUL }, /* fpgm */
+        { 0x8EA5F293UL, 0x000003B8UL }  /* prep */
+      },
+      { /* HuaTianKaiTi */
+        { 0xFFFBFFFCUL, 0x00000008UL }, /* cvt  */
+        { 0x9C9E48B8UL, 0x0000BEA2UL }, /* fpgm */
+        { 0x70020112UL, 0x00000008UL }  /* prep */
+      },
+      { /* HuaTianSongTi */
+        { 0xFFFBFFFCUL, 0x00000008UL }, /* cvt  */
+        { 0x0A5A0483UL, 0x00017C39UL }, /* fpgm */
+        { 0x70020112UL, 0x00000008UL }  /* prep */
+      },
+      { /* NEC fadpop7.ttf */
+        { 0x00000000UL, 0x00000000UL }, /* cvt  */
+        { 0x40C92555UL, 0x000000E5UL }, /* fpgm */
+        { 0xA39B58E3UL, 0x0000117CUL }  /* prep */
+      },
+      { /* NEC fadrei5.ttf */
+        { 0x00000000UL, 0x00000000UL }, /* cvt  */
+        { 0x33C41652UL, 0x000000E5UL }, /* fpgm */
+        { 0x26D6C52AUL, 0x00000F6AUL }  /* prep */
+      },
+      { /* NEC fangot7.ttf */
+        { 0x00000000UL, 0x00000000UL }, /* cvt  */
+        { 0x6DB1651DUL, 0x0000019DUL }, /* fpgm */
+        { 0x6C6E4B03UL, 0x00002492UL }  /* prep */
+      },
+      { /* NEC fangyo5.ttf */
+        { 0x00000000UL, 0x00000000UL }, /* cvt  */
+        { 0x40C92555UL, 0x000000E5UL }, /* fpgm */
+        { 0xDE51FAD0UL, 0x0000117CUL }  /* prep */
+      },
+      { /* NEC fankyo5.ttf */
+        { 0x00000000UL, 0x00000000UL }, /* cvt  */
+        { 0x85E47664UL, 0x000000E5UL }, /* fpgm */
+        { 0xA6C62831UL, 0x00001CAAUL }  /* prep */
+      },
+      { /* NEC fanrgo5.ttf */
+        { 0x00000000UL, 0x00000000UL }, /* cvt  */
+        { 0x2D891CFDUL, 0x0000019DUL }, /* fpgm */
+        { 0xA0604633UL, 0x00001DE8UL }  /* prep */
+      },
+      { /* NEC fangot5.ttc */
+        { 0x00000000UL, 0x00000000UL }, /* cvt  */
+        { 0x40AA774CUL, 0x000001CBUL }, /* fpgm */
+        { 0x9B5CAA96UL, 0x00001F9AUL }  /* prep */
+      },
+      { /* NEC fanmin3.ttc */
+        { 0x00000000UL, 0x00000000UL }, /* cvt  */
+        { 0x0D3DE9CBUL, 0x00000141UL }, /* fpgm */
+        { 0xD4127766UL, 0x00002280UL }  /* prep */
+      },
+      { /* NEC FA-Gothic, 1996 */
+        { 0x00000000UL, 0x00000000UL }, /* cvt  */
+        { 0x4A692698UL, 0x000001F0UL }, /* fpgm */
+        { 0x340D4346UL, 0x00001FCAUL }  /* prep */
+      },
+      { /* NEC FA-Minchou, 1996 */
+        { 0x00000000UL, 0x00000000UL }, /* cvt  */
+        { 0xCD34C604UL, 0x00000166UL }, /* fpgm */
+        { 0x6CF31046UL, 0x000022B0UL }  /* prep */
+      },
+      { /* NEC FA-RoundGothicB, 1996 */
+        { 0x00000000UL, 0x00000000UL }, /* cvt  */
+        { 0x5DA75315UL, 0x0000019DUL }, /* fpgm */
+        { 0x40745A5FUL, 0x000022E0UL }  /* prep */
+      },
+      { /* NEC FA-RoundGothicM, 1996 */
+        { 0x00000000UL, 0x00000000UL }, /* cvt  */
+        { 0xF055FC48UL, 0x000001C2UL }, /* fpgm */
+        { 0x3900DED3UL, 0x00001E18UL }  /* prep */
+      },
+        { /* MINGLI.TTF, 1992 */
+        { 0x00170003UL, 0x00000060UL }, /* cvt  */
+        { 0xDBB4306EUL, 0x000058AAUL }, /* fpgm */
+        { 0xD643482AUL, 0x00000035UL }  /* prep */
+      },
+        { /* DFHei-Bd-WIN-HK-BF, issue #1087 */
+        { 0x1269EB58UL, 0x00000350UL }, /* cvt  */
+        { 0x5CD5957AUL, 0x00006A4EUL }, /* fpgm */
+        { 0xF758323AUL, 0x00000380UL }  /* prep */
+      },
+        { /* DFMing-Md-WIN-HK-BF, issue #1087 */
+        { 0x122FEB0BUL, 0x00000350UL }, /* cvt  */
+        { 0x7F10919AUL, 0x000070A9UL }, /* fpgm */
+        { 0x7CD7E7B7UL, 0x0000025CUL }  /* prep */
+      }
+    };
+
+    FT_ULong   checksum;
+    int        num_matched_ids[TRICK_SFNT_IDS_NUM_FACES];
+    FT_Bool    has_cvt, has_fpgm, has_prep;
+    FT_UShort  i;
+    int        j, k;
+
+
+    FT_MEM_SET( num_matched_ids, 0,
+                sizeof ( int ) * TRICK_SFNT_IDS_NUM_FACES );
+    has_cvt  = FALSE;
+    has_fpgm = FALSE;
+    has_prep = FALSE;
+
+    for ( i = 0; i < face->num_tables; i++ )
+    {
+      checksum = 0;
+
+      switch( face->dir_tables[i].Tag )
+      {
+      case TTAG_cvt:
+        k = TRICK_SFNT_ID_cvt;
+        has_cvt  = TRUE;
+        break;
+
+      case TTAG_fpgm:
+        k = TRICK_SFNT_ID_fpgm;
+        has_fpgm = TRUE;
+        break;
+
+      case TTAG_prep:
+        k = TRICK_SFNT_ID_prep;
+        has_prep = TRUE;
+        break;
+
+      default:
+        continue;
+      }
+
+      for ( j = 0; j < TRICK_SFNT_IDS_NUM_FACES; j++ )
+        if ( face->dir_tables[i].Length == sfnt_id[j][k].Length )
+        {
+          if ( !checksum )
+            checksum = tt_get_sfnt_checksum( face, i );
+
+          if ( sfnt_id[j][k].CheckSum == checksum )
+            num_matched_ids[j]++;
+
+          if ( num_matched_ids[j] == TRICK_SFNT_IDS_PER_FACE )
+            return TRUE;
+        }
+    }
+
+    for ( j = 0; j < TRICK_SFNT_IDS_NUM_FACES; j++ )
+    {
+      if ( !has_cvt  && !sfnt_id[j][TRICK_SFNT_ID_cvt].Length )
+        num_matched_ids[j]++;
+      if ( !has_fpgm && !sfnt_id[j][TRICK_SFNT_ID_fpgm].Length )
+        num_matched_ids[j]++;
+      if ( !has_prep && !sfnt_id[j][TRICK_SFNT_ID_prep].Length )
+        num_matched_ids[j]++;
+      if ( num_matched_ids[j] == TRICK_SFNT_IDS_PER_FACE )
+        return TRUE;
+    }
+
+    return FALSE;
+  }
+
+
+  static FT_Bool
+  tt_check_trickyness( FT_Face  face )
+  {
+    if ( !face )
+      return FALSE;
+
+    /* For first, check the face name for quick check. */
+    if ( face->family_name                               &&
+         tt_check_trickyness_family( face->family_name ) )
+    {
+      FT_TRACE3(( "found as a tricky font"
+                  " by its family name: %s\n", face->family_name ));
+      return TRUE;
+    }
+
+    /* Type42 fonts may lack `name' tables, we thus try to identify */
+    /* tricky fonts by checking the checksums of Type42-persistent  */
+    /* sfnt tables (`cvt', `fpgm', and `prep').                     */
+    if ( tt_check_trickyness_sfnt_ids( (TT_Face)face ) )
+    {
+      FT_TRACE3(( "found as a tricky font"
+                  " by its cvt/fpgm/prep table checksum\n" ));
+      return TRUE;
+    }
+
+    return FALSE;
+  }
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+
+  /* Check whether `.notdef' is the only glyph in the `loca' table. */
+  static FT_Bool
+  tt_check_single_notdef( FT_Face  ttface )
+  {
+    FT_Bool   result = FALSE;
+
+    TT_Face   face = (TT_Face)ttface;
+    FT_ULong  asize;
+    FT_ULong  i;
+    FT_ULong  glyph_index = 0;
+    FT_UInt   count       = 0;
+
+
+    for( i = 0; i < face->num_locations; i++ )
+    {
+      tt_face_get_location( ttface, i, &asize );
+      if ( asize > 0 )
+      {
+        count += 1;
+        if ( count > 1 )
+          break;
+        glyph_index = i;
+      }
+    }
+
+    /* Only have a single outline. */
+    if ( count == 1 )
+    {
+      if ( glyph_index == 0 )
+        result = TRUE;
+      else
+      {
+        /* FIXME: Need to test glyphname == .notdef ? */
+        FT_Error error;
+        char buf[8];
+
+
+        error = FT_Get_Glyph_Name( ttface, glyph_index, buf, 8 );
+        if ( !error                                            &&
+             buf[0] == '.' && !ft_strncmp( buf, ".notdef", 8 ) )
+          result = TRUE;
+      }
+    }
+
+    return result;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_face_init
+   *
+   * @Description:
+   *   Initialize a given TrueType face object.
+   *
+   * @Input:
+   *   stream ::
+   *     The source font stream.
+   *
+   *   face_index ::
+   *     The index of the TrueType font, if we are opening a
+   *     collection, in bits 0-15.  The numbered instance
+   *     index~+~1 of a GX (sub)font, if applicable, in bits
+   *     16-30.
+   *
+   *   num_params ::
+   *     Number of additional generic parameters.  Ignored.
+   *
+   *   params ::
+   *     Additional generic parameters.  Ignored.
+   *
+   * @InOut:
+   *   face ::
+   *     The newly built face object.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_face_init( FT_Stream      stream,
+                FT_Face        ttface,      /* TT_Face */
+                FT_Int         face_index,
+                FT_Int         num_params,
+                FT_Parameter*  params )
+  {
+    FT_Error      error;
+    FT_Library    library;
+    SFNT_Service  sfnt;
+    TT_Face       face = (TT_Face)ttface;
+
+
+    FT_TRACE2(( "TTF driver\n" ));
+
+    library = ttface->driver->root.library;
+
+    sfnt = (SFNT_Service)FT_Get_Module_Interface( library, "sfnt" );
+    if ( !sfnt )
+    {
+      FT_ERROR(( "tt_face_init: cannot access `sfnt' module\n" ));
+      error = FT_THROW( Missing_Module );
+      goto Exit;
+    }
+
+    /* create input stream from resource */
+    if ( FT_STREAM_SEEK( 0 ) )
+      goto Exit;
+
+    /* check that we have a valid TrueType file */
+    FT_TRACE2(( "  " ));
+    error = sfnt->init_face( stream, face, face_index, num_params, params );
+
+    /* Stream may have changed. */
+    stream = face->root.stream;
+
+    if ( error )
+      goto Exit;
+
+    /* We must also be able to accept Mac/GX fonts, as well as OT ones. */
+    /* The 0x00020000 tag is completely undocumented; some fonts from   */
+    /* Arphic made for Chinese Windows 3.1 have this.                   */
+    if ( face->format_tag != 0x00010000L  && /* MS fonts                       
      */
+         face->format_tag != 0x00020000L  && /* CJK fonts for Win 3.1          
      */
+         face->format_tag != TTAG_true    && /* Mac fonts                      
      */
+         face->format_tag != TTAG_0xA5kbd && /* `Keyboard.dfont' (legacy Mac 
OS X)   */
+         face->format_tag != TTAG_0xA5lst )  /* `LastResort.dfont' (legacy Mac 
OS X) */
+    {
+      FT_TRACE2(( "  not a TTF font\n" ));
+      goto Bad_Format;
+    }
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+    ttface->face_flags |= FT_FACE_FLAG_HINTER;
+#endif
+
+    /* If we are performing a simple font format check, exit immediately. */
+    if ( face_index < 0 )
+      return FT_Err_Ok;
+
+    /* Load font directory */
+    error = sfnt->load_face( stream, face, face_index, num_params, params );
+    if ( error )
+      goto Exit;
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+    if ( tt_check_trickyness( ttface ) )
+      ttface->face_flags |= FT_FACE_FLAG_TRICKY;
+#endif
+
+    error = tt_face_load_hdmx( face, stream );
+    if ( error )
+      goto Exit;
+
+    if ( FT_IS_SCALABLE( ttface ) ||
+         FT_HAS_SBIX( ttface )    )
+    {
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+      if ( !ttface->internal->incremental_interface )
+#endif
+      {
+        error = tt_face_load_loca( face, stream );
+
+        /* having a (non-zero) `glyf' table without */
+        /* a `loca' table is not valid              */
+        if ( face->glyf_len && FT_ERR_EQ( error, Table_Missing ) )
+          goto Exit;
+        if ( error )
+          goto Exit;
+      }
+
+      /* `fpgm', `cvt', and `prep' are optional */
+      error = tt_face_load_cvt( face, stream );
+      if ( error && FT_ERR_NEQ( error, Table_Missing ) )
+        goto Exit;
+
+      error = tt_face_load_fpgm( face, stream );
+      if ( error && FT_ERR_NEQ( error, Table_Missing ) )
+        goto Exit;
+
+      error = tt_face_load_prep( face, stream );
+      if ( error && FT_ERR_NEQ( error, Table_Missing ) )
+        goto Exit;
+
+      /* Check the scalable flag based on `loca'. */
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+      if ( !ttface->internal->incremental_interface )
+#endif
+      {
+        if ( ttface->num_fixed_sizes          &&
+             face->glyph_locations            &&
+             tt_check_single_notdef( ttface ) )
+        {
+          FT_TRACE5(( "tt_face_init:"
+                      " Only the `.notdef' glyph has an outline.\n" ));
+          FT_TRACE5(( "             "
+                      " Resetting scalable flag to FALSE.\n" ));
+
+          ttface->face_flags &= ~FT_FACE_FLAG_SCALABLE;
+        }
+      }
+    }
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+    {
+      FT_UInt  instance_index = (FT_UInt)face_index >> 16;
+
+
+      if ( FT_HAS_MULTIPLE_MASTERS( ttface ) &&
+           instance_index > 0                )
+      {
+        error = FT_Set_Named_Instance( ttface, instance_index );
+        if ( error )
+          goto Exit;
+      }
+    }
+#endif /* TT_CONFIG_OPTION_GX_VAR_SUPPORT */
+
+    /* initialize standard glyph loading routines */
+    TT_Init_Glyph_Loading( face );
+
+  Exit:
+    return error;
+
+  Bad_Format:
+    error = FT_THROW( Unknown_File_Format );
+    goto Exit;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_face_done
+   *
+   * @Description:
+   *   Finalize a given face object.
+   *
+   * @Input:
+   *   face ::
+   *     A pointer to the face object to destroy.
+   */
+  FT_LOCAL_DEF( void )
+  tt_face_done( FT_Face  ttface )           /* TT_Face */
+  {
+    TT_Face       face = (TT_Face)ttface;
+    FT_Memory     memory;
+    FT_Stream     stream;
+    SFNT_Service  sfnt;
+
+
+    if ( !face )
+      return;
+
+    memory = ttface->memory;
+    stream = ttface->stream;
+    sfnt   = (SFNT_Service)face->sfnt;
+
+    /* for `extended TrueType formats' (i.e. compressed versions) */
+    if ( face->extra.finalizer )
+      face->extra.finalizer( face->extra.data );
+
+    if ( sfnt )
+      sfnt->done_face( face );
+
+    /* freeing the locations table */
+    tt_face_done_loca( face );
+
+    tt_face_free_hdmx( face );
+
+    /* freeing the CVT */
+    FT_FREE( face->cvt );
+    face->cvt_size = 0;
+
+    /* freeing the programs */
+    FT_FRAME_RELEASE( face->font_program );
+    FT_FRAME_RELEASE( face->cvt_program );
+    face->font_program_size = 0;
+    face->cvt_program_size  = 0;
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+    tt_done_blend( ttface );
+    face->blend = NULL;
+#endif
+  }
+
+
+  /**************************************************************************
+   *
+   *                          SIZE  FUNCTIONS
+   *
+   */
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_size_run_fpgm
+   *
+   * @Description:
+   *   Run the font program.
+   *
+   * @Input:
+   *   size ::
+   *     A handle to the size object.
+   *
+   *   pedantic ::
+   *     Set if bytecode execution should be pedantic.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_size_run_fpgm( TT_Size  size,
+                    FT_Bool  pedantic )
+  {
+    TT_Face         face = (TT_Face)size->root.face;
+    TT_ExecContext  exec;
+    FT_Error        error;
+
+
+    exec = size->context;
+
+    error = TT_Load_Context( exec, face, size );
+    if ( error )
+      return error;
+
+    exec->callTop = 0;
+    exec->top     = 0;
+
+    exec->period    = 64;
+    exec->phase     = 0;
+    exec->threshold = 0;
+
+    exec->instruction_trap = FALSE;
+    exec->F_dot_P          = 0x4000L;
+
+    exec->pedantic_hinting = pedantic;
+
+    {
+      FT_Size_Metrics*  size_metrics = &exec->metrics;
+      TT_Size_Metrics*  tt_metrics   = &exec->tt_metrics;
+
+
+      size_metrics->x_ppem   = 0;
+      size_metrics->y_ppem   = 0;
+      size_metrics->x_scale  = 0;
+      size_metrics->y_scale  = 0;
+
+      tt_metrics->ppem  = 0;
+      tt_metrics->scale = 0;
+      tt_metrics->ratio = 0x10000L;
+    }
+
+    /* allow font program execution */
+    TT_Set_CodeRange( exec,
+                      tt_coderange_font,
+                      face->font_program,
+                      (FT_Long)face->font_program_size );
+
+    /* disable CVT and glyph programs coderange */
+    TT_Clear_CodeRange( exec, tt_coderange_cvt );
+    TT_Clear_CodeRange( exec, tt_coderange_glyph );
+
+    if ( face->font_program_size > 0 )
+    {
+      TT_Goto_CodeRange( exec, tt_coderange_font, 0 );
+
+      FT_TRACE4(( "Executing `fpgm' table.\n" ));
+      error = face->interpreter( exec );
+#ifdef FT_DEBUG_LEVEL_TRACE
+      if ( error )
+        FT_TRACE4(( "  interpretation failed with error code 0x%x\n",
+                    error ));
+#endif
+    }
+    else
+      error = FT_Err_Ok;
+
+    size->bytecode_ready = error;
+
+    if ( !error )
+      TT_Save_Context( exec, size );
+
+    return error;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_size_run_prep
+   *
+   * @Description:
+   *   Run the control value program.
+   *
+   * @Input:
+   *   size ::
+   *     A handle to the size object.
+   *
+   *   pedantic ::
+   *     Set if bytecode execution should be pedantic.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_size_run_prep( TT_Size  size,
+                    FT_Bool  pedantic )
+  {
+    TT_Face         face = (TT_Face)size->root.face;
+    TT_ExecContext  exec;
+    FT_Error        error;
+    FT_UInt         i;
+
+    /* unscaled CVT values are already stored in 26.6 format */
+    FT_Fixed  scale = size->ttmetrics.scale >> 6;
+
+
+    /* Scale the cvt values to the new ppem.            */
+    /* By default, we use the y ppem value for scaling. */
+    FT_TRACE6(( "CVT values:\n" ));
+    for ( i = 0; i < size->cvt_size; i++ )
+    {
+      size->cvt[i] = FT_MulFix( face->cvt[i], scale );
+      FT_TRACE6(( "  %3d: %f (%f)\n",
+                  i, (double)face->cvt[i] / 64, (double)size->cvt[i] / 64 ));
+    }
+    FT_TRACE6(( "\n" ));
+
+    exec = size->context;
+
+    error = TT_Load_Context( exec, face, size );
+    if ( error )
+      return error;
+
+    exec->callTop = 0;
+    exec->top     = 0;
+
+    exec->instruction_trap = FALSE;
+
+    exec->pedantic_hinting = pedantic;
+
+    TT_Set_CodeRange( exec,
+                      tt_coderange_cvt,
+                      face->cvt_program,
+                      (FT_Long)face->cvt_program_size );
+
+    TT_Clear_CodeRange( exec, tt_coderange_glyph );
+
+    if ( face->cvt_program_size > 0 )
+    {
+      TT_Goto_CodeRange( exec, tt_coderange_cvt, 0 );
+
+      FT_TRACE4(( "Executing `prep' table.\n" ));
+      error = face->interpreter( exec );
+#ifdef FT_DEBUG_LEVEL_TRACE
+      if ( error )
+        FT_TRACE4(( "  interpretation failed with error code 0x%x\n",
+                    error ));
+#endif
+    }
+    else
+      error = FT_Err_Ok;
+
+    size->cvt_ready = error;
+
+    /* UNDOCUMENTED!  The MS rasterizer doesn't allow the following */
+    /* graphics state variables to be modified by the CVT program.  */
+
+    exec->GS.dualVector.x = 0x4000;
+    exec->GS.dualVector.y = 0;
+    exec->GS.projVector.x = 0x4000;
+    exec->GS.projVector.y = 0x0;
+    exec->GS.freeVector.x = 0x4000;
+    exec->GS.freeVector.y = 0x0;
+
+    exec->GS.rp0 = 0;
+    exec->GS.rp1 = 0;
+    exec->GS.rp2 = 0;
+
+    exec->GS.gep0 = 1;
+    exec->GS.gep1 = 1;
+    exec->GS.gep2 = 1;
+
+    exec->GS.loop = 1;
+
+    /* save as default graphics state */
+    size->GS = exec->GS;
+
+    TT_Save_Context( exec, size );
+
+    return error;
+  }
+
+
+  static void
+  tt_size_done_bytecode( FT_Size  ftsize )
+  {
+    TT_Size    size   = (TT_Size)ftsize;
+    TT_Face    face   = (TT_Face)ftsize->face;
+    FT_Memory  memory = face->root.memory;
+
+    if ( size->context )
+    {
+      TT_Done_Context( size->context );
+      size->context = NULL;
+    }
+
+    FT_FREE( size->cvt );
+    size->cvt_size = 0;
+
+    /* free storage area */
+    FT_FREE( size->storage );
+    size->storage_size = 0;
+
+    /* twilight zone */
+    tt_glyphzone_done( &size->twilight );
+
+    FT_FREE( size->function_defs );
+    FT_FREE( size->instruction_defs );
+
+    size->num_function_defs    = 0;
+    size->max_function_defs    = 0;
+    size->num_instruction_defs = 0;
+    size->max_instruction_defs = 0;
+
+    size->max_func = 0;
+    size->max_ins  = 0;
+
+    size->bytecode_ready = -1;
+    size->cvt_ready      = -1;
+  }
+
+
+  /* Initialize bytecode-related fields in the size object.       */
+  /* We do this only if bytecode interpretation is really needed. */
+  static FT_Error
+  tt_size_init_bytecode( FT_Size  ftsize,
+                         FT_Bool  pedantic )
+  {
+    FT_Error   error;
+    TT_Size    size = (TT_Size)ftsize;
+    TT_Face    face = (TT_Face)ftsize->face;
+    FT_Memory  memory = face->root.memory;
+
+    FT_UShort       n_twilight;
+    TT_MaxProfile*  maxp = &face->max_profile;
+
+
+    /* clean up bytecode related data */
+    FT_FREE( size->function_defs );
+    FT_FREE( size->instruction_defs );
+    FT_FREE( size->cvt );
+    FT_FREE( size->storage );
+
+    if ( size->context )
+      TT_Done_Context( size->context );
+    tt_glyphzone_done( &size->twilight );
+
+    size->bytecode_ready = -1;
+    size->cvt_ready      = -1;
+
+    size->context = TT_New_Context( (TT_Driver)face->root.driver );
+
+    size->max_function_defs    = maxp->maxFunctionDefs;
+    size->max_instruction_defs = maxp->maxInstructionDefs;
+
+    size->num_function_defs    = 0;
+    size->num_instruction_defs = 0;
+
+    size->max_func = 0;
+    size->max_ins  = 0;
+
+    size->cvt_size     = face->cvt_size;
+    size->storage_size = maxp->maxStorage;
+
+    /* Set default metrics */
+    {
+      TT_Size_Metrics*  tt_metrics = &size->ttmetrics;
+
+
+      tt_metrics->rotated   = FALSE;
+      tt_metrics->stretched = FALSE;
+
+      /* Set default engine compensation.  Value 3 is not described */
+      /* in the OpenType specification (as of Mai 2019), but Greg   */
+      /* says that MS handles it the same as `gray'.                */
+      /*                                                            */
+      /* The Apple specification says that the compensation for     */
+      /* `gray' is always zero.  FreeType doesn't do any            */
+      /* compensation at all.                                       */
+      tt_metrics->compensations[0] = 0;   /* gray  */
+      tt_metrics->compensations[1] = 0;   /* black */
+      tt_metrics->compensations[2] = 0;   /* white */
+      tt_metrics->compensations[3] = 0;   /* zero  */
+    }
+
+    /* allocate function defs, instruction defs, cvt, and storage area */
+    if ( FT_NEW_ARRAY( size->function_defs,    size->max_function_defs    ) ||
+         FT_NEW_ARRAY( size->instruction_defs, size->max_instruction_defs ) ||
+         FT_NEW_ARRAY( size->cvt,              size->cvt_size             ) ||
+         FT_NEW_ARRAY( size->storage,          size->storage_size         ) )
+      goto Exit;
+
+    /* reserve twilight zone */
+    n_twilight = maxp->maxTwilightPoints;
+
+    /* there are 4 phantom points (do we need this?) */
+    n_twilight += 4;
+
+    error = tt_glyphzone_new( memory, n_twilight, 0, &size->twilight );
+    if ( error )
+      goto Exit;
+
+    size->twilight.n_points = n_twilight;
+
+    size->GS = tt_default_graphics_state;
+
+    /* set `face->interpreter' according to the debug hook present */
+    {
+      FT_Library  library = face->root.driver->root.library;
+
+
+      face->interpreter = (TT_Interpreter)
+                            library->debug_hooks[FT_DEBUG_HOOK_TRUETYPE];
+      if ( !face->interpreter )
+        face->interpreter = (TT_Interpreter)TT_RunIns;
+    }
+
+    /* Fine, now run the font program! */
+
+    /* In case of an error while executing `fpgm', we intentionally don't */
+    /* clean up immediately – bugs in the `fpgm' are so fundamental that  */
+    /* all following hinting calls should fail.  Additionally, `fpgm' is  */
+    /* to be executed just once; calling it again is completely useless   */
+    /* and might even lead to extremely slow behaviour if it is malformed */
+    /* (containing an infinite loop, for example).                        */
+    error = tt_size_run_fpgm( size, pedantic );
+    return error;
+
+  Exit:
+    if ( error )
+      tt_size_done_bytecode( ftsize );
+
+    return error;
+  }
+
+
+  FT_LOCAL_DEF( FT_Error )
+  tt_size_ready_bytecode( TT_Size  size,
+                          FT_Bool  pedantic )
+  {
+    FT_Error  error = FT_Err_Ok;
+
+
+    if ( size->bytecode_ready < 0 )
+      error = tt_size_init_bytecode( (FT_Size)size, pedantic );
+    else
+      error = size->bytecode_ready;
+
+    if ( error )
+      goto Exit;
+
+    /* rescale CVT when needed */
+    if ( size->cvt_ready < 0 )
+    {
+      FT_UShort  i;
+
+
+      /* all twilight points are originally zero */
+      for ( i = 0; i < size->twilight.n_points; i++ )
+      {
+        size->twilight.org[i].x = 0;
+        size->twilight.org[i].y = 0;
+        size->twilight.cur[i].x = 0;
+        size->twilight.cur[i].y = 0;
+      }
+
+      /* clear storage area */
+      for ( i = 0; i < size->storage_size; i++ )
+        size->storage[i] = 0;
+
+      size->GS = tt_default_graphics_state;
+
+      error = tt_size_run_prep( size, pedantic );
+    }
+    else
+      error = size->cvt_ready;
+
+  Exit:
+    return error;
+  }
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_size_init
+   *
+   * @Description:
+   *   Initialize a new TrueType size object.
+   *
+   * @InOut:
+   *   size ::
+   *     A handle to the size object.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_size_init( FT_Size  ttsize )           /* TT_Size */
+  {
+    TT_Size   size  = (TT_Size)ttsize;
+    FT_Error  error = FT_Err_Ok;
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+    size->bytecode_ready = -1;
+    size->cvt_ready      = -1;
+#endif
+
+    size->ttmetrics.valid = FALSE;
+    size->strike_index    = 0xFFFFFFFFUL;
+
+    return error;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_size_done
+   *
+   * @Description:
+   *   The TrueType size object finalizer.
+   *
+   * @Input:
+   *   size ::
+   *     A handle to the target size object.
+   */
+  FT_LOCAL_DEF( void )
+  tt_size_done( FT_Size  ttsize )           /* TT_Size */
+  {
+    TT_Size  size = (TT_Size)ttsize;
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+    tt_size_done_bytecode( ttsize );
+#endif
+
+    size->ttmetrics.valid = FALSE;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_size_reset_height
+   *
+   * @Description:
+   *   Recompute a TrueType size's ascender, descender, and height
+   *   when resolutions and character dimensions have been changed.
+   *   Used for variation fonts as an iterator function.
+   *
+   * @Input:
+   *   ft_size ::
+   *     A handle to the target TT_Size object. This function will be called
+   *     through a `FT_Size_Reset_Func` pointer which takes `FT_Size`. This
+   *     function must take `FT_Size` as a result. The passed `FT_Size` is
+   *     expected to point to a `TT_Size`.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_size_reset_height( FT_Size  ft_size )
+  {
+    TT_Size           size         = (TT_Size)ft_size;
+    TT_Face           face         = (TT_Face)size->root.face;
+    FT_Size_Metrics*  size_metrics = &size->hinted_metrics;
+
+    size->ttmetrics.valid = FALSE;
+
+    /* copy the result from base layer */
+    *size_metrics = size->root.metrics;
+
+    if ( size_metrics->x_ppem < 1 || size_metrics->y_ppem < 1 )
+      return FT_THROW( Invalid_PPem );
+
+    /* This bit flag, if set, indicates that the ppems must be       */
+    /* rounded to integers.  Nearly all TrueType fonts have this bit */
+    /* set, as hinting won't work really well otherwise.             */
+    /*                                                               */
+    if ( face->header.Flags & 8 )
+    {
+      /* the TT spec always asks for ROUND, not FLOOR or CEIL */
+      size_metrics->ascender = FT_PIX_ROUND(
+                                 FT_MulFix( face->root.ascender,
+                                            size_metrics->y_scale ) );
+      size_metrics->descender = FT_PIX_ROUND(
+                                 FT_MulFix( face->root.descender,
+                                            size_metrics->y_scale ) );
+      size_metrics->height = FT_PIX_ROUND(
+                               FT_MulFix( face->root.height,
+                                          size_metrics->y_scale ) );
+    }
+
+    size->ttmetrics.valid = TRUE;
+
+    return FT_Err_Ok;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_size_reset
+   *
+   * @Description:
+   *   Reset a TrueType size when resolutions and character dimensions
+   *   have been changed.
+   *
+   * @Input:
+   *   size ::
+   *     A handle to the target size object.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_size_reset( TT_Size  size )
+  {
+    FT_Error          error;
+    TT_Face           face         = (TT_Face)size->root.face;
+    FT_Size_Metrics*  size_metrics = &size->hinted_metrics;
+
+
+    error = tt_size_reset_height( (FT_Size)size );
+    if ( error )
+      return error;
+
+    if ( face->header.Flags & 8 )
+    {
+      /* base scaling values on integer ppem values, */
+      /* as mandated by the TrueType specification   */
+      size_metrics->x_scale = FT_DivFix( size_metrics->x_ppem << 6,
+                                         face->root.units_per_EM );
+      size_metrics->y_scale = FT_DivFix( size_metrics->y_ppem << 6,
+                                         face->root.units_per_EM );
+
+      size_metrics->max_advance = FT_PIX_ROUND(
+                                    FT_MulFix( face->root.max_advance_width,
+                                               size_metrics->x_scale ) );
+    }
+
+    /* compute new transformation */
+    if ( size_metrics->x_ppem >= size_metrics->y_ppem )
+    {
+      size->ttmetrics.scale   = size_metrics->x_scale;
+      size->ttmetrics.ppem    = size_metrics->x_ppem;
+      size->ttmetrics.x_ratio = 0x10000L;
+      size->ttmetrics.y_ratio = FT_DivFix( size_metrics->y_ppem,
+                                           size_metrics->x_ppem );
+    }
+    else
+    {
+      size->ttmetrics.scale   = size_metrics->y_scale;
+      size->ttmetrics.ppem    = size_metrics->y_ppem;
+      size->ttmetrics.x_ratio = FT_DivFix( size_metrics->x_ppem,
+                                           size_metrics->y_ppem );
+      size->ttmetrics.y_ratio = 0x10000L;
+    }
+
+    size->widthp = tt_face_get_device_metrics( face, size_metrics->x_ppem, 0 );
+
+    size->metrics = size_metrics;
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+    size->cvt_ready = -1;
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+    return FT_Err_Ok;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_driver_init
+   *
+   * @Description:
+   *   Initialize a given TrueType driver object.
+   *
+   * @Input:
+   *   driver ::
+   *     A handle to the target driver object.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_driver_init( FT_Module  ttdriver )     /* TT_Driver */
+  {
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+    TT_Driver  driver = (TT_Driver)ttdriver;
+
+    driver->interpreter_version = TT_INTERPRETER_VERSION_35;
+#ifdef TT_SUPPORT_SUBPIXEL_HINTING_MINIMAL
+    driver->interpreter_version = TT_INTERPRETER_VERSION_40;
+#endif
+
+#else /* !TT_USE_BYTECODE_INTERPRETER */
+
+    FT_UNUSED( ttdriver );
+
+#endif /* !TT_USE_BYTECODE_INTERPRETER */
+
+    return FT_Err_Ok;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_driver_done
+   *
+   * @Description:
+   *   Finalize a given TrueType driver.
+   *
+   * @Input:
+   *   driver ::
+   *     A handle to the target TrueType driver.
+   */
+  FT_LOCAL_DEF( void )
+  tt_driver_done( FT_Module  ttdriver )     /* TT_Driver */
+  {
+    FT_UNUSED( ttdriver );
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_slot_init
+   *
+   * @Description:
+   *   Initialize a new slot object.
+   *
+   * @InOut:
+   *   slot ::
+   *     A handle to the slot object.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_slot_init( FT_GlyphSlot  slot )
+  {
+    return FT_GlyphLoader_CreateExtra( slot->internal->loader );
+  }
+
+
+/* END */
diff --git a/src/preload/ttobjs.h b/src/preload/ttobjs.h
new file mode 100644
index 000000000..40eb37b4c
--- /dev/null
+++ b/src/preload/ttobjs.h
@@ -0,0 +1,426 @@
+/****************************************************************************
+ *
+ * ttobjs.h
+ *
+ *   Objects manager (specification).
+ *
+ * Copyright (C) 1996-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+#ifndef TTOBJS_H_
+#define TTOBJS_H_
+
+
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/tttypes.h>
+
+
+FT_BEGIN_HEADER
+
+
+  /**************************************************************************
+   *
+   * @Type:
+   *   TT_Driver
+   *
+   * @Description:
+   *   A handle to a TrueType driver object.
+   */
+  typedef struct TT_DriverRec_*  TT_Driver;
+
+
+  /**************************************************************************
+   *
+   * @Type:
+   *   TT_GlyphSlot
+   *
+   * @Description:
+   *   A handle to a TrueType glyph slot object.
+   *
+   * @Note:
+   *   This is a direct typedef of FT_GlyphSlot, as there is nothing
+   *   specific about the TrueType glyph slot.
+   */
+  typedef FT_GlyphSlot  TT_GlyphSlot;
+
+
+  /**************************************************************************
+   *
+   * @Struct:
+   *   TT_GraphicsState
+   *
+   * @Description:
+   *   The TrueType graphics state used during bytecode interpretation.
+   */
+  typedef struct  TT_GraphicsState_
+  {
+    FT_UShort      rp0;
+    FT_UShort      rp1;
+    FT_UShort      rp2;
+
+    FT_UnitVector  dualVector;
+    FT_UnitVector  projVector;
+    FT_UnitVector  freeVector;
+
+    FT_Long        loop;
+    FT_F26Dot6     minimum_distance;
+    FT_Int         round_state;
+
+    FT_Bool        auto_flip;
+    FT_F26Dot6     control_value_cutin;
+    FT_F26Dot6     single_width_cutin;
+    FT_F26Dot6     single_width_value;
+    FT_UShort      delta_base;
+    FT_UShort      delta_shift;
+
+    FT_Byte        instruct_control;
+    /* According to Greg Hitchcock from Microsoft, the `scan_control'     */
+    /* variable as documented in the TrueType specification is a 32-bit   */
+    /* integer; the high-word part holds the SCANTYPE value, the low-word */
+    /* part the SCANCTRL value.  We separate it into two fields.          */
+    FT_Bool        scan_control;
+    FT_Int         scan_type;
+
+    FT_UShort      gep0;
+    FT_UShort      gep1;
+    FT_UShort      gep2;
+
+  } TT_GraphicsState;
+
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+  FT_LOCAL( void )
+  tt_glyphzone_done( TT_GlyphZone  zone );
+
+  FT_LOCAL( FT_Error )
+  tt_glyphzone_new( FT_Memory     memory,
+                    FT_UShort     maxPoints,
+                    FT_Short      maxContours,
+                    TT_GlyphZone  zone );
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+
+
+  /**************************************************************************
+   *
+   * EXECUTION SUBTABLES
+   *
+   * These sub-tables relate to instruction execution.
+   *
+   */
+
+
+#define TT_MAX_CODE_RANGES  3
+
+
+  /**************************************************************************
+   *
+   * There can only be 3 active code ranges at once:
+   *   - the Font Program
+   *   - the CVT Program
+   *   - a glyph's instructions set
+   */
+  typedef enum  TT_CodeRange_Tag_
+  {
+    tt_coderange_none = 0,
+    tt_coderange_font,
+    tt_coderange_cvt,
+    tt_coderange_glyph
+
+  } TT_CodeRange_Tag;
+
+
+  typedef struct  TT_CodeRange_
+  {
+    FT_Byte*  base;
+    FT_Long   size;
+
+  } TT_CodeRange;
+
+  typedef TT_CodeRange  TT_CodeRangeTable[TT_MAX_CODE_RANGES];
+
+
+  /**************************************************************************
+   *
+   * Defines a function/instruction definition record.
+   */
+  typedef struct  TT_DefRecord_
+  {
+    FT_Int    range;          /* in which code range is it located?     */
+    FT_Long   start;          /* where does it start?                   */
+    FT_Long   end;            /* where does it end?                     */
+    FT_UInt   opc;            /* function #, or instruction code        */
+    FT_Bool   active;         /* is it active?                          */
+
+  } TT_DefRecord, *TT_DefArray;
+
+
+  /**************************************************************************
+   *
+   * Subglyph transformation record.
+   */
+  typedef struct  TT_Transform_
+  {
+    FT_Fixed    xx, xy;     /* transformation matrix coefficients */
+    FT_Fixed    yx, yy;
+    FT_F26Dot6  ox, oy;     /* offsets                            */
+
+  } TT_Transform;
+
+
+  /**************************************************************************
+   *
+   * A note regarding non-squared pixels:
+   *
+   * (This text will probably go into some docs at some time; for now, it
+   * is kept here to explain some definitions in the TT_Size_Metrics
+   * record).
+   *
+   * The CVT is a one-dimensional array containing values that control
+   * certain important characteristics in a font, like the height of all
+   * capitals, all lowercase letter, default spacing or stem width/height.
+   *
+   * These values are found in FUnits in the font file, and must be scaled
+   * to pixel coordinates before being used by the CVT and glyph programs.
+   * Unfortunately, when using distinct x and y resolutions (or distinct x
+   * and y pointsizes), there are two possible scalings.
+   *
+   * A first try was to implement a `lazy' scheme where all values were
+   * scaled when first used.  However, while some values are always used
+   * in the same direction, some others are used under many different
+   * circumstances and orientations.
+   *
+   * I have found a simpler way to do the same, and it even seems to work
+   * in most of the cases:
+   *
+   * - All CVT values are scaled to the maximum ppem size.
+   *
+   * - When performing a read or write in the CVT, a ratio factor is used
+   *   to perform adequate scaling.  Example:
+   *
+   *     x_ppem = 14
+   *     y_ppem = 10
+   *
+   *   We choose ppem = x_ppem = 14 as the CVT scaling size.  All cvt
+   *   entries are scaled to it.
+   *
+   *     x_ratio = 1.0
+   *     y_ratio = y_ppem/ppem (< 1.0)
+   *
+   *   We compute the current ratio like:
+   *
+   *   - If projVector is horizontal,
+   *       ratio = x_ratio = 1.0
+   *
+   *   - if projVector is vertical,
+   *       ratio = y_ratio
+   *
+   *   - else,
+   *       ratio = sqrt( (proj.x * x_ratio) ^ 2 + (proj.y * y_ratio) ^ 2 )
+   *
+   *   Reading a cvt value returns
+   *     ratio * cvt[index]
+   *
+   *   Writing a cvt value in pixels:
+   *     cvt[index] / ratio
+   *
+   *   The current ppem is simply
+   *     ratio * ppem
+   *
+   */
+
+
+  /**************************************************************************
+   *
+   * Metrics used by the TrueType size and context objects.
+   */
+  typedef struct  TT_Size_Metrics_
+  {
+    /* for non-square pixels */
+    FT_Long     x_ratio;
+    FT_Long     y_ratio;
+
+    FT_UShort   ppem;               /* maximum ppem size              */
+    FT_Long     ratio;              /* current ratio                  */
+    FT_Fixed    scale;
+
+    FT_F26Dot6  compensations[4];   /* device-specific compensations  */
+
+    FT_Bool     valid;
+
+    FT_Bool     rotated;            /* `is the glyph rotated?'-flag   */
+    FT_Bool     stretched;          /* `is the glyph stretched?'-flag */
+
+  } TT_Size_Metrics;
+
+
+  /**************************************************************************
+   *
+   * TrueType size class.
+   */
+  typedef struct  TT_SizeRec_
+  {
+    FT_SizeRec         root;
+
+    /* we have our own copy of metrics so that we can modify */
+    /* it without affecting auto-hinting (when used)         */
+    FT_Size_Metrics*   metrics;        /* for the current rendering mode */
+    FT_Size_Metrics    hinted_metrics; /* for the hinted rendering mode  */
+
+    TT_Size_Metrics    ttmetrics;
+
+    FT_Byte*           widthp;          /* glyph widths from the hdmx table */
+
+    FT_ULong           strike_index;      /* 0xFFFFFFFF to indicate invalid */
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+    FT_Long            point_size;    /* for the `MPS' bytecode instruction */
+
+    FT_UInt            num_function_defs; /* number of function definitions */
+    FT_UInt            max_function_defs;
+    TT_DefArray        function_defs;     /* table of function definitions  */
+
+    FT_UInt            num_instruction_defs;  /* number of ins. definitions */
+    FT_UInt            max_instruction_defs;
+    TT_DefArray        instruction_defs;      /* table of ins. definitions  */
+
+    FT_UInt            max_func;
+    FT_UInt            max_ins;
+
+    TT_CodeRangeTable  codeRangeTable;
+
+    TT_GraphicsState   GS;
+
+    FT_ULong           cvt_size;      /* the scaled control value table */
+    FT_Long*           cvt;
+
+    FT_UShort          storage_size; /* The storage area is now part of */
+    FT_Long*           storage;      /* the instance                    */
+
+    TT_GlyphZoneRec    twilight;     /* The instance's twilight zone    */
+
+    TT_ExecContext     context;
+
+    /* if negative, `fpgm' (resp. `prep'), wasn't executed yet; */
+    /* otherwise it is the returned error code                  */
+    FT_Error           bytecode_ready;
+    FT_Error           cvt_ready;
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+  } TT_SizeRec;
+
+
+  /**************************************************************************
+   *
+   * TrueType driver class.
+   */
+  typedef struct  TT_DriverRec_
+  {
+    FT_DriverRec  root;
+
+    TT_GlyphZoneRec  zone;     /* glyph loader points zone */
+
+    FT_UInt  interpreter_version;
+
+  } TT_DriverRec;
+
+
+  /* Note: All of the functions below (except tt_size_reset()) are used    */
+  /* as function pointers in a FT_Driver_ClassRec.  Therefore their        */
+  /* parameters are of types FT_Face, FT_Size, etc., rather than TT_Face,  */
+  /* TT_Size, etc., so that the compiler can confirm that the types and    */
+  /* number of parameters are correct.  In all cases the FT_xxx types are  */
+  /* cast to their TT_xxx counterparts inside the functions since FreeType */
+  /* will always use the TT driver to create them.                         */
+
+
+  /**************************************************************************
+   *
+   * Face functions
+   */
+  FT_LOCAL( FT_Error )
+  tt_face_init( FT_Stream      stream,
+                FT_Face        ttface,      /* TT_Face */
+                FT_Int         face_index,
+                FT_Int         num_params,
+                FT_Parameter*  params );
+
+  FT_LOCAL( void )
+  tt_face_done( FT_Face  ttface );          /* TT_Face */
+
+
+  /**************************************************************************
+   *
+   * Size functions
+   */
+  FT_LOCAL( FT_Error )
+  tt_size_init( FT_Size  ttsize );          /* TT_Size */
+
+  FT_LOCAL( void )
+  tt_size_done( FT_Size  ttsize );          /* TT_Size */
+
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+  FT_LOCAL( FT_Error )
+  tt_size_run_fpgm( TT_Size  size,
+                    FT_Bool  pedantic );
+
+  FT_LOCAL( FT_Error )
+  tt_size_run_prep( TT_Size  size,
+                    FT_Bool  pedantic );
+
+  FT_LOCAL( FT_Error )
+  tt_size_ready_bytecode( TT_Size  size,
+                          FT_Bool  pedantic );
+
+#endif /* TT_USE_BYTECODE_INTERPRETER */
+
+  FT_LOCAL( FT_Error )
+  tt_size_reset_height( FT_Size  size );
+
+  FT_LOCAL( FT_Error )
+  tt_size_reset( TT_Size  size );
+
+
+  /**************************************************************************
+   *
+   * Driver functions
+   */
+  FT_LOCAL( FT_Error )
+  tt_driver_init( FT_Module  ttdriver );    /* TT_Driver */
+
+  FT_LOCAL( void )
+  tt_driver_done( FT_Module  ttdriver );    /* TT_Driver */
+
+
+  /**************************************************************************
+   *
+   * Slot functions
+   */
+  FT_LOCAL( FT_Error )
+  tt_slot_init( FT_GlyphSlot  slot );
+
+
+  /* auxiliary */
+#define IS_HINTED( flags )  ( ( flags & FT_LOAD_NO_HINTING ) == 0 )
+
+
+FT_END_HEADER
+
+#endif /* TTOBJS_H_ */
+
+
+/* END */
diff --git a/src/preload/ttpload.c b/src/preload/ttpload.c
new file mode 100644
index 000000000..54a64c7b4
--- /dev/null
+++ b/src/preload/ttpload.c
@@ -0,0 +1,665 @@
+/****************************************************************************
+ *
+ * ttpload.c
+ *
+ *   TrueType-specific tables loader (body).
+ *
+ * Copyright (C) 1996-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+#include <freetype/internal/ftdebug.h>
+#include <freetype/internal/ftobjs.h>
+#include <freetype/internal/ftstream.h>
+#include <freetype/tttags.h>
+
+#include "ttpload.h"
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+#include "ttgxvar.h"
+#endif
+
+#include "tterrors.h"
+
+
+  /**************************************************************************
+   *
+   * The macro FT_COMPONENT is used in trace mode.  It is an implicit
+   * parameter of the FT_TRACE() and FT_ERROR() macros, used to print/log
+   * messages during execution.
+   */
+#undef  FT_COMPONENT
+#define FT_COMPONENT  ttpload
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_face_load_loca
+   *
+   * @Description:
+   *   Load the locations table.
+   *
+   * @InOut:
+   *   face ::
+   *     A handle to the target face object.
+   *
+   * @Input:
+   *   stream ::
+   *     The input stream.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_face_load_loca( TT_Face    face,
+                     FT_Stream  stream )
+  {
+    FT_Error  error;
+    FT_ULong  table_len;
+    FT_Int    shift;
+
+
+    /* we need the size of the `glyf' table for malformed `loca' tables */
+    error = face->goto_table( face, TTAG_glyf, stream, &face->glyf_len );
+
+    /* it is possible that a font doesn't have a glyf table at all */
+    /* or its size is zero                                         */
+    if ( FT_ERR_EQ( error, Table_Missing ) )
+    {
+      face->glyf_len    = 0;
+      face->glyf_offset = 0;
+    }
+    else if ( error )
+      goto Exit;
+    else
+    {
+#ifdef FT_CONFIG_OPTION_INCREMENTAL
+      if ( face->root.internal->incremental_interface )
+        face->glyf_offset = 0;
+      else
+#endif
+        face->glyf_offset = FT_STREAM_POS();
+    }
+
+    FT_TRACE2(( "Locations " ));
+    error = face->goto_table( face, TTAG_loca, stream, &table_len );
+    if ( error )
+    {
+      error = FT_THROW( Locations_Missing );
+      goto Exit;
+    }
+
+    shift = face->header.Index_To_Loc_Format != 0 ? 2 : 1;
+
+    if ( table_len > 0x10000UL << shift )
+    {
+      FT_TRACE2(( "table too large\n" ));
+      table_len = 0x10000UL << shift;
+    }
+
+    face->num_locations = table_len >> shift;
+
+    if ( face->num_locations != (FT_ULong)face->root.num_glyphs + 1 )
+    {
+      FT_TRACE2(( "glyph count mismatch!  loca: %ld, maxp: %ld\n",
+                  face->num_locations - 1, face->root.num_glyphs ));
+
+      /* we only handle the case where `maxp' gives a larger value */
+      if ( face->num_locations < (FT_ULong)face->root.num_glyphs + 1 )
+      {
+        FT_ULong  new_loca_len =
+                    ( (FT_ULong)face->root.num_glyphs + 1 ) << shift;
+
+        TT_Table  entry = face->dir_tables;
+        TT_Table  limit = entry + face->num_tables;
+
+        FT_Long  pos   = (FT_Long)FT_STREAM_POS();
+        FT_Long  dist  = 0x7FFFFFFFL;
+        FT_Bool  found = 0;
+
+
+        /* compute the distance to next table in font file */
+        for ( ; entry < limit; entry++ )
+        {
+          FT_Long  diff = (FT_Long)entry->Offset - pos;
+
+
+          if ( diff > 0 && diff < dist )
+          {
+            dist  = diff;
+            found = 1;
+          }
+        }
+
+        if ( !found )
+        {
+          /* `loca' is the last table */
+          dist = (FT_Long)stream->size - pos;
+        }
+
+        if ( new_loca_len <= (FT_ULong)dist )
+        {
+          face->num_locations = (FT_ULong)face->root.num_glyphs + 1;
+          table_len           = new_loca_len;
+
+          FT_TRACE2(( "adjusting num_locations to %ld\n",
+                      face->num_locations ));
+        }
+        else
+        {
+          face->root.num_glyphs = face->num_locations
+                                    ? (FT_Long)face->num_locations - 1 : 0;
+
+          FT_TRACE2(( "adjusting num_glyphs to %ld\n",
+                      face->root.num_glyphs ));
+        }
+      }
+    }
+
+    /*
+     * Extract the frame.  We don't need to decompress it since
+     * we are able to parse it directly.
+     */
+    if ( FT_FRAME_EXTRACT( table_len, face->glyph_locations ) )
+      goto Exit;
+
+    FT_TRACE2(( "loaded\n" ));
+
+  Exit:
+    return error;
+  }
+
+
+  FT_LOCAL_DEF( FT_ULong )
+  tt_face_get_location( FT_Face    face,   /* TT_Face */
+                        FT_UInt    gindex,
+                        FT_ULong  *asize )
+  {
+    TT_Face   ttface = (TT_Face)face;
+    FT_ULong  pos1, pos2;
+    FT_Byte*  p;
+    FT_Byte*  p_limit;
+
+
+    pos1 = pos2 = 0;
+
+    if ( gindex < ttface->num_locations )
+    {
+      if ( ttface->header.Index_To_Loc_Format != 0 )
+      {
+        p       = ttface->glyph_locations + gindex * 4;
+        p_limit = ttface->glyph_locations + ttface->num_locations * 4;
+
+        pos1 = FT_NEXT_ULONG( p );
+        pos2 = pos1;
+
+        if ( p + 4 <= p_limit )
+          pos2 = FT_NEXT_ULONG( p );
+      }
+      else
+      {
+        p       = ttface->glyph_locations + gindex * 2;
+        p_limit = ttface->glyph_locations + ttface->num_locations * 2;
+
+        pos1 = FT_NEXT_USHORT( p );
+        pos2 = pos1;
+
+        if ( p + 2 <= p_limit )
+          pos2 = FT_NEXT_USHORT( p );
+
+        pos1 <<= 1;
+        pos2 <<= 1;
+      }
+    }
+
+    /* Check broken location data. */
+    if ( pos1 > ttface->glyf_len )
+    {
+      FT_TRACE1(( "tt_face_get_location:"
+                  " too large offset (0x%08lx) found for glyph index %d,\n",
+                  pos1, gindex ));
+      FT_TRACE1(( "                     "
+                  " exceeding the end of `glyf' table (0x%08lx)\n",
+                  ttface->glyf_len ));
+      *asize = 0;
+      return 0;
+    }
+
+    if ( pos2 > ttface->glyf_len )
+    {
+      /* We try to sanitize the last `loca' entry. */
+      if ( gindex == ttface->num_locations - 2 )
+      {
+        FT_TRACE1(( "tt_face_get_location:"
+                    " too large size (%ld bytes) found for glyph index %d,\n",
+                    pos2 - pos1, gindex ));
+        FT_TRACE1(( "                     "
+                    " truncating at the end of `glyf' table to %ld bytes\n",
+                    ttface->glyf_len - pos1 ));
+        pos2 = ttface->glyf_len;
+      }
+      else
+      {
+        FT_TRACE1(( "tt_face_get_location:"
+                    " too large offset (0x%08lx) found for glyph index %d,\n",
+                    pos2, gindex + 1 ));
+        FT_TRACE1(( "                     "
+                    " exceeding the end of `glyf' table (0x%08lx)\n",
+                    ttface->glyf_len ));
+        *asize = 0;
+        return 0;
+      }
+    }
+
+    /* The `loca' table must be ordered; it refers to the length of */
+    /* an entry as the difference between the current and the next  */
+    /* position.  However, there do exist (malformed) fonts which   */
+    /* don't obey this rule, so we are only able to provide an      */
+    /* upper bound for the size.                                    */
+    /*                                                              */
+    /* We get (intentionally) a wrong, non-zero result in case the  */
+    /* `glyf' table is missing.                                     */
+    if ( pos2 >= pos1 )
+      *asize = (FT_ULong)( pos2 - pos1 );
+    else
+      *asize = (FT_ULong)( ttface->glyf_len - pos1 );
+
+    return pos1;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  tt_face_done_loca( TT_Face  face )
+  {
+    FT_Stream  stream = face->root.stream;
+
+
+    FT_FRAME_RELEASE( face->glyph_locations );
+    face->num_locations = 0;
+  }
+
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_face_load_cvt
+   *
+   * @Description:
+   *   Load the control value table into a face object.
+   *
+   * @InOut:
+   *   face ::
+   *     A handle to the target face object.
+   *
+   * @Input:
+   *   stream ::
+   *     A handle to the input stream.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_face_load_cvt( TT_Face    face,
+                    FT_Stream  stream )
+  {
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+    FT_Error   error;
+    FT_Memory  memory = stream->memory;
+    FT_ULong   table_len;
+
+
+    FT_TRACE2(( "CVT " ));
+
+    error = face->goto_table( face, TTAG_cvt, stream, &table_len );
+    if ( error )
+    {
+      FT_TRACE2(( "is missing\n" ));
+
+      face->cvt_size = 0;
+      face->cvt      = NULL;
+      error          = FT_Err_Ok;
+
+      goto Exit;
+    }
+
+    face->cvt_size = table_len / 2;
+
+    if ( FT_QNEW_ARRAY( face->cvt, face->cvt_size ) )
+      goto Exit;
+
+    if ( FT_FRAME_ENTER( face->cvt_size * 2L ) )
+      goto Exit;
+
+    {
+      FT_Int32*  cur   = face->cvt;
+      FT_Int32*  limit = cur + face->cvt_size;
+
+
+      for ( ; cur < limit; cur++ )
+        *cur = FT_GET_SHORT() * 64;
+    }
+
+    FT_FRAME_EXIT();
+    FT_TRACE2(( "loaded\n" ));
+
+#ifdef TT_CONFIG_OPTION_GX_VAR_SUPPORT
+    if ( face->doblend )
+      error = tt_face_vary_cvt( face, stream );
+#endif
+
+  Exit:
+    return error;
+
+#else /* !TT_USE_BYTECODE_INTERPRETER */
+
+    FT_UNUSED( face   );
+    FT_UNUSED( stream );
+
+    return FT_Err_Ok;
+
+#endif
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_face_load_fpgm
+   *
+   * @Description:
+   *   Load the font program.
+   *
+   * @InOut:
+   *   face ::
+   *     A handle to the target face object.
+   *
+   * @Input:
+   *   stream ::
+   *     A handle to the input stream.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_face_load_fpgm( TT_Face    face,
+                     FT_Stream  stream )
+  {
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+    FT_Error  error;
+    FT_ULong  table_len;
+
+
+    FT_TRACE2(( "Font program " ));
+
+    /* The font program is optional */
+    error = face->goto_table( face, TTAG_fpgm, stream, &table_len );
+    if ( error )
+    {
+      face->font_program      = NULL;
+      face->font_program_size = 0;
+      error                   = FT_Err_Ok;
+
+      FT_TRACE2(( "is missing\n" ));
+    }
+    else
+    {
+      face->font_program_size = table_len;
+      if ( FT_FRAME_EXTRACT( table_len, face->font_program ) )
+        goto Exit;
+
+      FT_TRACE2(( "loaded, %12ld bytes\n", face->font_program_size ));
+    }
+
+  Exit:
+    return error;
+
+#else /* !TT_USE_BYTECODE_INTERPRETER */
+
+    FT_UNUSED( face   );
+    FT_UNUSED( stream );
+
+    return FT_Err_Ok;
+
+#endif
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_face_load_prep
+   *
+   * @Description:
+   *   Load the cvt program.
+   *
+   * @InOut:
+   *   face ::
+   *     A handle to the target face object.
+   *
+   * @Input:
+   *   stream ::
+   *     A handle to the input stream.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+  FT_LOCAL_DEF( FT_Error )
+  tt_face_load_prep( TT_Face    face,
+                     FT_Stream  stream )
+  {
+#ifdef TT_USE_BYTECODE_INTERPRETER
+
+    FT_Error  error;
+    FT_ULong  table_len;
+
+
+    FT_TRACE2(( "Prep program " ));
+
+    error = face->goto_table( face, TTAG_prep, stream, &table_len );
+    if ( error )
+    {
+      face->cvt_program      = NULL;
+      face->cvt_program_size = 0;
+      error                  = FT_Err_Ok;
+
+      FT_TRACE2(( "is missing\n" ));
+    }
+    else
+    {
+      face->cvt_program_size = table_len;
+      if ( FT_FRAME_EXTRACT( table_len, face->cvt_program ) )
+        goto Exit;
+
+      FT_TRACE2(( "loaded, %12ld bytes\n", face->cvt_program_size ));
+    }
+
+  Exit:
+    return error;
+
+#else /* !TT_USE_BYTECODE_INTERPRETER */
+
+    FT_UNUSED( face   );
+    FT_UNUSED( stream );
+
+    return FT_Err_Ok;
+
+#endif
+  }
+
+
+  FT_COMPARE_DEF( int )
+  compare_ppem( const void*  a,
+                const void*  b )
+  {
+    return **(FT_Byte**)a - **(FT_Byte**)b;
+  }
+
+
+  /**************************************************************************
+   *
+   * @Function:
+   *   tt_face_load_hdmx
+   *
+   * @Description:
+   *   Load the `hdmx' table into the face object.
+   *
+   * @Input:
+   *   face ::
+   *     A handle to the target face object.
+   *
+   *   stream ::
+   *     A handle to the input stream.
+   *
+   * @Return:
+   *   FreeType error code.  0 means success.
+   */
+
+  FT_LOCAL_DEF( FT_Error )
+  tt_face_load_hdmx( TT_Face    face,
+                     FT_Stream  stream )
+  {
+    FT_Error   error;
+    FT_Memory  memory = stream->memory;
+    FT_UInt    nn, num_records;
+    FT_ULong   table_size, record_size;
+    FT_Byte*   p;
+    FT_Byte*   limit;
+
+
+    /* this table is optional */
+    error = face->goto_table( face, TTAG_hdmx, stream, &table_size );
+    if ( error || table_size < 8 )
+      return FT_Err_Ok;
+
+    if ( FT_FRAME_EXTRACT( table_size, face->hdmx_table ) )
+      goto Exit;
+
+    p     = face->hdmx_table;
+    limit = p + table_size;
+
+    /* Given that `hdmx' tables are losing its importance (for example, */
+    /* variation fonts introduced in OpenType 1.8 must not have this    */
+    /* table) we no longer test for a correct `version' field.          */
+    p          += 2;
+    num_records = FT_NEXT_USHORT( p );
+    record_size = FT_NEXT_ULONG( p );
+
+    /* There are at least two fonts, HANNOM-A and HANNOM-B version */
+    /* 2.0 (2005), which get this wrong: The upper two bytes of    */
+    /* the size value are set to 0xFF instead of 0x00.  We catch   */
+    /* and fix this.                                               */
+
+    if ( record_size >= 0xFFFF0000UL )
+      record_size &= 0xFFFFU;
+
+    FT_TRACE2(( "Hdmx " ));
+
+    /* The limit for `num_records' is a heuristic value. */
+    if ( num_records > 255 || num_records == 0 )
+    {
+      FT_TRACE2(( "with unreasonable %u records rejected\n", num_records ));
+      goto Fail;
+    }
+
+    /* Out-of-spec tables are rejected.  The record size must be */
+    /* equal to the number of glyphs + 2 + 32-bit padding.       */
+    if ( (FT_Long)record_size != ( ( face->root.num_glyphs + 2 + 3 ) & ~3 ) )
+    {
+      FT_TRACE2(( "with record size off by %ld bytes rejected\n",
+                  (FT_Long)record_size -
+                    ( ( face->root.num_glyphs + 2 + 3 ) & ~3 ) ));
+      goto Fail;
+    }
+
+    if ( FT_QNEW_ARRAY( face->hdmx_records, num_records ) )
+      goto Fail;
+
+    for ( nn = 0; nn < num_records; nn++ )
+    {
+      if ( p + record_size > limit )
+        break;
+      face->hdmx_records[nn] = p;
+      p                     += record_size;
+    }
+
+    /* The records must be already sorted by ppem but it does not */
+    /* hurt to make sure so that the binary search works later.   */
+    ft_qsort( face->hdmx_records, nn, sizeof ( FT_Byte* ), compare_ppem );
+
+    face->hdmx_record_count = nn;
+    face->hdmx_table_size   = table_size;
+    face->hdmx_record_size  = record_size;
+
+    FT_TRACE2(( "%ux%lu loaded\n", num_records, record_size ));
+
+  Exit:
+    return error;
+
+  Fail:
+    FT_FRAME_RELEASE( face->hdmx_table );
+    face->hdmx_table_size = 0;
+    goto Exit;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  tt_face_free_hdmx( TT_Face  face )
+  {
+    FT_Stream  stream = face->root.stream;
+    FT_Memory  memory = stream->memory;
+
+
+    FT_FREE( face->hdmx_records );
+    FT_FRAME_RELEASE( face->hdmx_table );
+  }
+
+
+  /**************************************************************************
+   *
+   * Return the advance width table for a given pixel size if it is found
+   * in the font's `hdmx' table (if any).  The records must be sorted for
+   * the binary search to work properly.
+   */
+  FT_LOCAL_DEF( FT_Byte* )
+  tt_face_get_device_metrics( TT_Face  face,
+                              FT_UInt  ppem,
+                              FT_UInt  gindex )
+  {
+    FT_UInt   min    = 0;
+    FT_UInt   max    = face->hdmx_record_count;
+    FT_UInt   mid;
+    FT_Byte*  result = NULL;
+
+
+    while ( min < max )
+    {
+      mid = ( min + max ) >> 1;
+
+      if ( face->hdmx_records[mid][0] > ppem )
+        max = mid;
+      else if ( face->hdmx_records[mid][0] < ppem )
+        min = mid + 1;
+      else
+      {
+        result = face->hdmx_records[mid] + 2 + gindex;
+        break;
+      }
+    }
+
+    return result;
+  }
+
+
+/* END */
diff --git a/src/preload/ttpload.h b/src/preload/ttpload.h
new file mode 100644
index 000000000..ed229fa46
--- /dev/null
+++ b/src/preload/ttpload.h
@@ -0,0 +1,74 @@
+/****************************************************************************
+ *
+ * ttpload.h
+ *
+ *   TrueType-specific tables loader (specification).
+ *
+ * Copyright (C) 1996-2023 by
+ * David Turner, Robert Wilhelm, and Werner Lemberg.
+ *
+ * This file is part of the FreeType project, and may only be used,
+ * modified, and distributed under the terms of the FreeType project
+ * license, LICENSE.TXT.  By continuing to use, modify, or distribute
+ * this file you indicate that you have read the license and
+ * understand and accept it fully.
+ *
+ */
+
+
+#ifndef TTPLOAD_H_
+#define TTPLOAD_H_
+
+
+#include <freetype/internal/tttypes.h>
+
+
+FT_BEGIN_HEADER
+
+
+  FT_LOCAL( FT_Error )
+  tt_face_load_loca( TT_Face    face,
+                     FT_Stream  stream );
+
+  FT_LOCAL( FT_ULong )
+  tt_face_get_location( FT_Face    face,
+                        FT_UInt    gindex,
+                        FT_ULong  *asize );
+
+  FT_LOCAL( void )
+  tt_face_done_loca( TT_Face  face );
+
+  FT_LOCAL( FT_Error )
+  tt_face_load_cvt( TT_Face    face,
+                    FT_Stream  stream );
+
+  FT_LOCAL( FT_Error )
+  tt_face_load_fpgm( TT_Face    face,
+                     FT_Stream  stream );
+
+
+  FT_LOCAL( FT_Error )
+  tt_face_load_prep( TT_Face    face,
+                     FT_Stream  stream );
+
+
+  FT_LOCAL( FT_Error )
+  tt_face_load_hdmx( TT_Face    face,
+                     FT_Stream  stream );
+
+
+  FT_LOCAL( void )
+  tt_face_free_hdmx( TT_Face  face );
+
+
+  FT_LOCAL( FT_Byte* )
+  tt_face_get_device_metrics( TT_Face    face,
+                              FT_UInt    ppem,
+                              FT_UInt    gindex );
+
+FT_END_HEADER
+
+#endif /* TTPLOAD_H_ */
+
+
+/* END */



reply via email to

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