shithub: freetype+ttf2subf

Download patch

ref: 283c8ed817a645dfd08b5e1fe78a34bd91140389
parent: 831dac881427d0db83ba64fa7cb6f11f9f50cfef
author: Werner Lemberg <[email protected]>
date: Sat Apr 13 11:02:31 EDT 2013

[cff] New files for Adobe's Type 2 interpreter and hinting engine.

git/fs: mount .git/fs: mount/attach disallowed
--- /dev/null
+++ b/src/cff/cf2arrst.c
@@ -1,0 +1,241 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2arrst.c                                                             */
+/*                                                                         */
+/*    Adobe's code for Array Stacks (body).                                */
+/*                                                                         */
+/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#include "cf2ft.h"
+#include FT_INTERNAL_DEBUG_H
+
+#include "cf2glue.h"
+#include "cf2arrst.h"
+
+#include "cf2error.h"
+
+
+  /*
+   * CF2_ArrStack uses an error pointer, to enable shared errors.
+   * Shared errors are necessary when multiple objects allow the program
+   * to continue after detecting errors.  Only the first error should be
+   * recorded.
+   */
+
+  FT_LOCAL_DEF( void )
+  cf2_arrstack_init( CF2_ArrStack  arrstack,
+                     FT_Memory     memory,
+                     FT_Error*     error,
+                     size_t        sizeItem )
+  {
+    FT_ASSERT( arrstack != NULL );
+
+    /* initialize the structure */
+    arrstack->memory    = memory;
+    arrstack->error     = error;
+    arrstack->sizeItem  = sizeItem;
+    arrstack->allocated = 0;
+    arrstack->chunk     = 10;    /* chunks of 10 items */
+    arrstack->count     = 0;
+    arrstack->totalSize = 0;
+    arrstack->ptr       = NULL;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_arrstack_finalize( CF2_ArrStack  arrstack )
+  {
+    FT_Memory  memory = arrstack->memory;     /* for FT_FREE */
+
+
+    FT_ASSERT( arrstack != NULL );
+
+    arrstack->allocated = 0;
+    arrstack->count     = 0;
+    arrstack->totalSize = 0;
+
+    /* free the data buffer */
+    FT_FREE( arrstack->ptr );
+  }
+
+
+  /* allocate or reallocate the buffer size; */
+  /* return false on memory error */
+  static FT_Bool
+  cf2_arrstack_setNumElements( CF2_ArrStack  arrstack,
+                               size_t        numElements )
+  {
+    FT_ASSERT( arrstack != NULL );
+
+    {
+      FT_Error   error  = FT_Err_Ok;        /* for FT_REALLOC */
+      FT_Memory  memory = arrstack->memory; /* for FT_REALLOC */
+
+      FT_Long  newSize = numElements * arrstack->sizeItem;
+
+
+      if ( numElements > LONG_MAX / arrstack->sizeItem )
+        goto exit;
+
+
+      FT_ASSERT( newSize > 0 );   /* avoid realloc with zero size */
+
+      if ( !FT_REALLOC( arrstack->ptr, arrstack->totalSize, newSize ) )
+      {
+        arrstack->allocated = numElements;
+        arrstack->totalSize = newSize;
+
+        if ( arrstack->count > numElements )
+        {
+          /* we truncated the list! */
+          CF2_SET_ERROR( arrstack->error, Stack_Overflow );
+          arrstack->count = numElements;
+          return FALSE;
+        }
+
+        return TRUE;     /* success */
+      }
+    }
+
+  exit:
+    /* if there's not already an error, store this one */
+    CF2_SET_ERROR( arrstack->error, Out_Of_Memory );
+
+    return FALSE;
+  }
+
+
+  /* set the count, ensuring allocation is sufficient */
+  FT_LOCAL_DEF( void )
+  cf2_arrstack_setCount( CF2_ArrStack  arrstack,
+                         size_t        numElements )
+  {
+    FT_ASSERT( arrstack != NULL );
+
+    if ( numElements > arrstack->allocated )
+    {
+      /* expand the allocation first */
+      if ( !cf2_arrstack_setNumElements( arrstack, numElements ) )
+        return;
+    }
+
+    arrstack->count = numElements;
+  }
+
+
+  /* clear the count */
+  FT_LOCAL_DEF( void )
+  cf2_arrstack_clear( CF2_ArrStack  arrstack )
+  {
+    FT_ASSERT( arrstack != NULL );
+
+    arrstack->count = 0;
+  }
+
+
+  /* current number of items */
+  FT_LOCAL_DEF( size_t )
+  cf2_arrstack_size( const CF2_ArrStack  arrstack )
+  {
+    FT_ASSERT( arrstack != NULL );
+
+    return arrstack->count;
+  }
+
+
+  FT_LOCAL_DEF( void* )
+  cf2_arrstack_getBuffer( const CF2_ArrStack  arrstack )
+  {
+    FT_ASSERT( arrstack != NULL );
+
+    return arrstack->ptr;
+  }
+
+
+  /* return pointer to the given element */
+  FT_LOCAL_DEF( void* )
+  cf2_arrstack_getPointer( const CF2_ArrStack  arrstack,
+                           size_t              idx )
+  {
+    void*  newPtr;
+
+
+    FT_ASSERT( arrstack != NULL );
+
+    if ( idx >= arrstack->count )
+    {
+      /* overflow */
+      CF2_SET_ERROR( arrstack->error, Stack_Overflow );
+      idx = 0;    /* choose safe default */
+    }
+
+    newPtr = (FT_Byte*)arrstack->ptr + idx * arrstack->sizeItem;
+
+    return newPtr;
+  }
+
+
+  /* push (append) an element at the end of the list;         */
+  /* return false on memory error                             */
+  /* TODO: should there be a length param for extra checking? */
+  FT_LOCAL_DEF( void )
+  cf2_arrstack_push( CF2_ArrStack  arrstack,
+                     const void*   ptr )
+  {
+    FT_ASSERT( arrstack != NULL );
+
+    if ( arrstack->count == arrstack->allocated )
+    {
+      /* grow the buffer by one chunk */
+      if ( !cf2_arrstack_setNumElements(
+             arrstack, arrstack->allocated + arrstack->chunk ) )
+      {
+        /* on error, ignore the push */
+        return;
+      }
+    }
+
+    FT_ASSERT( ptr != NULL );
+
+    {
+      size_t  offset = arrstack->count * arrstack->sizeItem;
+      void*   newPtr = (FT_Byte*)arrstack->ptr + offset;
+
+
+      FT_MEM_COPY( newPtr, ptr, arrstack->sizeItem );
+      arrstack->count += 1;
+    }
+  }
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2arrst.h
@@ -1,0 +1,100 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2arrst.h                                                             */
+/*                                                                         */
+/*    Adobe's code for Array Stacks (specification).                       */
+/*                                                                         */
+/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __CF2ARRST_H__
+#define __CF2ARRST_H__
+
+
+#include "cf2error.h"
+
+
+FT_BEGIN_HEADER
+
+
+  /* need to define the struct here (not opaque) so it can be allocated by */
+  /* clients                                                               */
+  typedef struct  CF2_ArrStackRec_
+  {
+    FT_Memory  memory;
+    FT_Error*  error;
+
+    size_t  sizeItem;       /* bytes per element             */
+    size_t  allocated;      /* items allocated               */
+    size_t  chunk;          /* allocation increment in items */
+    size_t  count;          /* number of elements allocated  */
+    size_t  totalSize;      /* total bytes allocated         */
+
+    void*  ptr;             /* ptr to data                   */
+
+  } CF2_ArrStackRec, *CF2_ArrStack;
+
+
+  FT_LOCAL( void )
+  cf2_arrstack_init( CF2_ArrStack  arrstack,
+                     FT_Memory     memory,
+                     FT_Error*     error,
+                     size_t        sizeItem );
+  FT_LOCAL( void )
+  cf2_arrstack_finalize( CF2_ArrStack  arrstack );
+
+  FT_LOCAL( void )
+  cf2_arrstack_setCount( CF2_ArrStack  arrstack,
+                         size_t        numElements );
+  FT_LOCAL( void )
+  cf2_arrstack_clear( CF2_ArrStack  arrstack );
+  FT_LOCAL( size_t )
+  cf2_arrstack_size( const CF2_ArrStack  arrstack );
+
+  FT_LOCAL( void* )
+  cf2_arrstack_getBuffer( const CF2_ArrStack  arrstack );
+  FT_LOCAL( void* )
+  cf2_arrstack_getPointer( const CF2_ArrStack  arrstack,
+                           size_t              idx );
+
+  FT_LOCAL( void )
+  cf2_arrstack_push( CF2_ArrStack  arrstack,
+                     const void*   ptr );
+
+
+FT_END_HEADER
+
+
+#endif /* __CF2ARRST_H__ */
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2blues.c
@@ -1,0 +1,584 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2blues.c                                                             */
+/*                                                                         */
+/*    Adobe's code for handling Blue Zones (body).                         */
+/*                                                                         */
+/*  Copyright 2009-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#include "cf2ft.h"
+#include FT_INTERNAL_DEBUG_H
+
+#include "cf2blues.h"
+#include "cf2hints.h"
+#include "cf2font.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  trace_cf2blues
+
+
+  /*
+   * For blue values, FreeType parser produces an array of integers, while
+   * PFR parser produces an array of fixed.
+   * Define a macro to convert FreeType to fixed.
+   *
+   */
+#if 1
+#define cf2_blueToFixed( x )  cf2_intToFixed( x )
+#else
+#define cf2_blueToFixed( x )  ( x )
+#endif
+
+
+  FT_LOCAL_DEF( void )
+  cf2_blues_init( CF2_Blues  blues,
+                  CF2_Font   font )
+  {
+    /* pointer to parsed font object, either PFR ParsedFont or FreeType */
+    /* Decoder                                                          */
+    CFF_Decoder*  decoder = font->decoder;
+
+    CF2_Fixed  zoneHeight;
+    CF2_Fixed  maxZoneHeight = 0;
+    CF2_Fixed  csUnitsPerPixel;
+
+    size_t  numBlueValues;
+    size_t  numOtherBlues;
+    size_t  numFamilyBlues;
+    size_t  numFamilyOtherBlues;
+
+    CF2_Fixed*  blueValues;
+    CF2_Fixed*  otherBlues;
+    CF2_Fixed*  familyBlues;
+    CF2_Fixed*  familyOtherBlues;
+
+    size_t     i;
+    CF2_Fixed  emBoxBottom, emBoxTop;
+
+    CF2_Int  unitsPerEm = font->unitsPerEm;
+
+
+    if ( unitsPerEm == 0 )
+      unitsPerEm = 1000;
+
+    FT_ZERO( blues );
+    blues->scale = font->innerTransform.d;
+
+    cf2_getBlueMetrics( decoder,
+                        &blues->blueScale,
+                        &blues->blueShift,
+                        &blues->blueFuzz );
+
+    cf2_getBlueValues( decoder, &numBlueValues, &blueValues );
+    cf2_getOtherBlues( decoder, &numOtherBlues, &otherBlues );
+    cf2_getFamilyBlues( decoder, &numFamilyBlues, &familyBlues );
+    cf2_getFamilyOtherBlues( decoder, &numFamilyOtherBlues, &familyOtherBlues );
+
+    /*
+     * synthetic em box hint heuristic
+     *
+     * Apply this when ideographic dictionary (LanguageGroup 1) has no
+     * real alignment zones.  Adobe tools generate dummy zones at -250 and
+     * 1100 for a 1000 unit em.  Fonts with ICF-based alignment zones
+     * should not enable the heuristic.  When the heuristic is enabled,
+     * the font's blue zones are ignored.
+     *
+     */
+
+    /* get em box from OS/2 typoAscender/Descender                      */
+    /* TODO: FreeType does not parse these metrics.  Skip them for now. */
+#if 0
+    FCM_getHorizontalLineMetrics( &e,
+                                  font->font,
+                                  &ascender,
+                                  &descender,
+                                  &linegap );
+    if ( ascender - descender == unitsPerEm )
+    {
+      emBoxBottom = cf2_intToFixed( descender );
+      emBoxTop    = cf2_intToFixed( ascender );
+    }
+    else
+#endif
+    {
+      emBoxBottom = CF2_ICF_Bottom;
+      emBoxTop    = CF2_ICF_Top;
+    }
+
+    if ( cf2_getLanguageGroup( decoder ) == 1                   &&
+         ( numBlueValues == 0                                 ||
+           ( numBlueValues == 4                             &&
+             cf2_blueToFixed( blueValues[0] ) < emBoxBottom &&
+             cf2_blueToFixed( blueValues[1] ) < emBoxBottom &&
+             cf2_blueToFixed( blueValues[2] ) > emBoxTop    &&
+             cf2_blueToFixed( blueValues[3] ) > emBoxTop    ) ) )
+    {
+      /*
+       * Construct hint edges suitable for synthetic ghost hints at top
+       * and bottom of em box.  +-CF2_MIN_COUNTER allows for unhinted
+       * features above or below the last hinted edge.  This also gives a
+       * net 1 pixel boost to the height of ideographic glyphs.
+       *
+       * Note: Adjust synthetic hints outward by epsilon (0x.0001) to
+       *       avoid interference.  E.g., some fonts have real hints at
+       *       880 and -120.
+       */
+
+      blues->emBoxBottomEdge.csCoord = emBoxBottom - CF2_FIXED_EPSILON;
+      blues->emBoxBottomEdge.dsCoord = cf2_fixedRound(
+                                         FT_MulFix(
+                                           blues->emBoxBottomEdge.csCoord,
+                                           blues->scale ) ) -
+                                       CF2_MIN_COUNTER;
+      blues->emBoxBottomEdge.scale   = blues->scale;
+      blues->emBoxBottomEdge.flags   = CF2_GhostBottom |
+                                       CF2_Locked |
+                                       CF2_Synthetic;
+
+      blues->emBoxTopEdge.csCoord = emBoxTop + CF2_FIXED_EPSILON +
+                                    2 * font->darkenY;
+      blues->emBoxTopEdge.dsCoord = cf2_fixedRound(
+                                      FT_MulFix(
+                                        blues->emBoxTopEdge.csCoord,
+                                        blues->scale ) ) +
+                                    CF2_MIN_COUNTER;
+      blues->emBoxTopEdge.scale   = blues->scale;
+      blues->emBoxTopEdge.flags   = CF2_GhostTop |
+                                    CF2_Locked |
+                                    CF2_Synthetic;
+
+      blues->doEmBoxHints = TRUE;    /* enable the heuristic */
+
+      return;
+    }
+
+    /* copy `BlueValues' and `OtherBlues' to a combined array of top and */
+    /* bottom zones                                                      */
+    for ( i = 0; i < numBlueValues; i += 2 )
+    {
+      blues->zone[blues->count].csBottomEdge =
+        cf2_blueToFixed( blueValues[i] );
+      blues->zone[blues->count].csTopEdge =
+        cf2_blueToFixed( blueValues[i + 1] );
+
+      zoneHeight = blues->zone[blues->count].csTopEdge -
+                   blues->zone[blues->count].csBottomEdge;
+
+      if ( zoneHeight < 0 )
+      {
+        FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" ));
+        continue;   /* reject this zone */
+      }
+
+      if ( zoneHeight > maxZoneHeight )
+      {
+        /* take maximum before darkening adjustment      */
+        /* so overshoot suppression point doesn't change */
+        maxZoneHeight = zoneHeight;
+      }
+
+      /* adjust both edges of top zone upward by twice darkening amount */
+      if ( i != 0 )
+      {
+        blues->zone[blues->count].csTopEdge    += 2 * font->darkenY;
+        blues->zone[blues->count].csBottomEdge += 2 * font->darkenY;
+      }
+
+      /* first `BlueValue' is bottom zone; others are top */
+      if ( i == 0 )
+      {
+        blues->zone[blues->count].bottomZone =
+          TRUE;
+        blues->zone[blues->count].csFlatEdge =
+          blues->zone[blues->count].csTopEdge;
+      }
+      else
+      {
+        blues->zone[blues->count].bottomZone =
+          FALSE;
+        blues->zone[blues->count].csFlatEdge =
+          blues->zone[blues->count].csBottomEdge;
+      }
+
+      blues->count += 1;
+    }
+
+    for ( i = 0; i < numOtherBlues; i += 2 )
+    {
+      blues->zone[blues->count].csBottomEdge =
+        cf2_blueToFixed( otherBlues[i] );
+      blues->zone[blues->count].csTopEdge =
+        cf2_blueToFixed( otherBlues[i + 1] );
+
+      zoneHeight = blues->zone[blues->count].csTopEdge -
+                   blues->zone[blues->count].csBottomEdge;
+
+      if ( zoneHeight < 0 )
+      {
+        FT_TRACE4(( "cf2_blues_init: ignoring negative zone height\n" ));
+        continue;   /* reject this zone */
+      }
+
+      if ( zoneHeight > maxZoneHeight )
+      {
+        /* take maximum before darkening adjustment      */
+        /* so overshoot suppression point doesn't change */
+        maxZoneHeight = zoneHeight;
+      }
+
+      /* Note: bottom zones are not adjusted for darkening amount */
+
+      /* all OtherBlues are bottom zone */
+      blues->zone[blues->count].bottomZone =
+        TRUE;
+      blues->zone[blues->count].csFlatEdge =
+        blues->zone[blues->count].csTopEdge;
+
+      blues->count += 1;
+    }
+
+    /* Adjust for FamilyBlues */
+
+    /* Search for the nearest flat edge in `FamilyBlues' or                */
+    /* `FamilyOtherBlues'.  According to the Black Book, any matching edge */
+    /* must be within one device pixel                                     */
+
+    csUnitsPerPixel = FT_DivFix( cf2_intToFixed( 1 ), blues->scale );
+
+    /* loop on all zones in this font */
+    for ( i = 0; i < blues->count; i++ )
+    {
+      size_t     j;
+      CF2_Fixed  minDiff;
+      CF2_Fixed  flatFamilyEdge, diff;
+      /* value for this font */
+      CF2_Fixed  flatEdge = blues->zone[i].csFlatEdge;
+
+
+      if ( blues->zone[i].bottomZone )
+      {
+        /* In a bottom zone, the top edge is the flat edge.             */
+        /* Search `FamilyOtherBlues' for bottom zones; look for closest */
+        /* Family edge that is within the one pixel threshold.          */
+
+        minDiff = CF2_FIXED_MAX;
+
+        for ( j = 0; j < numFamilyOtherBlues; j += 2 )
+        {
+          /* top edge */
+          flatFamilyEdge = cf2_blueToFixed( familyOtherBlues[j + 1] );
+
+          diff = cf2_fixedAbs( flatEdge - flatFamilyEdge );
+
+          if ( diff < minDiff && diff < csUnitsPerPixel )
+          {
+            blues->zone[i].csFlatEdge = flatFamilyEdge;
+            minDiff                   = diff;
+
+            if ( diff == 0 )
+              break;
+          }
+        }
+
+        /* check the first member of FamilyBlues, which is a bottom zone */
+        if ( numFamilyBlues >= 2 )
+        {
+          /* top edge */
+          flatFamilyEdge = cf2_blueToFixed( familyBlues[1] );
+
+          diff = cf2_fixedAbs( flatEdge - flatFamilyEdge );
+
+          if ( diff < minDiff && diff < csUnitsPerPixel )
+            blues->zone[i].csFlatEdge = flatFamilyEdge;
+        }
+      }
+      else
+      {
+        /* In a top zone, the bottom edge is the flat edge.                */
+        /* Search `FamilyBlues' for top zones; skip first zone, which is a */
+        /* bottom zone; look for closest Family edge that is within the    */
+        /* one pixel threshold                                             */
+
+        minDiff = CF2_FIXED_MAX;
+
+        for ( j = 2; j < numFamilyBlues; j += 2 )
+        {
+          /* bottom edge */
+          flatFamilyEdge = cf2_blueToFixed( familyBlues[j] );
+
+          /* adjust edges of top zone upward by twice darkening amount */
+          flatFamilyEdge += 2 * font->darkenY;      /* bottom edge */
+
+          diff = cf2_fixedAbs( flatEdge - flatFamilyEdge );
+
+          if ( diff < minDiff && diff < csUnitsPerPixel )
+          {
+            blues->zone[i].csFlatEdge = flatFamilyEdge;
+            minDiff                   = diff;
+
+            if ( diff == 0 )
+              break;
+          }
+        }
+      }
+    }
+
+    /* TODO: enforce separation of zones, including BlueFuzz */
+
+    /* Adjust BlueScale; similar to AdjustBlueScale() in coretype */
+    /* `bcsetup.c'.                                               */
+
+    if ( maxZoneHeight > 0 )
+    {
+      if ( blues->blueScale > FT_DivFix( cf2_intToFixed( 1 ),
+                                         maxZoneHeight ) )
+      {
+        /* clamp at maximum scale */
+        blues->blueScale = FT_DivFix( cf2_intToFixed( 1 ),
+                                      maxZoneHeight );
+      }
+
+      /*
+       * TODO: Revisit the bug fix for 613448.  The minimum scale
+       *       requirement catches a number of library fonts.  For
+       *       example, with default BlueScale (.039625) and 0.4 minimum,
+       *       the test below catches any font with maxZoneHeight < 10.1.
+       *       There are library fonts ranging from 2 to 10 that get
+       *       caught, including e.g., Eurostile LT Std Medium with
+       *       maxZoneHeight of 6.
+       *
+       */
+#if 0
+      if ( blueScale < .4 / maxZoneHeight )
+      {
+        tetraphilia_assert( 0 );
+        /* clamp at minimum scale, per bug 0613448 fix */
+        blueScale = .4 / maxZoneHeight;
+      }
+#endif
+
+    }
+
+    /*
+     * Suppress overshoot and boost blue zones at small sizes.  Boost
+     * amount varies linearly from 0.5 pixel near 0 to 0 pixel at
+     * blueScale cutoff.
+     * Note: This boost amount is different from the coretype heuristic.
+     *
+     */
+
+    if ( blues->scale < blues->blueScale )
+    {
+      blues->suppressOvershoot = TRUE;
+
+      /* Change rounding threshold for `dsFlatEdge'.                    */
+      /* Note: constant changed from 0.5 to 0.6 to avoid a problem with */
+      /*       10ppem Arial                                             */
+
+      blues->boost = FT_MulFix(
+                       cf2_floatToFixed( .6 ),
+                       ( cf2_intToFixed( 1 ) -
+                         FT_DivFix( blues->scale,
+                                    blues->blueScale ) ) );
+      if ( blues->boost > 0x7FFF )
+      {
+        /* boost must remain less than 0.5, or baseline could go negative */
+        blues->boost = 0x7FFF;
+      }
+    }
+
+    /* boost and darkening have similar effects; don't do both */
+    if ( font->stemDarkened )
+      blues->boost = 0;
+
+    /* set device space alignment for each zone;    */
+    /* apply boost amount before rounding flat edge */
+
+    for ( i = 0; i < blues->count; i++ )
+    {
+      if ( blues->zone[i].bottomZone )
+        blues->zone[i].dsFlatEdge = cf2_fixedRound(
+                                      FT_MulFix(
+                                        blues->zone[i].csFlatEdge,
+                                        blues->scale ) -
+                                      blues->boost );
+      else
+        blues->zone[i].dsFlatEdge = cf2_fixedRound(
+                                      FT_MulFix(
+                                        blues->zone[i].csFlatEdge,
+                                        blues->scale ) +
+                                      blues->boost );
+    }
+  }
+
+
+  /*
+   * Check whether `stemHint' is captured by one of the blue zones.
+   *
+   * Zero, one or both edges may be valid; only valid edges can be
+   * captured.  For compatibility with CoolType, search top and bottom
+   * zones in the same pass (see `BlueLock').  If a hint is captured,
+   * return true and position the edge(s) in one of 3 ways:
+   *
+   *  1) If `BlueScale' suppresses overshoot, position the captured edge
+   *     at the flat edge of the zone.
+   *  2) If overshoot is not suppressed and `BlueShift' requires
+   *     overshoot, position the captured edge a minimum of 1 device pixel
+   *     from the flat edge.
+   *  3) If overshoot is not suppressed or required, position the captured
+   *     edge at the nearest device pixel.
+   *
+   */
+  FT_LOCAL_DEF( FT_Bool )
+  cf2_blues_capture( const CF2_Blues  blues,
+                     CF2_Hint         bottomHintEdge,
+                     CF2_Hint         topHintEdge )
+  {
+    /* TODO: validate? */
+    CF2_Fixed  csFuzz = blues->blueFuzz;
+
+    /* new position of captured edge */
+    CF2_Fixed  dsNew;
+
+    /* amount that hint is moved when positioned */
+    CF2_Fixed  dsMove = 0;
+
+    FT_Bool   captured = FALSE;
+    CF2_UInt  i;
+
+
+    /* assert edge flags are consistent */
+    FT_ASSERT( !cf2_hint_isTop( bottomHintEdge ) &&
+               !cf2_hint_isBottom( topHintEdge ) );
+
+    /* TODO: search once without blue fuzz for compatibility with coretype? */
+    for ( i = 0; i < blues->count; i++ )
+    {
+      if ( blues->zone[i].bottomZone           &&
+           cf2_hint_isBottom( bottomHintEdge ) )
+      {
+        if ( ( blues->zone[i].csBottomEdge - csFuzz ) <=
+               bottomHintEdge->csCoord                   &&
+             bottomHintEdge->csCoord <=
+               ( blues->zone[i].csTopEdge + csFuzz )     )
+        {
+          /* bottom edge captured by bottom zone */
+
+          if ( blues->suppressOvershoot )
+            dsNew = blues->zone[i].dsFlatEdge;
+
+          else if ( ( blues->zone[i].csTopEdge - bottomHintEdge->csCoord ) >=
+                      blues->blueShift )
+          {
+            /* guarantee minimum of 1 pixel overshoot */
+            dsNew = FT_MIN(
+                      cf2_fixedRound( bottomHintEdge->dsCoord ),
+                      blues->zone[i].dsFlatEdge - cf2_intToFixed( 1 ) );
+          }
+
+          else
+          {
+            /* simply round captured edge */
+            dsNew = cf2_fixedRound( bottomHintEdge->dsCoord );
+          }
+
+          dsMove   = dsNew - bottomHintEdge->dsCoord;
+          captured = TRUE;
+
+          break;
+        }
+      }
+
+      if ( !blues->zone[i].bottomZone && cf2_hint_isTop( topHintEdge ) )
+      {
+        if ( ( blues->zone[i].csBottomEdge - csFuzz ) <=
+               topHintEdge->csCoord                      &&
+             topHintEdge->csCoord <=
+               ( blues->zone[i].csTopEdge + csFuzz )     )
+        {
+          /* top edge captured by top zone */
+
+          if ( blues->suppressOvershoot )
+            dsNew = blues->zone[i].dsFlatEdge;
+
+          else if ( ( topHintEdge->csCoord - blues->zone[i].csBottomEdge ) >=
+                      blues->blueShift )
+          {
+            /* guarantee minimum of 1 pixel overshoot */
+            dsNew = FT_MAX(
+                      cf2_fixedRound( topHintEdge->dsCoord ),
+                      blues->zone[i].dsFlatEdge + cf2_intToFixed( 1 ) );
+          }
+
+          else
+          {
+            /* simply round captured edge */
+            dsNew = cf2_fixedRound( topHintEdge->dsCoord );
+          }
+
+          dsMove   = dsNew - topHintEdge->dsCoord;
+          captured = TRUE;
+
+          break;
+        }
+      }
+    }
+
+    if ( captured )
+    {
+      /* move both edges and flag them `locked' */
+      if ( cf2_hint_isValid( bottomHintEdge ) )
+      {
+        bottomHintEdge->dsCoord += dsMove;
+        cf2_hint_lock( bottomHintEdge );
+      }
+
+      if ( cf2_hint_isValid( topHintEdge ) )
+      {
+        topHintEdge->dsCoord += dsMove;
+        cf2_hint_lock( topHintEdge );
+      }
+    }
+
+    return captured;
+  }
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2blues.h
@@ -1,0 +1,185 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2blues.h                                                             */
+/*                                                                         */
+/*    Adobe's code for handling Blue Zones (specification).                */
+/*                                                                         */
+/*  Copyright 2009-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+  /*
+   * A `CF2_Blues' object stores the blue zones (horizontal alignment
+   * zones) of a font.  These are specified in the CFF private dictionary
+   * by `BlueValues', `OtherBlues', `FamilyBlues', and `FamilyOtherBlues'.
+   * Each zone is defined by a top and bottom edge in character space.
+   * Further, each zone is either a top zone or a bottom zone, as recorded
+   * by `bottomZone'.
+   *
+   * The maximum number of `BlueValues' and `FamilyBlues' is 7 each.
+   * However, these are combined to produce a total of 7 zones.
+   * Similarly, the maximum number of `OtherBlues' and `FamilyOtherBlues'
+   * is 5 and these are combined to produce an additional 5 zones.
+   *
+   * Blue zones are used to `capture' hints and force them to a common
+   * alignment point.  This alignment is recorded in device space in
+   * `dsFlatEdge'.  Except for this value, a `CF2_Blues' object could be
+   * constructed independently of scaling.  Construction may occur once
+   * the matrix is known.  Other features implemented in the Capture
+   * method are overshoot suppression, overshoot enforcement, and Blue
+   * Boost.
+   *
+   * Capture is determined by `BlueValues' and `OtherBlues', but the
+   * alignment point may be adjusted to the scaled flat edge of
+   * `FamilyBlues' or `FamilyOtherBlues'.  No alignment is done to the
+   * curved edge of a zone.
+   *
+   */
+
+
+#ifndef __CF2BLUES_H__
+#define __CF2BLUES_H__
+
+
+#include "cf2glue.h"
+
+
+FT_BEGIN_HEADER
+
+
+  /*
+   * `CF2_Hint' is shared by `cf2hints.h' and
+   * `cf2blues.h', but `cf2blues.h' depends on
+   * `cf2hints.h', so define it here.  Note: The typedef is in
+   * `cf2glue.h'.
+   *
+   */
+  enum
+  {
+    CF2_GhostBottom = 0x1,  /* a single bottom edge           */
+    CF2_GhostTop    = 0x2,  /* a single top edge              */
+    CF2_PairBottom  = 0x4,  /* the bottom edge of a stem hint */
+    CF2_PairTop     = 0x8,  /* the top edge of a stem hint    */
+    CF2_Locked      = 0x10, /* this edge has been aligned     */
+                            /* by a blue zone                 */
+    CF2_Synthetic   = 0x20  /* this edge was synthesized      */
+  };
+
+
+  /*
+   * Default value for OS/2 typoAscender/Descender when their difference
+   * is not equal to `unitsPerEm'.  The default is based on -250 and 1100
+   * in `CF2_Blues', assuming 1000 units per em here.
+   *
+   */
+  enum
+  {
+    CF2_ICF_Top    = cf2_intToFixed(  880 ),
+    CF2_ICF_Bottom = cf2_intToFixed( -120 )
+  };
+
+
+  /*
+   * Constant used for hint adjustment and for synthetic em box hint
+   * placement.
+   */
+#define CF2_MIN_COUNTER  cf2_floatToFixed( 0.5 )
+
+
+  /* shared typedef is in cf2glue.h */
+  struct  CF2_HintRec_
+  {
+    CF2_UInt  flags;  /* attributes of the edge            */
+    size_t    index;  /* index in original stem hint array */
+                      /* (if not synthetic)                */
+    CF2_Fixed  csCoord;
+    CF2_Fixed  dsCoord;
+    CF2_Fixed  scale;
+  };
+
+
+  typedef struct  CF2_BlueRec_
+  {
+    CF2_Fixed  csBottomEdge;
+    CF2_Fixed  csTopEdge;
+    CF2_Fixed  csFlatEdge; /* may be from either local or Family zones */
+    CF2_Fixed  dsFlatEdge; /* top edge of bottom zone or bottom edge   */
+                           /* of top zone (rounded)                    */
+    FT_Bool  bottomZone;
+
+  } CF2_BlueRec;
+
+
+  /* max total blue zones is 12 */
+  enum
+  {
+    CF2_MAX_BLUES      = 7,
+    CF2_MAX_OTHERBLUES = 5
+  };
+
+
+  typedef struct  CF2_BluesRec_
+  {
+    CF2_Fixed  scale;
+    CF2_UInt   count;
+    FT_Bool    suppressOvershoot;
+    FT_Bool    doEmBoxHints;
+
+    CF2_Fixed  blueScale;
+    CF2_Fixed  blueShift;
+    CF2_Fixed  blueFuzz;
+
+    CF2_Fixed  boost;
+
+    CF2_HintRec  emBoxTopEdge;
+    CF2_HintRec  emBoxBottomEdge;
+
+    CF2_BlueRec  zone[CF2_MAX_BLUES + CF2_MAX_OTHERBLUES];
+
+  } CF2_BluesRec, *CF2_Blues;
+
+
+  FT_LOCAL( void )
+  cf2_blues_init( CF2_Blues  blues,
+                  CF2_Font   font );
+  FT_LOCAL( FT_Bool )
+  cf2_blues_capture( const CF2_Blues  blues,
+                     CF2_Hint         bottomHintEdge,
+                     CF2_Hint         topHintEdge );
+
+
+FT_END_HEADER
+
+
+#endif /* __CF2BLUES_H__ */
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2error.c
@@ -1,0 +1,52 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2error.c                                                             */
+/*                                                                         */
+/*    Adobe's code for error handling (body).                              */
+/*                                                                         */
+/*  Copyright 2006-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#include "cf2ft.h"
+#include "cf2error.h"
+
+
+  FT_LOCAL_DEF( void )
+  cf2_setError( FT_Error*  error,
+                FT_Error   value )
+  {
+    if ( error && *error == 0 )
+      *error = value;
+  }
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2error.h
@@ -1,0 +1,119 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2error.h                                                             */
+/*                                                                         */
+/*    Adobe's code for error handling (specification).                     */
+/*                                                                         */
+/*  Copyright 2006-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __CF2ERROR_H__
+#define __CF2ERROR_H__
+
+
+#include FT_MODULE_ERRORS_H
+
+#undef __FTERRORS_H__
+
+#undef  FT_ERR_PREFIX
+#define FT_ERR_PREFIX  CF2_Err_
+#define FT_ERR_BASE    FT_Mod_Err_CF2
+
+
+#include FT_ERRORS_H
+#include "cf2ft.h"
+
+
+FT_BEGIN_HEADER
+
+
+  /*
+   * A poor-man error facility.
+   *
+   * This code being written in vanilla C, doesn't have the luxury of a
+   * language-supported exception mechanism such as the one available in
+   * Java.  Instead, we are stuck with using error codes that must be
+   * carefully managed and preserved.  However, it is convenient for us to
+   * model our error mechanism on a Java-like exception mechanism.
+   * When we assign an error code we are thus `throwing' an error.
+   *
+   * The perservation of an error code is done by coding convention.
+   * Upon a function call if the error code is anything other than
+   * `FT_Err_Ok', which is guaranteed to be zero, we
+   * will return without altering that error.  This will allow the
+   * error to propogate and be handled at the appropriate location in
+   * the code.
+   *
+   * This allows a style of code where the error code is initialized
+   * up front and a block of calls are made with the error code only
+   * being checked after the block.  If a new error occurs, the original
+   * error will be preserved and a functional no-op should result in any
+   * subsequent function that has an initial error code not equal to
+   * `FT_Err_Ok'.
+   *
+   * Errors are encoded by calling the `FT_THROW' macro.  For example,
+   *
+   * {
+   *   FT_Error  e;
+   *
+   *
+   *   ...
+   *   e = FT_THROW( Out_Of_Memory );
+   * }
+   *
+   */
+
+
+  /* Set error code to a particular value. */
+  FT_LOCAL( void )
+  cf2_setError( FT_Error*  error,
+                FT_Error   value );
+
+
+  /*
+   * A macro that conditionally sets an error code.
+   *
+   * This macro will first check whether `error' is set;
+   * if not, it will set it to `e'.
+   *
+  */
+#define CF2_SET_ERROR( error, e )              \
+          cf2_setError( error, FT_THROW( e ) )
+
+
+FT_END_HEADER
+
+
+#endif /* __CF2ERROR_H__ */
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2fixed.h
@@ -1,0 +1,97 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2fixed.h                                                             */
+/*                                                                         */
+/*    Adobe's code for Fixed Point Mathematics (specification only).       */
+/*                                                                         */
+/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __CF2FIXED_H__
+#define __CF2FIXED_H__
+
+
+FT_BEGIN_HEADER
+
+
+  /* rasterizer integer and fixed point arithmetic must be 32-bit */
+
+#define   CF2_Fixed  CF2_F16Dot16
+  typedef FT_Int32   CF2_Frac;   /* 2.30 fixed point */
+
+
+#define CF2_FIXED_MAX      ( (CF2_Fixed)0x7FFFFFFFL )
+#define CF2_FIXED_MIN      ( (CF2_Fixed)0x80000000L )
+#define CF2_FIXED_ONE      0x10000L
+#define CF2_FIXED_EPSILON  0x0001
+
+#define cf2_intToFixed( i )                                 \
+          ( (i) << 16 )
+#define cf2_fixedToInt( x )                                 \
+          ( ( (x) + 0x8000 ) >> 16 )
+#define cf2_fixedRound( x )                                 \
+          ( (CF2_Fixed)( ( (x) + 0x8000 ) & 0xFFFF0000L ) )
+#define cf2_floatToFixed( f )                               \
+          ( (CF2_Fixed)( (f) * 65536.0 + 0.5 ) )
+#define cf2_fixedAbs( x )                                   \
+          ( (x) < 0 ? -(x) : (x) )
+#define cf2_fixedFloor( x )                                 \
+          ( (CF2_Fixed)((x) & 0xFFFF0000L ) )
+#define cf2_fixedFraction( x )                              \
+          ( (x) - cf2_fixedFloor( x ) )
+#define cf2_fracToFixed( x )                                \
+          ( ( (x) + 0x2000 ) >> 14 )
+#define cf2_intToFrac( i )                                  \
+          ( (i) << 30 )
+#define cf2_fixedToFrac( x )                                \
+          ( (x) << 14 )
+#define cf2_fixedTo26Dot6( x )                              \
+          ( ( (x) + 0x200 ) >> 10 )
+
+
+  /* signed numeric types */
+  typedef enum  CF2_NumberType_
+  {
+    CF2_NumberFixed,    /* 16.16 */
+    CF2_NumberFrac,     /*  2.30 */
+    CF2_NumberInt       /* 32.0  */
+
+  } CF2_NumberType;
+
+
+FT_END_HEADER
+
+
+#endif /*  __CF2FIXED_H__ */
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2font.c
@@ -1,0 +1,402 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2font.c                                                              */
+/*                                                                         */
+/*    Adobe's code for font instances (body).                              */
+/*                                                                         */
+/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#include "cf2ft.h"
+
+#include "cf2glue.h"
+#include "cf2font.h"
+#include "cf2error.h"
+#include "cf2intrp.h"
+
+
+  /* Compute a stem darkening amount in character space. */
+  static void
+  cf2_computeDarkening( CF2_Fixed   emRatio,
+                        CF2_Fixed   ppem,
+                        CF2_Fixed   stemWidth,
+                        CF2_Fixed*  darkenAmount,
+                        CF2_Fixed   boldenAmount,
+                        FT_Bool     stemDarkened )
+  {
+    /* Internal calculations are done in units per thousand for */
+    /* convenience.                                             */
+    CF2_Fixed  stemWidthPer1000, scaledStem;
+
+
+    *darkenAmount = 0;
+
+    if ( boldenAmount == 0 && !stemDarkened )
+      return;
+
+    /* protect against range problems and divide by zero */
+    if ( emRatio < cf2_floatToFixed( .01 ) )
+      return;
+
+    if ( stemDarkened )
+    {
+      /* convert from true character space to 1000 unit character space; */
+      /* add synthetic emboldening effect                                */
+
+      /* we have to assure that the computation of `scaledStem' */
+      /* and `stemWidthPer1000' don't overflow                  */
+
+      stemWidthPer1000 = FT_MulFix( stemWidth + boldenAmount, emRatio );
+
+      if ( emRatio > CF2_FIXED_ONE                          &&
+           stemWidthPer1000 <= ( stemWidth + boldenAmount ) )
+      {
+        stemWidthPer1000 = 0;                      /* to pacify compiler */
+        scaledStem       = cf2_intToFixed( 2333 );
+      }
+      else
+      {
+        scaledStem = FT_MulFix( stemWidthPer1000, ppem );
+
+        if ( ppem > CF2_FIXED_ONE           &&
+             scaledStem <= stemWidthPer1000 )
+          scaledStem = cf2_intToFixed( 2333 );
+      }
+
+      /*
+       * Total darkening amount is computed in 1000 unit character space
+       * using the modified 5 part curve as Avalon rasterizer.
+       * The darkening amount is smaller for thicker stems.
+       * It becomes zero when the stem is thicker than 2.333 pixels.
+       *
+       * In Avalon rasterizer,
+       *
+       *   darkenAmount = 0.5 pixels   if scaledStem <= 0.5 pixels,
+       *   darkenAmount = 0.333 pixels if 1 <= scaledStem <= 1.667 pixels,
+       *   darkenAmount = 0 pixel      if scaledStem >= 2.333 pixels,
+       *
+       * and piecewise linear in-between.
+       *
+       */
+      if ( scaledStem < cf2_intToFixed( 500 ) )
+        *darkenAmount = FT_DivFix( cf2_intToFixed( 400 ), ppem );
+
+      else if ( scaledStem < cf2_intToFixed( 1000 ) )
+        *darkenAmount = FT_DivFix( cf2_intToFixed( 525 ), ppem ) -
+                          FT_MulFix( stemWidthPer1000,
+                                     cf2_floatToFixed( .25 ) );
+
+      else if ( scaledStem < cf2_intToFixed( 1667 ) )
+        *darkenAmount = FT_DivFix( cf2_intToFixed( 275 ), ppem );
+
+      else if ( scaledStem < cf2_intToFixed( 2333 ) )
+        *darkenAmount = FT_DivFix( cf2_intToFixed( 963 ), ppem ) -
+                          FT_MulFix( stemWidthPer1000,
+                                     cf2_floatToFixed( .413 ) );
+
+      /* use half the amount on each side and convert back to true */
+      /* character space                                           */
+      *darkenAmount = FT_DivFix( *darkenAmount, 2 * emRatio );
+    }
+
+    /* add synthetic emboldening effect in character space */
+    *darkenAmount += boldenAmount / 2;
+  }
+
+
+  /* set up values for the current FontDict and matrix */
+
+  /* caller's transform is adjusted for subpixel positioning */
+  static void
+  cf2_font_setup( CF2_Font           font,
+                  const CF2_Matrix*  transform )
+  {
+    /* pointer to parsed font object, either PFR ParsedFont or FreeType */
+    /* Decoder                                                          */
+    CFF_Decoder*  decoder = font->decoder;
+
+    FT_Bool  needExtraSetup = FALSE;
+
+    /* character space units */
+    CF2_Fixed  boldenX = font->syntheticEmboldeningAmountX;
+    CF2_Fixed  boldenY = font->syntheticEmboldeningAmountY;
+
+    CF2_Fixed  ppem;
+
+
+    /* clear previous error */
+    font->error = FT_Err_Ok;
+
+    /* if a CID fontDict has changed, we need to recompute some cached */
+    /* data                                                            */
+    needExtraSetup = font->lastSubfont != cf2_getSubfont( decoder );
+
+    /* if ppem has changed, we need to recompute some cached data         */
+    /* note: because of CID font matrix concatenation, ppem and transform */
+    /*       do not necessarily track.                                    */
+    ppem = cf2_getPpemY( decoder );
+    if ( font->ppem != ppem )
+    {
+      font->ppem     = ppem;
+      needExtraSetup = TRUE;
+    }
+
+    /* copy hinted flag on each call */
+    font->hinted = font->renderingFlags & CF2_FlagsHinted;
+
+    /* determine if transform has changed;       */
+    /* include Fontmatrix but ignore translation */
+    if ( ft_memcmp( transform,
+                    &font->currentTransform,
+                    4 * sizeof ( CF2_Fixed ) ) != 0 )
+    {
+      /* save `key' information for `cache of one' matrix data; */
+      /* save client transform, without the translation         */
+      font->currentTransform    = *transform;
+      font->currentTransform.tx =
+      font->currentTransform.ty = cf2_intToFixed( 0 );
+
+      /* TODO: FreeType transform is simple scalar; for now, use identity */
+      /*       for outer                                                  */
+      font->innerTransform   = *transform;
+      font->outerTransform.a =
+      font->outerTransform.d = cf2_intToFixed( 1 );
+      font->outerTransform.b =
+      font->outerTransform.c = cf2_intToFixed( 0 );
+
+      needExtraSetup = TRUE;
+    }
+
+    /*
+     * font->darkened is set to true if there is a stem darkening request or
+     * the font is synthetic emboldened.
+     * font->darkened controls whether to adjust blue zones, winding order,
+     * and hinting.
+     *
+     */
+    if ( font->stemDarkened != ( font->renderingFlags & CF2_FlagsDarkened ) )
+    {
+      font->stemDarkened = font->renderingFlags & CF2_FlagsDarkened;
+
+      /* blue zones depend on darkened flag */
+      needExtraSetup = TRUE;
+    }
+
+    /* recompute variables that are dependent on transform or FontDict or */
+    /* darken flag                                                        */
+    if ( needExtraSetup )
+    {
+      /* StdVW is found in the private dictionary;                       */
+      /* recompute darkening amounts whenever private dictionary or      */
+      /* transform change                                                */
+      /* Note: a rendering flag turns darkening on or off, so we want to */
+      /*       store the `on' amounts;                                   */
+      /*       darkening amount is computed in character space           */
+      /* TODO: testing size-dependent darkening here;                    */
+      /*       what to do for rotations?                                 */
+
+      CF2_Fixed  emRatio;
+      CF2_Fixed  stdHW;
+      CF2_Int    unitsPerEm = font->unitsPerEm;
+
+
+      if ( unitsPerEm == 0 )
+        unitsPerEm = 1000;
+
+      ppem = FT_MAX( cf2_intToFixed( 4 ),
+                     font->ppem ); /* use minimum ppem of 4 */
+
+#if 0
+      /* since vstem is measured in the x-direction, we use the `a' member */
+      /* of the fontMatrix                                                 */
+      emRatio = cf2_fixedFracMul( cf2_intToFixed( 1000 ), fontMatrix->a );
+#endif
+
+      /* Freetype does not preserve the fontMatrix when parsing; use */
+      /* unitsPerEm instead.                                         */
+      /* TODO: check precision of this                               */
+      emRatio     = cf2_intToFixed( 1000 ) / unitsPerEm;
+      font->stdVW = cf2_getStdVW( decoder );
+
+      if ( font->stdVW <= 0 )
+        font->stdVW = FT_DivFix( cf2_intToFixed( 75 ), emRatio );
+
+      if ( boldenX > 0 )
+      {
+        /* Ensure that boldenX is at least 1 pixel for synthetic bold font */
+        /* (similar to what Avalon does)                                   */
+        boldenX = FT_MAX( boldenX,
+                          FT_DivFix( cf2_intToFixed( unitsPerEm ), ppem ) );
+
+        /* Synthetic emboldening adds at least 1 pixel to darkenX, while */
+        /* stem darkening adds at most half pixel.  Since the purpose of */
+        /* stem darkening (readability at small sizes) is met with       */
+        /* synthetic emboldening, no need to add stem darkening for a    */
+        /* synthetic bold font.                                          */
+        cf2_computeDarkening( emRatio,
+                              ppem,
+                              font->stdVW,
+                              &font->darkenX,
+                              boldenX,
+                              FALSE );
+      }
+      else
+        cf2_computeDarkening( emRatio,
+                              ppem,
+                              font->stdVW,
+                              &font->darkenX,
+                              0,
+                              font->stemDarkened );
+
+#if 0
+      /* since hstem is measured in the y-direction, we use the `d' member */
+      /* of the fontMatrix                                                 */
+      /* TODO: use the same units per em as above; check this              */
+      emRatio = cf2_fixedFracMul( cf2_intToFixed( 1000 ), fontMatrix->d );
+#endif
+
+      /* set the default stem width, because it must be the same for all */
+      /* family members;                                                 */
+      /* choose a constant for StdHW that depends on font contrast       */
+      stdHW = cf2_getStdHW( decoder );
+
+      if ( stdHW > 0 && font->stdVW > 2 * stdHW )
+        font->stdHW = FT_DivFix( cf2_intToFixed( 75 ), emRatio );
+      else
+      {
+        /* low contrast font gets less hstem darkening */
+        font->stdHW = FT_DivFix( cf2_intToFixed( 110 ), emRatio );
+      }
+
+      cf2_computeDarkening( emRatio,
+                            ppem,
+                            font->stdHW,
+                            &font->darkenY,
+                            boldenY,
+                            font->stemDarkened );
+
+      if ( font->darkenX != 0 || font->darkenY != 0 )
+        font->darkened = TRUE;
+      else
+        font->darkened = FALSE;
+
+      font->reverseWinding = FALSE; /* initial expectation is CCW */
+
+      /* compute blue zones for this instance */
+      cf2_blues_init( &font->blues, font );
+    }
+  }
+
+
+  /* equivalent to AdobeGetOutline */
+  FT_LOCAL_DEF( FT_Error )
+  cf2_getGlyphWidth( CF2_Font           font,
+                     CF2_Buffer         charstring,
+                     const CF2_Matrix*  transform,
+                     CF2_F16Dot16*      glyphWidth )
+  {
+    FT_Error  lastError = FT_Err_Ok;
+
+    FT_Vector  translation;
+
+#if 0
+    FT_Vector  advancePoint;
+#endif
+
+    CF2_Fixed  advWidth;
+    FT_Bool    needWinding;
+
+
+    /* Note: use both integer and fraction for outlines.  This allows bbox */
+    /*       to come out directly.                                         */
+
+    translation.x = transform->tx;
+    translation.y = transform->ty;
+
+    /* set up values based on transform */
+    cf2_font_setup( font, transform );
+    if ( font->error )
+      goto exit;                      /* setup encountered an error */
+
+    /* reset darken direction */
+    font->reverseWinding = FALSE;
+
+    /* winding order only affects darkening */
+    needWinding = font->darkened;
+
+    while ( 1 )
+    {
+      /* reset output buffer */
+      cf2_outline_reset( &font->outline );
+
+      /* build the outline, passing the full translation */
+      cf2_interpT2CharString( font,
+                              charstring,
+                              (CF2_OutlineCallbacks)&font->outline,
+                              &translation,
+                              FALSE,
+                              0,
+                              0,
+                              &advWidth );
+
+      if ( font->error )
+        goto exit;
+
+      if ( !needWinding )
+        break;
+
+      /* check winding order */
+      if ( font->outline.root.windingMomentum >= 0 ) /* CFF is CCW */
+        break;
+
+      /* invert darkening and render again                            */
+      /* TODO: this should be a parameter to getOutline-computeOffset */
+      font->reverseWinding = TRUE;
+
+      needWinding = FALSE;    /* exit after next iteration */
+    }
+
+    /* finish storing client outline */
+    cf2_outline_close( &font->outline );
+
+    /* FreeType just wants the advance width; there is no translation */
+    *glyphWidth = advWidth;
+
+    /* free resources and collect errors from objects we've used */
+  exit:
+    cf2_setError( &font->error, lastError );
+
+    return font->error;
+  }
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2font.h
@@ -1,0 +1,114 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2font.h                                                              */
+/*                                                                         */
+/*    Adobe's code for font instances (specification).                     */
+/*                                                                         */
+/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __CF2FONT_H__
+#define __CF2FONT_H__
+
+
+#include "cf2ft.h"
+#include "cf2blues.h"
+
+
+FT_BEGIN_HEADER
+
+
+#define CF2_OPERAND_STACK_SIZE  48
+#define CF2_MAX_SUBR            10 /* maximum subroutine nesting */
+
+
+  /* typedef is in `cf2glue.h' */
+  struct  CF2_FontRec_
+  {
+    FT_Memory  memory;
+    FT_Error   error;     /* shared error for this instance */
+
+    CF2_RenderingFlags  renderingFlags;
+
+    /* variables that depend on Transform:  */
+    /* the following have zero translation; */
+    /* inner * outer = font * original      */
+
+    CF2_Matrix  currentTransform;  /* original client matrix           */
+    CF2_Matrix  innerTransform;    /* for hinting; erect, scaled       */
+    CF2_Matrix  outerTransform;    /* post hinting; includes rotations */
+    CF2_Fixed   ppem;              /* transform-dependent              */
+
+    CF2_Int  unitsPerEm;
+
+    CF2_Fixed  syntheticEmboldeningAmountX;   /* character space units */
+    CF2_Fixed  syntheticEmboldeningAmountY;   /* character space units */
+
+    /* FreeType related members */
+    CF2_OutlineRec  outline;       /* freetype glyph outline functions */
+    CFF_Decoder*    decoder;
+    CFF_SubFont     lastSubfont;              /* FreeType parsed data; */
+                                              /* top font or subfont   */
+
+    /* these flags can vary from one call to the next */
+    FT_Bool  hinted;
+    FT_Bool  darkened;       /* true if stemDarkened or synthetic bold */
+                             /* i.e. darkenX != 0 || darkenY != 0      */
+    FT_Bool  stemDarkened;
+
+    /* variables that depend on both FontDict and Transform */
+    CF2_Fixed  stdVW;     /* in character space; depends on dict entry */
+    CF2_Fixed  stdHW;     /* in character space; depends on dict entry */
+    CF2_Fixed  darkenX;                    /* character space units    */
+    CF2_Fixed  darkenY;                    /* depends on transform     */
+                                           /* and private dict (StdVW) */
+    FT_Bool  reverseWinding;               /* darken assuming          */
+                                           /* counterclockwise winding */
+
+    CF2_BluesRec  blues;                         /* computed zone data */
+  };
+
+
+  FT_LOCAL( FT_Error )
+  cf2_getGlyphWidth( CF2_Font           font,
+                     CF2_Buffer         charstring,
+                     const CF2_Matrix*  transform,
+                     CF2_F16Dot16*      glyphWidth );
+
+
+FT_END_HEADER
+
+
+#endif /* __CF2FONT_H__ */
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2ft.c
@@ -1,0 +1,637 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2ft.c                                                                */
+/*                                                                         */
+/*    FreeType Glue Component to Adobe's Interpreter (body).               */
+/*                                                                         */
+/*  Copyright 2013 Adobe Systems Incorporated.                             */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#include "cf2ft.h"
+#include FT_INTERNAL_DEBUG_H
+
+#include "cf2font.h"
+#include "cf2error.h"
+
+
+#define CF2_MAX_SIZE  cf2_intToFixed( 2000 )    /* max ppem */
+
+
+  /*
+   * This check should avoid most internal overflow cases.  Clients should
+   * generally respond to `Glyph_Too_Big' by getting a glyph outline
+   * at EM size, scaling it and filling it as a graphics operation.
+   *
+   */
+  static FT_Error
+  cf2_checkTransform( const CF2_Matrix*  transform,
+                      CF2_Int            unitsPerEm )
+  {
+    CF2_Fixed  maxScale;
+
+
+    FT_ASSERT( unitsPerEm > 0 );
+
+    FT_ASSERT( transform->a > 0 && transform->d > 0 );
+    FT_ASSERT( transform->b == 0 && transform->c == 0 );
+    FT_ASSERT( transform->tx == 0 && transform->ty == 0 );
+
+    if ( unitsPerEm > 0x7FFF )
+      return FT_THROW( Glyph_Too_Big );
+
+    maxScale = FT_DivFix( CF2_MAX_SIZE, cf2_intToFixed( unitsPerEm ) );
+
+    if ( transform->a > maxScale || transform->d > maxScale )
+      return FT_THROW( Glyph_Too_Big );
+
+    return FT_Err_Ok;
+  }
+
+
+  static void
+  cf2_setGlyphWidth( CF2_Outline  outline,
+                     CF2_Fixed    width )
+  {
+    CFF_Decoder*  decoder = outline->decoder;
+
+
+    FT_ASSERT( decoder );
+
+    decoder->glyph_width = cf2_fixedToInt( width );
+  }
+
+
+  /* Clean up font instance. */
+  static void
+  cf2_free_instance( void*  ptr )
+  {
+    CF2_Font  font = (CF2_Font)ptr;
+
+
+    if ( font )
+    {
+      FT_Memory  memory = font->memory;
+
+
+      (void)memory;
+    }
+  }
+
+
+  /********************************************/
+  /*                                          */
+  /* functions for handling client outline;   */
+  /* FreeType uses coordinates in 26.6 format */
+  /*                                          */
+  /********************************************/
+
+  static void
+  cf2_builder_moveTo( CF2_OutlineCallbacks      callbacks,
+                      const CF2_CallbackParams  params )
+  {
+    /* downcast the object pointer */
+    CF2_Outline   outline = (CF2_Outline)callbacks;
+    CFF_Builder*  builder;
+
+
+    FT_ASSERT( outline && outline->decoder );
+    FT_ASSERT( params->op == CF2_PathOpMoveTo );
+
+    builder = &outline->decoder->builder;
+
+    /* note: two successive moves simply close the contour twice */
+    cff_builder_close_contour( builder );
+    builder->path_begun = 0;
+  }
+
+
+  static void
+  cf2_builder_lineTo( CF2_OutlineCallbacks      callbacks,
+                      const CF2_CallbackParams  params )
+  {
+    /* downcast the object pointer */
+    CF2_Outline   outline = (CF2_Outline)callbacks;
+    CFF_Builder*  builder;
+
+
+    FT_ASSERT( outline && outline->decoder );
+    FT_ASSERT( params->op == CF2_PathOpLineTo );
+
+    builder = &outline->decoder->builder;
+
+    if ( !builder->path_begun )
+    {
+      /* record the move before the line; also check points and set */
+      /* `path_begun'                                               */
+      cff_builder_start_point( builder,
+                               params->pt0.x,
+                               params->pt0.y );
+    }
+
+    /* `cff_builder_add_point1' includes a check_points call for one point */
+    cff_builder_add_point1( builder,
+                            params->pt1.x,
+                            params->pt1.y );
+  }
+
+
+  static void
+  cf2_builder_cubeTo( CF2_OutlineCallbacks      callbacks,
+                      const CF2_CallbackParams  params )
+  {
+    /* downcast the object pointer */
+    CF2_Outline   outline = (CF2_Outline)callbacks;
+    CFF_Builder*  builder;
+
+
+    FT_ASSERT( outline && outline->decoder );
+    FT_ASSERT( params->op == CF2_PathOpCubeTo );
+
+    builder = &outline->decoder->builder;
+
+    if ( !builder->path_begun )
+    {
+      /* record the move before the line; also check points and set */
+      /* `path_begun'                                               */
+      cff_builder_start_point( builder,
+                               params->pt0.x,
+                               params->pt0.y );
+    }
+
+    /* prepare room for 3 points: 2 off-curve, 1 on-curve */
+    cff_check_points( builder, 3 );
+
+    cff_builder_add_point( builder,
+                           params->pt1.x,
+                           params->pt1.y, 0 );
+    cff_builder_add_point( builder,
+                           params->pt2.x,
+                           params->pt2.y, 0 );
+    cff_builder_add_point( builder,
+                           params->pt3.x,
+                           params->pt3.y, 1 );
+  }
+
+
+  static void
+  cf2_outline_init( CF2_Outline  outline,
+                    FT_Memory    memory,
+                    FT_Error*    error )
+  {
+    FT_MEM_ZERO( outline, sizeof ( CF2_OutlineRec ) );
+
+    outline->root.memory = memory;
+    outline->root.error  = error;
+
+    outline->root.moveTo = cf2_builder_moveTo;
+    outline->root.lineTo = cf2_builder_lineTo;
+    outline->root.cubeTo = cf2_builder_cubeTo;
+  }
+
+
+  /* get scaling and hint flag from GlyphSlot */
+  static void
+  cf2_getScaleAndHintFlag( CFF_Decoder*  decoder,
+                           CF2_Fixed*    x_scale,
+                           CF2_Fixed*    y_scale,
+                           FT_Bool*      hinted,
+                           FT_Bool*      scaled )
+  {
+    FT_ASSERT( decoder && decoder->builder.glyph );
+
+    /* note: FreeType scale includes a factor of 64 */
+    *hinted = decoder->builder.glyph->hint;
+    *scaled = decoder->builder.glyph->scaled;
+
+    if ( *hinted )
+    {
+      *x_scale = FT_DivFix( decoder->builder.glyph->x_scale,
+                            cf2_intToFixed( 64 ) );
+      *y_scale = FT_DivFix( decoder->builder.glyph->y_scale,
+                            cf2_intToFixed( 64 ) );
+    }
+    else
+    {
+      /* for unhinted outlines, `cff_slot_load' does the scaling, */
+      /* thus render at `unity' scale                             */
+
+      *x_scale = 0x0400;   /* 1/64 as 16.16 */
+      *y_scale = 0x0400;
+    }
+  }
+
+
+  /* get units per em from `FT_Face' */
+  /* TODO: should handle font matrix concatenation? */
+  static FT_UShort
+  cf2_getUnitsPerEm( CFF_Decoder*  decoder )
+  {
+    FT_ASSERT( decoder && decoder->builder.face );
+    FT_ASSERT( decoder->builder.face->root.units_per_EM );
+
+    return decoder->builder.face->root.units_per_EM;
+  }
+
+
+  /* Main entry point: Render one glyph. */
+  FT_LOCAL_DEF( FT_Error )
+  cf2_decoder_parse_charstrings( CFF_Decoder*  decoder,
+                                 FT_Byte*      charstring_base,
+                                 FT_ULong      charstring_len )
+  {
+    FT_Memory  memory;
+    FT_Error   error = FT_Err_Ok;
+    CF2_Font   font;
+
+
+    FT_ASSERT( decoder && decoder->cff );
+
+    memory = decoder->builder.memory;
+
+    /* CF2 data is saved here across glyphs */
+    font = (CF2_Font)decoder->cff->cf2_instance.data;
+
+    /* on first glyph, allocate instance structure */
+    if ( decoder->cff->cf2_instance.data == NULL )
+    {
+      decoder->cff->cf2_instance.finalizer =
+        (FT_Generic_Finalizer)cf2_free_instance;
+
+      if ( FT_ALLOC( decoder->cff->cf2_instance.data,
+                     sizeof ( CF2_FontRec ) ) )
+        return FT_THROW( Out_Of_Memory );
+
+      font = (CF2_Font)decoder->cff->cf2_instance.data;
+
+      font->memory = memory;
+
+      /* initialize a client outline, to be shared by each glyph rendered */
+      cf2_outline_init( &font->outline, font->memory, &font->error );
+    }
+
+    /* save decoder; it is a stack variable and will be different on each */
+    /* call                                                               */
+    font->decoder         = decoder;
+    font->outline.decoder = decoder;
+
+    {
+      /* build parameters for Adobe engine */
+
+      CFF_Builder*  builder = &decoder->builder;
+      CFF_Driver    driver  = (CFF_Driver)FT_FACE_DRIVER( builder->face );
+
+      /* local error */
+      FT_Error       error2 = FT_Err_Ok;
+      CF2_BufferRec  buf;
+      CF2_Matrix     transform;
+      CF2_F16Dot16   glyphWidth;
+
+      FT_Bool  hinted;
+      FT_Bool  scaled;
+
+
+      /* FreeType has already looked up the GID; convert to         */
+      /* `RegionBuffer', assuming that the input has been validated */
+      FT_ASSERT( charstring_base + charstring_len >= charstring_base );
+
+      FT_ZERO( &buf );
+      buf.start =
+      buf.ptr   = charstring_base;
+      buf.end   = charstring_base + charstring_len;
+
+      FT_ZERO( &transform );
+
+      cf2_getScaleAndHintFlag( decoder,
+                               &transform.a,
+                               &transform.d,
+                               &hinted,
+                               &scaled );
+
+      font->renderingFlags = 0;
+      if ( hinted )
+        font->renderingFlags |= CF2_FlagsHinted;
+      if ( scaled && !driver->no_stem_darkening )
+        font->renderingFlags |= CF2_FlagsDarkened;
+
+      /* now get an outline for this glyph;      */
+      /* also get units per em to validate scale */
+      font->unitsPerEm = (CF2_Int)cf2_getUnitsPerEm( decoder );
+
+      error2 = cf2_checkTransform( &transform, font->unitsPerEm );
+      if ( error2 )
+        return error2;
+
+      error2 = cf2_getGlyphWidth( font, &buf, &transform, &glyphWidth );
+      if ( error2 )
+        return FT_ERR( Invalid_File_Format );
+
+      cf2_setGlyphWidth( &font->outline, glyphWidth );
+
+      return FT_Err_Ok;
+    }
+  }
+
+
+  /* get pointer to current FreeType subfont (based on current glyphID) */
+  FT_LOCAL_DEF( CFF_SubFont )
+  cf2_getSubfont( CFF_Decoder*  decoder )
+  {
+    FT_ASSERT( decoder && decoder->current_subfont );
+
+    return decoder->current_subfont;
+  }
+
+
+  /* get `y_ppem' from `CFF_Size' */
+  FT_LOCAL_DEF( CF2_Fixed )
+  cf2_getPpemY( CFF_Decoder*  decoder )
+  {
+    FT_ASSERT( decoder                          &&
+               decoder->builder.face            &&
+               decoder->builder.face->root.size );
+    FT_ASSERT( decoder->builder.face->root.size->metrics.y_ppem );
+
+    return cf2_intToFixed(
+             decoder->builder.face->root.size->metrics.y_ppem );
+  }
+
+
+  /* get standard stem widths for the current subfont; */
+  /* FreeType stores these as integer font units       */
+  /* (note: variable names seem swapped)               */
+  FT_LOCAL_DEF( CF2_Fixed )
+  cf2_getStdVW( CFF_Decoder*  decoder )
+  {
+    FT_ASSERT( decoder && decoder->current_subfont );
+
+    return cf2_intToFixed(
+             decoder->current_subfont->private_dict.standard_height );
+  }
+
+
+  FT_LOCAL_DEF( CF2_Fixed )
+  cf2_getStdHW( CFF_Decoder*  decoder )
+  {
+    FT_ASSERT( decoder && decoder->current_subfont );
+
+    return cf2_intToFixed(
+             decoder->current_subfont->private_dict.standard_width );
+  }
+
+
+  /* note: FreeType stores 1000 times the actual value for `BlueScale' */
+  FT_LOCAL_DEF( void )
+  cf2_getBlueMetrics( CFF_Decoder*  decoder,
+                      CF2_Fixed*    blueScale,
+                      CF2_Fixed*    blueShift,
+                      CF2_Fixed*    blueFuzz )
+  {
+    FT_ASSERT( decoder && decoder->current_subfont );
+
+    *blueScale = FT_DivFix(
+                   decoder->current_subfont->private_dict.blue_scale,
+                   cf2_intToFixed( 1000 ) );
+    *blueShift = cf2_intToFixed(
+                   decoder->current_subfont->private_dict.blue_shift );
+    *blueFuzz  = cf2_intToFixed(
+                   decoder->current_subfont->private_dict.blue_fuzz );
+  }
+
+
+  /* get blue values counts and arrays; the FreeType parser has validated */
+  /* the counts and verified that each is an even number                  */
+  FT_LOCAL_DEF( void )
+  cf2_getBlueValues( CFF_Decoder*  decoder,
+                     size_t*       count,
+                     CF2_Fixed*   *data )
+  {
+    FT_ASSERT( decoder && decoder->current_subfont );
+
+    *count = decoder->current_subfont->private_dict.num_blue_values;
+    *data  = (CF2_Fixed*)
+               &decoder->current_subfont->private_dict.blue_values;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_getOtherBlues( CFF_Decoder*  decoder,
+                     size_t*       count,
+                     CF2_Fixed*   *data )
+  {
+    FT_ASSERT( decoder && decoder->current_subfont );
+
+    *count = decoder->current_subfont->private_dict.num_other_blues;
+    *data  = (CF2_Fixed*)
+               &decoder->current_subfont->private_dict.other_blues;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_getFamilyBlues( CFF_Decoder*  decoder,
+                      size_t*       count,
+                      CF2_Fixed*   *data )
+  {
+    FT_ASSERT( decoder && decoder->current_subfont );
+
+    *count = decoder->current_subfont->private_dict.num_family_blues;
+    *data  = (CF2_Fixed*)
+               &decoder->current_subfont->private_dict.family_blues;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_getFamilyOtherBlues( CFF_Decoder*  decoder,
+                           size_t*       count,
+                           CF2_Fixed*   *data )
+  {
+    FT_ASSERT( decoder && decoder->current_subfont );
+
+    *count = decoder->current_subfont->private_dict.num_family_other_blues;
+    *data  = (CF2_Fixed*)
+               &decoder->current_subfont->private_dict.family_other_blues;
+  }
+
+
+  FT_LOCAL_DEF( CF2_Int )
+  cf2_getLanguageGroup( CFF_Decoder*  decoder )
+  {
+    FT_ASSERT( decoder && decoder->current_subfont );
+
+    return decoder->current_subfont->private_dict.language_group;
+  }
+
+
+  /* convert unbiased subroutine index to `CF2_Buffer' and */
+  /* return 0 on success                                   */
+  FT_LOCAL_DEF( CF2_Int )
+  cf2_initGlobalRegionBuffer( CFF_Decoder*  decoder,
+                              CF2_UInt      idx,
+                              CF2_Buffer    buf )
+  {
+    FT_ASSERT( decoder && decoder->globals );
+
+    FT_ZERO( buf );
+
+    idx += decoder->globals_bias;
+    if ( idx >= decoder->num_globals )
+      return TRUE;     /* error */
+
+    buf->start =
+    buf->ptr   = decoder->globals[idx];
+    buf->end   = decoder->globals[idx + 1];
+
+    return FALSE;      /* success */
+  }
+
+
+  /* convert AdobeStandardEncoding code to CF2_Buffer; */
+  /* used for seac component                           */
+  FT_LOCAL_DEF( FT_Error )
+  cf2_getSeacComponent( CFF_Decoder*  decoder,
+                        CF2_UInt      code,
+                        CF2_Buffer    buf )
+  {
+    CF2_Int   gid;
+    FT_Byte*  charstring;
+    FT_ULong  len;
+    FT_Error  error;
+
+
+    FT_ASSERT( decoder );
+
+    FT_ZERO( buf );
+
+    gid = cff_lookup_glyph_by_stdcharcode( decoder->cff, code );
+    if ( gid < 0 )
+      return FT_THROW( Invalid_Glyph_Format );
+
+    error = cff_get_glyph_data( decoder->builder.face,
+                                gid,
+                                &charstring,
+                                &len );
+    /* TODO: for now, just pass the FreeType error through */
+    if ( error )
+      return error;
+
+    /* assume input has been validated */
+    FT_ASSERT( charstring + len >= charstring );
+
+    buf->start = charstring;
+    buf->end   = charstring + len;
+    buf->ptr   = buf->start;
+
+    return FT_Err_Ok;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_freeSeacComponent( CFF_Decoder*  decoder,
+                         CF2_Buffer    buf )
+  {
+    FT_ASSERT( decoder );
+
+    cff_free_glyph_data( decoder->builder.face,
+                         (FT_Byte**)&buf->start,
+                         buf->end - buf->start );
+  }
+
+
+  FT_LOCAL_DEF( CF2_Int )
+  cf2_initLocalRegionBuffer( CFF_Decoder*  decoder,
+                             CF2_UInt      idx,
+                             CF2_Buffer    buf )
+  {
+    FT_ASSERT( decoder && decoder->locals );
+
+    FT_ZERO( buf );
+
+    idx += decoder->locals_bias;
+    if ( idx >= decoder->num_locals )
+      return TRUE;     /* error */
+
+    buf->start =
+    buf->ptr   = decoder->locals[idx];
+    buf->end   = decoder->locals[idx + 1];
+
+    return FALSE;      /* success */
+  }
+
+
+  FT_LOCAL_DEF( CF2_Fixed )
+  cf2_getDefaultWidthX( CFF_Decoder*  decoder )
+  {
+    FT_ASSERT( decoder && decoder->current_subfont );
+
+    return cf2_intToFixed(
+             decoder->current_subfont->private_dict.default_width );
+  }
+
+
+  FT_LOCAL_DEF( CF2_Fixed )
+  cf2_getNominalWidthX( CFF_Decoder*  decoder )
+  {
+    FT_ASSERT( decoder && decoder->current_subfont );
+
+    return cf2_intToFixed(
+             decoder->current_subfont->private_dict.nominal_width );
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_outline_reset( CF2_Outline  outline )
+  {
+    CFF_Decoder*  decoder = outline->decoder;
+
+
+    FT_ASSERT( decoder );
+
+    outline->root.windingMomentum = 0;
+
+    FT_GlyphLoader_Rewind( decoder->builder.loader );
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_outline_close( CF2_Outline  outline )
+  {
+    CFF_Decoder*  decoder = outline->decoder;
+
+
+    FT_ASSERT( decoder );
+
+    cff_builder_close_contour( &decoder->builder );
+
+    FT_GlyphLoader_Add( decoder->builder.loader );
+  }
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2ft.h
@@ -1,0 +1,147 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2ft.h                                                                */
+/*                                                                         */
+/*    FreeType Glue Component to Adobe's Interpreter (specification).      */
+/*                                                                         */
+/*  Copyright 2013 Adobe Systems Incorporated.                             */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __CF2FT_H__
+#define __CF2FT_H__
+
+
+#include "cf2types.h"
+
+
+  /* TODO: disable asserts for now */
+#define CF2_NDEBUG
+
+
+#include FT_SYSTEM_H
+
+#include "cf2glue.h"
+#include "cffgload.h"    /* for CFF_Decoder */
+
+
+FT_BEGIN_HEADER
+
+
+  FT_LOCAL( FT_Error )
+  cf2_decoder_parse_charstrings( CFF_Decoder*  decoder,
+                                 FT_Byte*      charstring_base,
+                                 FT_ULong      charstring_len );
+
+  FT_LOCAL( CFF_SubFont )
+  cf2_getSubfont( CFF_Decoder*  decoder );
+
+
+  FT_LOCAL( CF2_Fixed )
+  cf2_getPpemY( CFF_Decoder*  decoder );
+  FT_LOCAL( CF2_Fixed )
+  cf2_getStdVW( CFF_Decoder*  decoder );
+  FT_LOCAL( CF2_Fixed )
+  cf2_getStdHW( CFF_Decoder*  decoder );
+
+  FT_LOCAL( void )
+  cf2_getBlueMetrics( CFF_Decoder*  decoder,
+                      CF2_Fixed*    blueScale,
+                      CF2_Fixed*    blueShift,
+                      CF2_Fixed*    blueFuzz );
+  FT_LOCAL( void )
+  cf2_getBlueValues( CFF_Decoder*  decoder,
+                     size_t*       count,
+                     CF2_Fixed*   *data );
+  FT_LOCAL( void )
+  cf2_getOtherBlues( CFF_Decoder*  decoder,
+                     size_t*       count,
+                     CF2_Fixed*   *data );
+  FT_LOCAL( void )
+  cf2_getFamilyBlues( CFF_Decoder*  decoder,
+                      size_t*       count,
+                      CF2_Fixed*   *data );
+  FT_LOCAL( void )
+  cf2_getFamilyOtherBlues( CFF_Decoder*  decoder,
+                           size_t*       count,
+                           CF2_Fixed*   *data );
+
+  FT_LOCAL( CF2_Int )
+  cf2_getLanguageGroup( CFF_Decoder*  decoder );
+
+  FT_LOCAL( CF2_Int )
+  cf2_initGlobalRegionBuffer( CFF_Decoder*  decoder,
+                              CF2_UInt      idx,
+                              CF2_Buffer    buf );
+  FT_LOCAL( FT_Error )
+  cf2_getSeacComponent( CFF_Decoder*  decoder,
+                        CF2_UInt      code,
+                        CF2_Buffer    buf );
+  FT_LOCAL( void )
+  cf2_freeSeacComponent( CFF_Decoder*  decoder,
+                         CF2_Buffer    buf );
+  FT_LOCAL( CF2_Int )
+  cf2_initLocalRegionBuffer( CFF_Decoder*  decoder,
+                             CF2_UInt      idx,
+                             CF2_Buffer    buf );
+
+  FT_LOCAL( CF2_Fixed )
+  cf2_getDefaultWidthX( CFF_Decoder*  decoder );
+  FT_LOCAL( CF2_Fixed )
+  cf2_getNominalWidthX( CFF_Decoder*  decoder );
+
+
+  /*
+   * FreeType client outline
+   *
+   * process output from the charstring interpreter
+   */
+  typedef struct  CF2_OutlineRec_
+  {
+    CF2_OutlineCallbacksRec  root;        /* base class must be first */
+    CFF_Decoder*             decoder;
+
+  } CF2_OutlineRec, *CF2_Outline;
+
+
+  FT_LOCAL( void )
+  cf2_outline_reset( CF2_Outline  outline );
+  FT_LOCAL( void )
+  cf2_outline_close( CF2_Outline  outline );
+
+
+FT_END_HEADER
+
+
+#endif /* __CF2FT_H__ */
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2glue.h
@@ -1,0 +1,144 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2glue.h                                                              */
+/*                                                                         */
+/*    Adobe's code for shared stuff (specification only).                  */
+/*                                                                         */
+/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __CF2GLUE_H__
+#define __CF2GLUE_H__
+
+
+/* common includes for other modules */
+#include "cf2error.h"
+#include "cf2fixed.h"
+#include "cf2arrst.h"
+#include "cf2read.h"
+
+
+FT_BEGIN_HEADER
+
+
+  /* rendering parameters */
+
+  /* apply hints to rendered glyphs */
+#define CF2_FlagsHinted    1
+  /* for testing */
+#define CF2_FlagsDarkened  2
+
+  /* type for holding the flags */
+  typedef CF2_Int  CF2_RenderingFlags;
+
+
+  /* elements of a glyph outline */
+  typedef enum  CF2_PathOp_
+  {
+    CF2_PathOpMoveTo = 1,     /* change the current point */
+    CF2_PathOpLineTo = 2,     /* line                     */
+    CF2_PathOpQuadTo = 3,     /* quadratic curve          */
+    CF2_PathOpCubeTo = 4      /* cubic curve              */
+
+  } CF2_PathOp;
+
+
+  /* a matrix of fixed point values */
+  typedef struct  CF2_Matrix_
+  {
+    CF2_F16Dot16  a;
+    CF2_F16Dot16  b;
+    CF2_F16Dot16  c;
+    CF2_F16Dot16  d;
+    CF2_F16Dot16  tx;
+    CF2_F16Dot16  ty;
+
+  } CF2_Matrix;
+
+
+  /* these typedefs are needed by more than one header file */
+  /* and gcc compiler doesn't allow redefinition            */
+  typedef struct CF2_FontRec_  CF2_FontRec, *CF2_Font;
+  typedef struct CF2_HintRec_  CF2_HintRec, *CF2_Hint;
+
+
+  /* A common structure for all callback parameters.                       */
+  /*                                                                       */
+  /* Some members may be unused.  For example, `pt0' is not used for       */
+  /* `moveTo' and `pt3' is not used for `quadTo'.  The initial point `pt0' */
+  /* is included for each path element for generality; curve conversions   */
+  /* need it.  The `op' parameter allows one function to handle multiple   */
+  /* element types.                                                        */
+
+  typedef struct  CF2_CallbackParamsRec_
+  {
+    FT_Vector  pt0;
+    FT_Vector  pt1;
+    FT_Vector  pt2;
+    FT_Vector  pt3;
+
+    CF2_Int  op;
+
+  } CF2_CallbackParamsRec, *CF2_CallbackParams;
+
+
+  /* forward reference */
+  typedef struct CF2_OutlineCallbacksRec_  CF2_OutlineCallbacksRec,
+                                           *CF2_OutlineCallbacks;
+
+  /* callback function pointers */
+  typedef void
+  (*CF2_Callback_Type)( CF2_OutlineCallbacks      callbacks,
+                        const CF2_CallbackParams  params );
+
+
+  struct  CF2_OutlineCallbacksRec_
+  {
+    CF2_Callback_Type  moveTo;
+    CF2_Callback_Type  lineTo;
+    CF2_Callback_Type  quadTo;
+    CF2_Callback_Type  cubeTo;
+
+    CF2_Int  windingMomentum;    /* for winding order detection */
+
+    FT_Memory  memory;
+    FT_Error*  error;
+  };
+
+
+FT_END_HEADER
+
+
+#endif /* __CF2GLUE_H__ */
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2hints.c
@@ -1,0 +1,1733 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2hints.c                                                             */
+/*                                                                         */
+/*    Adobe's code for handling CFF hints (body).                          */
+/*                                                                         */
+/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#include "cf2ft.h"
+#include FT_INTERNAL_DEBUG_H
+
+#include "cf2glue.h"
+#include "cf2font.h"
+#include "cf2hints.h"
+#include "cf2intrp.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  trace_cf2hints
+
+
+  typedef struct  CF2_HintMoveRec_
+  {
+    size_t     j;          /* index of upper hint map edge   */
+    CF2_Fixed  moveUp;     /* adjustment to optimum position */
+
+  } CF2_HintMoveRec, *CF2_HintMove;
+
+
+  /* Compute angular momentum for winding order detection.  It is called */
+  /* for all lines and curves, but not necessarily in element order.     */
+  static CF2_Int
+  cf2_getWindingMomentum( CF2_Fixed  x1,
+                          CF2_Fixed  y1,
+                          CF2_Fixed  x2,
+                          CF2_Fixed  y2 )
+  {
+    /* cross product of pt1 position from origin with pt2 position from  */
+    /* pt1; we reduce the precision so that the result fits into 32 bits */
+
+    return ( x1 >> 16 ) * ( ( y2 - y1 ) >> 16 ) -
+           ( y1 >> 16 ) * ( ( x2 - x1 ) >> 16 );
+  }
+
+
+  /*
+   * Construct from a StemHint; this is used as a parameter to
+   * `cf2_blues_capture'.
+   * `hintOrigin' is the character space displacement of a seac accent.
+   * Adjust stem hint for darkening here.
+   *
+   */
+  static void
+  cf2_hint_init( CF2_Hint            hint,
+                 const CF2_ArrStack  stemHintArray,
+                 size_t              indexStemHint,
+                 const CF2_Font      font,
+                 CF2_Fixed           hintOrigin,
+                 CF2_Fixed           scale,
+                 FT_Bool             bottom )
+  {
+    CF2_Fixed               width;
+    const CF2_StemHintRec*  stemHint;
+
+
+    FT_ZERO( hint );
+
+    stemHint = (const CF2_StemHintRec*)cf2_arrstack_getPointer(
+                                         stemHintArray,
+                                         indexStemHint );
+
+    width = stemHint->max - stemHint->min;
+
+    if ( width == cf2_intToFixed( -21 ) )
+    {
+      /* ghost bottom */
+
+      if ( bottom )
+      {
+        hint->csCoord = stemHint->max;
+        hint->flags   = CF2_GhostBottom;
+      }
+      else
+        hint->flags = 0;
+    }
+
+    else if ( width == cf2_intToFixed( -20 ) )
+    {
+      /* ghost top */
+
+      if ( bottom )
+        hint->flags = 0;
+      else
+      {
+        hint->csCoord = stemHint->min;
+        hint->flags   = CF2_GhostTop;
+      }
+    }
+
+    else if ( width < 0 )
+    {
+      /* inverted pair */
+
+      /*
+       * Hints with negative widths were produced by an early version of a
+       * non-Adobe font tool.  The Type 2 spec allows edge (ghost) hints
+       * with negative widths, but says
+       *
+       *   All other negative widths have undefined meaning.
+       *
+       * CoolType has a silent workaround that negates the hint width; for
+       * permissive mode, we do the same here.
+       *
+       * Note: Such fonts cannot use ghost hints, but should otherwise work.
+       * Note: Some poor hints in our faux fonts can produce negative
+       *       widths at some blends.  For example, see a light weight of
+       *       `u' in ASerifMM.
+       *
+       */
+      if ( bottom )
+      {
+        hint->csCoord = stemHint->max;
+        hint->flags   = CF2_PairBottom;
+      }
+      else
+      {
+        hint->csCoord = stemHint->min;
+        hint->flags   = CF2_PairTop;
+      }
+    }
+
+    else
+    {
+      /* normal pair */
+
+      if ( bottom )
+      {
+        hint->csCoord = stemHint->min;
+        hint->flags   = CF2_PairBottom;
+      }
+      else
+      {
+        hint->csCoord = stemHint->max;
+        hint->flags   = CF2_PairTop;
+      }
+    }
+
+    /* Now that ghost hints have been detected, adjust this edge for      */
+    /* darkening.  Bottoms are not changed; tops are incremented by twice */
+    /* `darkenY'.                                                         */
+    if ( cf2_hint_isTop( hint ) )
+      hint->csCoord += 2 * font->darkenY;
+
+    hint->csCoord += hintOrigin;
+    hint->scale    = scale;
+    hint->index    = indexStemHint;   /* index in original stem hint array */
+
+    /* if original stem hint has been used, use the same position */
+    if ( hint->flags != 0 && stemHint->used )
+    {
+      if ( cf2_hint_isTop( hint ) )
+        hint->dsCoord = stemHint->maxDS;
+      else
+        hint->dsCoord = stemHint->minDS;
+
+      cf2_hint_lock( hint );
+    }
+    else
+      hint->dsCoord = FT_MulFix( hint->csCoord, scale );
+  }
+
+
+  /* initialize an invalid hint map element */
+  static void
+  cf2_hint_initZero( CF2_Hint  hint )
+  {
+    FT_ZERO( hint );
+  }
+
+
+  FT_LOCAL_DEF( FT_Bool )
+  cf2_hint_isValid( const CF2_Hint  hint )
+  {
+    return hint->flags != 0;
+  }
+
+
+  static FT_Bool
+  cf2_hint_isPair( const CF2_Hint  hint )
+  {
+    return ( hint->flags                      &
+             ( CF2_PairBottom | CF2_PairTop ) ) != 0;
+  }
+
+
+  static FT_Bool
+  cf2_hint_isPairTop( const CF2_Hint  hint )
+  {
+    return ( hint->flags & CF2_PairTop ) != 0;
+  }
+
+
+  FT_LOCAL_DEF( FT_Bool )
+  cf2_hint_isTop( const CF2_Hint  hint )
+  {
+    return ( hint->flags                    &
+             ( CF2_PairTop | CF2_GhostTop ) ) != 0;
+  }
+
+
+  FT_LOCAL_DEF( FT_Bool )
+  cf2_hint_isBottom( const CF2_Hint  hint )
+  {
+    return ( hint->flags                          &
+             ( CF2_PairBottom | CF2_GhostBottom ) ) != 0;
+  }
+
+
+  static FT_Bool
+  cf2_hint_isLocked( const CF2_Hint  hint )
+  {
+    return ( hint->flags & CF2_Locked ) != 0;
+  }
+
+
+  static FT_Bool
+  cf2_hint_isSynthetic( const CF2_Hint  hint )
+  {
+    return ( hint->flags & CF2_Synthetic ) != 0;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_hint_lock( CF2_Hint  hint )
+  {
+    hint->flags |= CF2_Locked;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_hintmap_init( CF2_HintMap   hintmap,
+                    CF2_Font      font,
+                    CF2_HintMap   initialMap,
+                    CF2_ArrStack  hintMoves,
+                    CF2_Fixed     scale )
+  {
+    FT_ZERO( hintmap );
+
+    /* copy parameters from font instance */
+    hintmap->hinted         = font->hinted;
+    hintmap->scale          = scale;
+    hintmap->font           = font;
+    hintmap->initialHintMap = initialMap;
+    /* will clear in `cf2_hintmap_adjustHints' */
+    hintmap->hintMoves      = hintMoves;
+  }
+
+
+  static FT_Bool
+  cf2_hintmap_isValid( const CF2_HintMap  hintmap )
+  {
+    return hintmap->isValid;
+  }
+
+
+  /* transform character space coordinate to device space using hint map */
+  static CF2_Fixed
+  cf2_hintmap_map( CF2_HintMap  hintmap,
+                   CF2_Fixed    csCoord )
+  {
+    FT_ASSERT( hintmap->isValid );  /* must call Build before Map */
+    FT_ASSERT( hintmap->lastIndex < CF2_MAX_HINT_EDGES );
+
+    if ( hintmap->count == 0 || ! hintmap->hinted )
+    {
+      /* there are no hints; use uniform scale and zero offset */
+      return FT_MulFix( csCoord, hintmap->scale );
+    }
+    else
+    {
+      /* start linear search from last hit */
+      CF2_UInt  i = hintmap->lastIndex;
+
+
+      /* search up */
+      while ( i < hintmap->count - 1                  &&
+              csCoord >= hintmap->edge[i + 1].csCoord )
+        i += 1;
+
+      /* search down */
+      while ( i > 0 && csCoord < hintmap->edge[i].csCoord )
+        i -= 1;
+
+      hintmap->lastIndex = i;
+
+      if ( i == 0 && csCoord < hintmap->edge[0].csCoord )
+      {
+        /* special case for points below first edge: use uniform scale */
+        return FT_MulFix( csCoord - hintmap->edge[0].csCoord,
+                          hintmap->scale ) +
+                 hintmap->edge[0].dsCoord;
+      }
+      else
+      {
+        /*
+         * Note: entries with duplicate csCoord are allowed.
+         * Use edge[i], the highest entry where csCoord >= entry[i].csCoord
+         */
+        return FT_MulFix( csCoord - hintmap->edge[i].csCoord,
+                          hintmap->edge[i].scale ) +
+                 hintmap->edge[i].dsCoord;
+      }
+    }
+  }
+
+
+  /*
+   * This hinting policy moves a hint pair in device space so that one of
+   * its two edges is on a device pixel boundary (its fractional part is
+   * zero).  `cf2_hintmap_insertHint' guarantees no overlap in CS
+   * space.  Ensure here that there is no overlap in DS.
+   *
+   * In the first pass, edges are adjusted relative to adjacent hints.
+   * Those that are below have already been adjusted.  Those that are
+   * above have not yet been adjusted.  If a hint above blocks an
+   * adjustment to an optimal position, we will try again in a second
+   * pass.  The second pass is top-down.
+   *
+   */
+
+  static void
+  cf2_hintmap_adjustHints( CF2_HintMap  hintmap )
+  {
+    size_t  i, j;
+
+
+    cf2_arrstack_clear( hintmap->hintMoves );      /* working storage */
+
+    /*
+     * First pass is bottom-up (font hint order) without look-ahead.
+     * Locked edges are already adjusted.
+     * Unlocked edges begin with dsCoord from `initialHintMap'.
+     * Save edges that are not optimally adjusted in `hintMoves' array,
+     * and process them in second pass.
+     */
+
+    for ( i = 0; i < hintmap->count; i++ )
+    {
+      FT_Bool  isPair = cf2_hint_isPair( &hintmap->edge[i] );
+
+
+      /* index of upper edge (same value for ghost hint) */
+      j = isPair ? i + 1 : i;
+
+      FT_ASSERT( j < hintmap->count );
+      FT_ASSERT( cf2_hint_isValid( &hintmap->edge[i] ) );
+      FT_ASSERT( cf2_hint_isValid( &hintmap->edge[j] ) );
+      FT_ASSERT( cf2_hint_isLocked( &hintmap->edge[i] ) ==
+                   cf2_hint_isLocked( &hintmap->edge[j] ) );
+
+      if ( !cf2_hint_isLocked( &hintmap->edge[i] ) )
+      {
+        /* hint edge is not locked, we can adjust it */
+        CF2_Fixed  fracDown = cf2_fixedFraction( hintmap->edge[i].dsCoord );
+        CF2_Fixed  fracUp   = cf2_fixedFraction( hintmap->edge[j].dsCoord );
+
+        /* calculate all four possibilities; moves down are negative */
+        CF2_Fixed  downMoveDown = 0 - fracDown;
+        CF2_Fixed  upMoveDown   = 0 - fracUp;
+        CF2_Fixed  downMoveUp   = fracDown == 0
+                                    ? 0
+                                    : cf2_intToFixed( 1 ) - fracDown;
+        CF2_Fixed  upMoveUp     = fracUp == 0
+                                    ? 0
+                                    : cf2_intToFixed( 1 ) - fracUp;
+
+        /* smallest move up */
+        CF2_Fixed  moveUp   = FT_MIN( downMoveUp, upMoveUp );
+        /* smallest move down */
+        CF2_Fixed  moveDown = FT_MAX( downMoveDown, upMoveDown );
+
+        /* final amount to move edge or edge pair */
+        CF2_Fixed  move;
+
+        CF2_Fixed  downMinCounter = CF2_MIN_COUNTER;
+        CF2_Fixed  upMinCounter   = CF2_MIN_COUNTER;
+        FT_Bool    saveEdge       = FALSE;
+
+
+        /* minimum counter constraint doesn't apply when adjacent edges */
+        /* are synthetic                                                */
+        /* TODO: doesn't seem a big effect; for now, reduce the code    */
+#if 0
+        if ( i == 0                                        ||
+             cf2_hint_isSynthetic( &hintmap->edge[i - 1] ) )
+          downMinCounter = 0;
+
+        if ( j >= hintmap->count - 1                       ||
+             cf2_hint_isSynthetic( &hintmap->edge[j + 1] ) )
+          upMinCounter = 0;
+#endif
+
+        /* is there room to move up?                                    */
+        /* there is if we are at top of array or the next edge is at or */
+        /* beyond proposed move up?                                     */
+        if ( j >= hintmap->count - 1                            ||
+             hintmap->edge[j + 1].dsCoord >=
+               hintmap->edge[j].dsCoord + moveUp + upMinCounter )
+        {
+          /* there is room to move up; is there also room to move down? */
+          if ( i == 0                                                 ||
+               hintmap->edge[i - 1].dsCoord <=
+                 hintmap->edge[i].dsCoord + moveDown - downMinCounter )
+          {
+            /* move smaller absolute amount */
+            move = ( -moveDown < moveUp ) ? moveDown : moveUp;  /* optimum */
+          }
+          else
+            move = moveUp;
+        }
+        else
+        {
+          /* is there room to move down? */
+          if ( i == 0                                                 ||
+               hintmap->edge[i - 1].dsCoord <=
+                 hintmap->edge[i].dsCoord + moveDown - downMinCounter )
+          {
+            move     = moveDown;
+            saveEdge = moveUp < -moveDown;  /* true if non-optimum move */
+          }
+          else
+          {
+            /* no room to move either way without overlapping or reducing */
+            /* the counter too much                                       */
+            move     = 0;
+            saveEdge = TRUE;
+          }
+        }
+
+        /* Identify non-moves and moves down that aren't optimal, and save */
+        /* them for second pass.                                           */
+        /* Do this only if there is an unlocked edge above (which could    */
+        /* possibly move).                                                 */
+        if ( saveEdge                                    &&
+             j < hintmap->count - 1                      &&
+             !cf2_hint_isLocked( &hintmap->edge[j + 1] ) )
+        {
+          CF2_HintMoveRec  savedMove;
+
+
+          savedMove.j      = j;
+          /* desired adjustment in second pass */
+          savedMove.moveUp = moveUp - move;
+
+          cf2_arrstack_push( hintmap->hintMoves, &savedMove );
+        }
+
+        /* move the edge(s) */
+        hintmap->edge[i].dsCoord += move;
+        if ( isPair )
+          hintmap->edge[j].dsCoord += move;
+      }
+
+      /* assert there are no overlaps in device space */
+      FT_ASSERT( i == 0                                                   ||
+                 hintmap->edge[i - 1].dsCoord <= hintmap->edge[i].dsCoord );
+      FT_ASSERT( i < j                                                ||
+                 hintmap->edge[i].dsCoord <= hintmap->edge[j].dsCoord );
+
+      /* adjust the scales, avoiding divide by zero */
+      if ( i > 0 )
+      {
+        if ( hintmap->edge[i].csCoord != hintmap->edge[i - 1].csCoord )
+          hintmap->edge[i - 1].scale =
+            FT_DivFix(
+              hintmap->edge[i].dsCoord - hintmap->edge[i - 1].dsCoord,
+              hintmap->edge[i].csCoord - hintmap->edge[i - 1].csCoord );
+      }
+
+      if ( isPair )
+      {
+        if ( hintmap->edge[j].csCoord != hintmap->edge[j - 1].csCoord )
+          hintmap->edge[j - 1].scale =
+            FT_DivFix(
+              hintmap->edge[j].dsCoord - hintmap->edge[j - 1].dsCoord,
+              hintmap->edge[j].csCoord - hintmap->edge[j - 1].csCoord );
+
+        i += 1;     /* skip upper edge on next loop */
+      }
+    }
+
+    /* second pass tries to move non-optimal hints up, in case there is */
+    /* room now                                                         */
+    for ( i = cf2_arrstack_size( hintmap->hintMoves ); i > 0; i-- )
+    {
+      CF2_HintMove  hintMove = (CF2_HintMove)
+                      cf2_arrstack_getPointer( hintmap->hintMoves, i - 1 );
+
+
+      j = hintMove->j;
+
+      /* this was tested before the push, above */
+      FT_ASSERT( j < hintmap->count - 1 );
+
+      /* is there room to move up? */
+      if ( hintmap->edge[j + 1].dsCoord >=
+             hintmap->edge[j].dsCoord + hintMove->moveUp + CF2_MIN_COUNTER )
+      {
+        /* there is more room now, move edge up */
+        hintmap->edge[j].dsCoord += hintMove->moveUp;
+
+        if ( cf2_hint_isPair( &hintmap->edge[j] ) )
+        {
+          FT_ASSERT( j > 0 );
+          hintmap->edge[j - 1].dsCoord += hintMove->moveUp;
+        }
+      }
+    }
+  }
+
+
+  /* insert hint edges into map, sorted by csCoord */
+  static void
+  cf2_hintmap_insertHint( CF2_HintMap  hintmap,
+                          CF2_Hint     bottomHintEdge,
+                          CF2_Hint     topHintEdge )
+  {
+    CF2_UInt  indexInsert;
+
+    /* set default values, then check for edge hints */
+    FT_Bool   isPair         = TRUE;
+    CF2_Hint  firstHintEdge  = bottomHintEdge;
+    CF2_Hint  secondHintEdge = topHintEdge;
+
+
+    /* one or none of the input params may be invalid when dealing with */
+    /* edge hints; at least one edge must be valid                      */
+    FT_ASSERT( cf2_hint_isValid( bottomHintEdge ) ||
+               cf2_hint_isValid( topHintEdge )    );
+
+    /* determine how many and which edges to insert */
+    if ( !cf2_hint_isValid( bottomHintEdge ) )
+    {
+      /* insert only the top edge */
+      firstHintEdge = topHintEdge;
+      isPair        = FALSE;
+    }
+    else if ( !cf2_hint_isValid( topHintEdge ) )
+    {
+      /* insert only the bottom edge */
+      isPair = FALSE;
+    }
+
+    /* paired edges must be in proper order */
+    FT_ASSERT( !isPair                                         ||
+               topHintEdge->csCoord >= bottomHintEdge->csCoord );
+
+    /* linear search to find index value of insertion point */
+    indexInsert = 0;
+    for ( ; indexInsert < hintmap->count; indexInsert++ )
+    {
+      if ( hintmap->edge[indexInsert].csCoord > firstHintEdge->csCoord )
+        break;
+    }
+
+    /*
+     * Discard any hints that overlap in character space.  Most often,
+     * this is while building the initial map, but in theory, it can also
+     * occur because of darkening.
+     *
+     */
+    if ( indexInsert < hintmap->count )
+    {
+      /* we are inserting before an existing edge:              */
+      /* verify that a new pair does not straddle the next edge */
+      if ( isPair                                                       &&
+           hintmap->edge[indexInsert].csCoord < secondHintEdge->csCoord )
+        return; /* ignore overlapping stem hint */
+
+      /* verify that we are not inserting between paired edges */
+      if ( cf2_hint_isPairTop( &hintmap->edge[indexInsert] ) )
+        return; /* ignore overlapping stem hint */
+    }
+
+    /* recompute device space locations using initial hint map */
+    if ( cf2_hintmap_isValid( hintmap->initialHintMap ) &&
+         !cf2_hint_isLocked( firstHintEdge )            )
+    {
+      if ( isPair )
+      {
+        /* Use hint map to position the center of stem, and nominal scale */
+        /* to position the two edges.  This preserves the stem width.     */
+        CF2_Fixed  midpoint  = cf2_hintmap_map(
+                                 hintmap->initialHintMap,
+                                 ( secondHintEdge->csCoord +
+                                   firstHintEdge->csCoord ) / 2 );
+        CF2_Fixed  halfWidth = FT_MulFix(
+                                 ( secondHintEdge->csCoord -
+                                   firstHintEdge->csCoord ) / 2,
+                                 hintmap->scale );
+
+
+        firstHintEdge->dsCoord  = midpoint - halfWidth;
+        secondHintEdge->dsCoord = midpoint + halfWidth;
+      }
+      else
+        firstHintEdge->dsCoord = cf2_hintmap_map( hintmap->initialHintMap,
+                                                  firstHintEdge->csCoord );
+    }
+
+    /* discard any hints that overlap in device space; this can occur */
+    /* because locked hints have been moved to align with blue zones  */
+    if ( indexInsert > 0 )
+    {
+      /* we are inserting after an existing edge */
+      if ( firstHintEdge->dsCoord < hintmap->edge[indexInsert - 1].dsCoord )
+        return;
+    }
+
+    if ( indexInsert < hintmap->count )
+    {
+      /* we are inserting before an existing edge */
+      if ( isPair )
+      {
+        if ( secondHintEdge->dsCoord > hintmap->edge[indexInsert].dsCoord )
+          return;
+      }
+      else
+      {
+        if ( firstHintEdge->dsCoord > hintmap->edge[indexInsert].dsCoord )
+          return;
+      }
+    }
+
+    /* make room to insert */
+    {
+      CF2_Int  iSrc = hintmap->count - 1;
+      CF2_Int  iDst = isPair ? hintmap->count + 1 : hintmap->count;
+
+      CF2_Int  count = hintmap->count - indexInsert;
+
+
+      if ( iDst >= CF2_MAX_HINT_EDGES )
+      {
+        FT_TRACE4(( "cf2_hintmap_insertHint: too many hintmaps\n" ));
+        return;
+      }
+
+      while ( count-- )
+        hintmap->edge[iDst--] = hintmap->edge[iSrc--];
+
+      /* insert first edge */
+      hintmap->edge[indexInsert] = *firstHintEdge;         /* copy struct */
+      hintmap->count += 1;
+
+      if ( isPair )
+      {
+        /* insert second edge */
+        hintmap->edge[indexInsert + 1] = *secondHintEdge;  /* copy struct */
+        hintmap->count                += 1;
+      }
+    }
+
+    return;
+  }
+
+
+  /*
+   * Build a map from hints and mask.
+   *
+   * This function may recur one level if `hintmap->initialHintMap' is not yet
+   * valid.
+   * If `initialMap' is true, simply build initial map.
+   *
+   * Synthetic hints are used in two ways.  A hint at zero is inserted, if
+   * needed, in the initial hint map, to prevent translations from
+   * propagating across the origin.  If synthetic em box hints are enabled
+   * for ideographic dictionaries, then they are inserted in all hint
+   * maps, including the initial one.
+   *
+   */
+  FT_LOCAL_DEF( void )
+  cf2_hintmap_build( CF2_HintMap   hintmap,
+                     CF2_ArrStack  hStemHintArray,
+                     CF2_ArrStack  vStemHintArray,
+                     CF2_HintMask  hintMask,
+                     CF2_Fixed     hintOrigin,
+                     FT_Bool       initialMap )
+  {
+    FT_Byte*  maskPtr;
+
+    CF2_Font         font = hintmap->font;
+    CF2_HintMaskRec  tempHintMask;
+
+    size_t   bitCount, i;
+    FT_Byte  maskByte;
+
+
+    /* check whether initial map is constructed */
+    if ( !initialMap && !cf2_hintmap_isValid( hintmap->initialHintMap ) )
+    {
+      /* make recursive call with initialHintMap and temporary mask; */
+      /* temporary mask will get all bits set, below */
+      cf2_hintmask_init( &tempHintMask, hintMask->error );
+      cf2_hintmap_build( hintmap->initialHintMap,
+                         hStemHintArray,
+                         vStemHintArray,
+                         &tempHintMask,
+                         hintOrigin,
+                         TRUE );
+    }
+
+    if ( !cf2_hintmask_isValid( hintMask ) )
+    {
+      /* without a hint mask, assume all hints are active */
+      cf2_hintmask_setAll( hintMask,
+                           cf2_arrstack_size( hStemHintArray ) +
+                             cf2_arrstack_size( vStemHintArray ) );
+    }
+
+    /* begin by clearing the map */
+    hintmap->count     = 0;
+    hintmap->lastIndex = 0;
+
+    /* make a copy of the hint mask so we can modify it */
+    tempHintMask = *hintMask;
+    maskPtr      = cf2_hintmask_getMaskPtr( &tempHintMask );
+
+    /* use the hStem hints only, which are first in the mask */
+    /* TODO: compare this to cffhintmaskGetBitCount */
+    bitCount = cf2_arrstack_size( hStemHintArray );
+
+    /* synthetic embox hints get highest priority */
+    if ( font->blues.doEmBoxHints )
+    {
+      CF2_HintRec  dummy;
+
+
+      cf2_hint_initZero( &dummy );   /* invalid hint map element */
+
+      /* ghost bottom */
+      cf2_hintmap_insertHint( hintmap,
+                              &font->blues.emBoxBottomEdge,
+                              &dummy );
+      /* ghost top */
+      cf2_hintmap_insertHint( hintmap,
+                              &dummy,
+                              &font->blues.emBoxTopEdge );
+    }
+
+    /* insert hints captured by a blue zone or already locked (higher */
+    /* priority)                                                      */
+    for ( i = 0, maskByte = 0x80; i < bitCount; i++ )
+    {
+      if ( maskByte & *maskPtr )
+      {
+        /* expand StemHint into two `CF2_Hint' elements */
+        CF2_HintRec  bottomHintEdge, topHintEdge;
+
+
+        cf2_hint_init( &bottomHintEdge,
+                       hStemHintArray,
+                       i,
+                       font,
+                       hintOrigin,
+                       hintmap->scale,
+                       TRUE /* bottom */ );
+        cf2_hint_init( &topHintEdge,
+                       hStemHintArray,
+                       i,
+                       font,
+                       hintOrigin,
+                       hintmap->scale,
+                       FALSE /* top */ );
+
+        if ( cf2_hint_isLocked( &bottomHintEdge ) ||
+             cf2_hint_isLocked( &topHintEdge )    ||
+             cf2_blues_capture( &font->blues,
+                                &bottomHintEdge,
+                                &topHintEdge )   )
+        {
+          /* insert captured hint into map */
+          cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge );
+
+          *maskPtr &= ~maskByte;      /* turn off the bit for this hint */
+        }
+      }
+
+      if ( ( i & 7 ) == 7 )
+      {
+        /* move to next mask byte */
+        maskPtr++;
+        maskByte = 0x80;
+      }
+      else
+        maskByte >>= 1;
+    }
+
+    /* initial hint map includes only captured hints plus maybe one at 0 */
+
+    /*
+     * TODO: There is a problem here because we are trying to build a
+     *       single hint map containing all captured hints.  It is
+     *       possible for there to be conflicts between captured hints,
+     *       either because of darkening or because the hints are in
+     *       separate hint zones (we are ignoring hint zones for the
+     *       initial map).  An example of the latter is MinionPro-Regular
+     *       v2.030 glyph 883 (Greek Capital Alpha with Psili) at 15ppem.
+     *       A stem hint for the psili conflicts with the top edge hint
+     *       for the base character.  The stem hint gets priority because
+     *       of its sort order.  In glyph 884 (Greek Capital Alpha with
+     *       Psili and Oxia), the top of the base character gets a stem
+     *       hint, and the psili does not.  This creates different initial
+     *       maps for the two glyphs resulting in different renderings of
+     *       the base character.  Will probably defer this either as not
+     *       worth the cost or as a font bug.  I don't think there is any
+     *       good reason for an accent to be captured by an alignment
+     *       zone.  -darnold 2/12/10
+     */
+
+    if ( initialMap )
+    {
+      /* Apply a heuristic that inserts a point for (0,0), unless it's     */
+      /* already covered by a mapping.  This locks the baseline for glyphs */
+      /* that have no baseline hints.                                      */
+
+      if ( hintmap->count == 0                           ||
+           hintmap->edge[0].csCoord > 0                  ||
+           hintmap->edge[hintmap->count - 1].csCoord < 0 )
+      {
+        /* all edges are above 0 or all edges are below 0; */
+        /* construct a locked edge hint at 0               */
+
+        CF2_HintRec  edge, invalid;
+
+
+        cf2_hint_initZero( &edge );
+
+        edge.flags = CF2_GhostBottom |
+                     CF2_Locked      |
+                     CF2_Synthetic;
+        edge.scale = hintmap->scale;
+
+        cf2_hint_initZero( &invalid );
+        cf2_hintmap_insertHint( hintmap, &edge, &invalid );
+      }
+    }
+    else
+    {
+      /* insert remaining hints */
+
+      maskPtr = cf2_hintmask_getMaskPtr( &tempHintMask );
+
+      for ( i = 0, maskByte = 0x80; i < bitCount; i++ )
+      {
+        if ( maskByte & *maskPtr )
+        {
+          CF2_HintRec  bottomHintEdge, topHintEdge;
+
+
+          cf2_hint_init( &bottomHintEdge,
+                         hStemHintArray,
+                         i,
+                         font,
+                         hintOrigin,
+                         hintmap->scale,
+                         TRUE /* bottom */ );
+          cf2_hint_init( &topHintEdge,
+                         hStemHintArray,
+                         i,
+                         font,
+                         hintOrigin,
+                         hintmap->scale,
+                         FALSE /* top */ );
+
+          cf2_hintmap_insertHint( hintmap, &bottomHintEdge, &topHintEdge );
+        }
+
+        if ( ( i & 7 ) == 7 )
+        {
+          /* move to next mask byte */
+          maskPtr++;
+          maskByte = 0x80;
+        }
+        else
+          maskByte >>= 1;
+      }
+    }
+
+    /*
+     * Note: The following line is a convenient place to break when
+     *       debugging hinting.  Examine `hintmap->edge' for the list of
+     *       enabled hints, then step over the call to see the effect of
+     *       adjustment.  We stop here first on the recursive call that
+     *       creates the initial map, and then on each counter group and
+     *       hint zone.
+     */
+
+    /* adjust positions of hint edges that are not locked to blue zones */
+    cf2_hintmap_adjustHints( hintmap );
+
+    /* save the position of all hints that were used in this hint map; */
+    /* if we use them again, we'll locate them in the same position    */
+    if ( !initialMap )
+    {
+      for ( i = 0; i < hintmap->count; i++ )
+      {
+        if ( !cf2_hint_isSynthetic( &hintmap->edge[i] ) )
+        {
+          /* Note: include both valid and invalid edges            */
+          /* Note: top and bottom edges are copied back separately */
+          CF2_StemHint  stemhint = (CF2_StemHint)
+                          cf2_arrstack_getPointer( hStemHintArray,
+                                                   hintmap->edge[i].index );
+
+
+          if ( cf2_hint_isTop( &hintmap->edge[i] ) )
+            stemhint->maxDS = hintmap->edge[i].dsCoord;
+          else
+            stemhint->minDS = hintmap->edge[i].dsCoord;
+
+          stemhint->used = TRUE;
+        }
+      }
+    }
+
+    /* hint map is ready to use */
+    hintmap->isValid = TRUE;
+
+    /* remember this mask has been used */
+    cf2_hintmask_setNew( hintMask, FALSE );
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_glyphpath_init( CF2_GlyphPath         glyphpath,
+                      CF2_Font              font,
+                      CF2_OutlineCallbacks  callbacks,
+                      CF2_Fixed             scaleY,
+                      /* CF2_Fixed  hShift, */
+                      CF2_ArrStack          hStemHintArray,
+                      CF2_ArrStack          vStemHintArray,
+                      CF2_HintMask          hintMask,
+                      CF2_Fixed             hintOriginY,
+                      const CF2_Blues       blues,
+                      const FT_Vector*      fractionalTranslation )
+  {
+    FT_ZERO( glyphpath );
+
+    glyphpath->font      = font;
+    glyphpath->callbacks = callbacks;
+
+    cf2_arrstack_init( &glyphpath->hintMoves,
+                       font->memory,
+                       &font->error,
+                       sizeof ( CF2_HintMoveRec ) );
+
+    cf2_hintmap_init( &glyphpath->initialHintMap,
+                      font,
+                      &glyphpath->initialHintMap,
+                      &glyphpath->hintMoves,
+                      scaleY );
+    cf2_hintmap_init( &glyphpath->firstHintMap,
+                      font,
+                      &glyphpath->initialHintMap,
+                      &glyphpath->hintMoves,
+                      scaleY );
+    cf2_hintmap_init( &glyphpath->hintMap,
+                      font,
+                      &glyphpath->initialHintMap,
+                      &glyphpath->hintMoves,
+                      scaleY );
+
+    glyphpath->scaleX = font->innerTransform.a;
+    glyphpath->scaleC = font->innerTransform.c;
+    glyphpath->scaleY = font->innerTransform.d;
+
+    glyphpath->fractionalTranslation = *fractionalTranslation;
+
+#if 0
+    glyphpath->hShift = hShift;       /* for fauxing */
+#endif
+
+    glyphpath->hStemHintArray = hStemHintArray;
+    glyphpath->vStemHintArray = vStemHintArray;
+    glyphpath->hintMask       = hintMask;      /* ptr to current mask */
+    glyphpath->hintOriginY    = hintOriginY;
+    glyphpath->blues          = blues;
+    glyphpath->darken         = font->darkened; /* TODO: should we make copies? */
+    glyphpath->xOffset        = font->darkenX;
+    glyphpath->yOffset        = font->darkenY;
+    glyphpath->miterLimit     = 2 * FT_MAX(
+                                     cf2_fixedAbs( glyphpath->xOffset ),
+                                     cf2_fixedAbs( glyphpath->yOffset ) );
+
+    /* .1 character space unit */
+    glyphpath->snapThreshold = cf2_floatToFixed( 0.1f );
+
+    glyphpath->moveIsPending = TRUE;
+    glyphpath->pathIsOpen    = FALSE;
+    glyphpath->elemIsQueued  = FALSE;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_glyphpath_finalize( CF2_GlyphPath  glyphpath )
+  {
+    cf2_arrstack_finalize( &glyphpath->hintMoves );
+  }
+
+
+  /*
+   * Hint point in y-direction and apply outerTransform.
+   * Input `current' hint map (which is actually delayed by one element).
+   * Input x,y point in Character Space.
+   * Output x,y point in Device Space, including translation.
+   */
+  static void
+  cf2_glyphpath_hintPoint( CF2_GlyphPath  glyphpath,
+                           CF2_HintMap    hintmap,
+                           FT_Vector*     ppt,
+                           CF2_Fixed      x,
+                           CF2_Fixed      y )
+  {
+    FT_Vector  pt;   /* hinted point in upright DS */
+
+
+    pt.x = FT_MulFix( glyphpath->scaleX, x ) +
+             FT_MulFix( glyphpath->scaleC, y );
+    pt.y = cf2_hintmap_map( hintmap, y );
+
+    ppt->x = FT_MulFix( glyphpath->font->outerTransform.a, pt.x )   +
+               FT_MulFix( glyphpath->font->outerTransform.c, pt.y ) +
+               glyphpath->fractionalTranslation.x;
+    ppt->y = FT_MulFix( glyphpath->font->outerTransform.b, pt.x )   +
+               FT_MulFix( glyphpath->font->outerTransform.d, pt.y ) +
+               glyphpath->fractionalTranslation.y;
+  }
+
+
+  /*
+   * From two line segments, (u1,u2) and (v1,v2), compute a point of
+   * intersection on the corresponding lines.
+   * Return false if no intersection is found, or if the intersection is
+   * too far away from the ends of the line segments, u2 and v1.
+   *
+   */
+  static FT_Bool
+  cf2_glyphpath_computeIntersection( CF2_GlyphPath     glyphpath,
+                                     const FT_Vector*  u1,
+                                     const FT_Vector*  u2,
+                                     const FT_Vector*  v1,
+                                     const FT_Vector*  v2,
+                                     FT_Vector*        intersection )
+  {
+    /*
+     * Let `u' be a zero-based vector from the first segment, `v' from the
+     * second segment.
+     * Let `w 'be the zero-based vector from `u1' to `v1'.
+     * `perp' is the `perpendicular dot product'; see
+     * http://mathworld.wolfram.com/PerpDotProduct.html.
+     * `s' is the parameter for the parametric line for the first segment
+     * (`u').
+     *
+     * See notation in
+     * http://softsurfer.com/Archive/algorithm_0104/algorithm_0104B.htm.
+     * Calculations are done in 16.16, but must handle the squaring of
+     * line lengths in character space.  We scale all vectors by 1/32 to
+     * avoid overflow.  This allows values up to 4095 to be squared.  The
+     * scale factor cancels in the divide.
+     *
+     * TODO: the scale factor could be computed from UnitsPerEm.
+     *
+     */
+
+#define cf2_perp( a, b )                                    \
+          ( FT_MulFix( a.x, b.y ) - FT_MulFix( a.y, b.x ) )
+
+  /* round and divide by 32 */
+#define CF2_CS_SCALE( x )         \
+          ( ( (x) + 0x10 ) >> 5 )
+
+    FT_Vector  u, v, w;      /* scaled vectors */
+    CF2_Fixed  denominator, s;
+
+
+    u.x = CF2_CS_SCALE( u2->x - u1->x );
+    u.y = CF2_CS_SCALE( u2->y - u1->y );
+    v.x = CF2_CS_SCALE( v2->x - v1->x );
+    v.y = CF2_CS_SCALE( v2->y - v1->y );
+    w.x = CF2_CS_SCALE( v1->x - u1->x );
+    w.y = CF2_CS_SCALE( v1->y - u1->y );
+
+    denominator = cf2_perp( u, v );
+
+    if ( denominator == 0 )
+      return FALSE;           /* parallel or coincident lines */
+
+    s = FT_DivFix( cf2_perp( w, v ), denominator );
+
+    intersection->x = u1->x + FT_MulFix( s, u2->x - u1->x );
+    intersection->y = u1->y + FT_MulFix( s, u2->y - u1->y );
+
+    /*
+     * Special case snapping for horizontal and vertical lines.
+     * This cleans up intersections and reduces problems with winding
+     * order detection.
+     * Sample case is sbc cd KozGoPr6N-Medium.otf 20 16685.
+     * Note: these calculations are in character space.
+     *
+     */
+
+    if ( u1->x == u2->x                                                     &&
+         cf2_fixedAbs( intersection->x - u1->x ) < glyphpath->snapThreshold )
+      intersection->x = u1->x;
+    if ( u1->y == u2->y                                                     &&
+         cf2_fixedAbs( intersection->y - u1->y ) < glyphpath->snapThreshold )
+      intersection->y = u1->y;
+
+    if ( v1->x == v2->x                                                     &&
+         cf2_fixedAbs( intersection->x - v1->x ) < glyphpath->snapThreshold )
+      intersection->x = v1->x;
+    if ( v1->y == v2->y                                                     &&
+         cf2_fixedAbs( intersection->y - v1->y ) < glyphpath->snapThreshold )
+      intersection->y = v1->y;
+
+    /* limit the intersection distance from midpoint of u2 and v1 */
+    if ( cf2_fixedAbs( intersection->x - ( u2->x + v1->x ) / 2 ) >
+           glyphpath->miterLimit                                   ||
+         cf2_fixedAbs( intersection->y - ( u2->y + v1->y ) / 2 ) >
+           glyphpath->miterLimit                                   )
+      return FALSE;
+
+    return TRUE;
+  }
+
+
+  /*
+   * Push the cached element (glyphpath->prevElem*) to the outline
+   * consumer.  When a darkening offset is used, the end point of the
+   * cached element may be adjusted to an intersection point or it may be
+   * connected by a line to the current element.  This calculation must
+   * use a HintMap that was valid at the time the element was saved.  For
+   * the first point in a subpath, that is a saved HintMap.  For most
+   * elements, it just means the caller has delayed building a HintMap
+   * from the current HintMask.
+   *
+   * Transform each point with outerTransform and call the outline
+   * callbacks.  This is a general 3x3 transform:
+   *
+   *   x' = a*x + c*y + tx, y' = b*x + d*y + ty
+   *
+   * but it uses 4 elements from CF2_Font and the translation part
+   * from CF2_GlyphPath.
+   *
+   */
+  static void
+  cf2_glyphpath_pushPrevElem( CF2_GlyphPath  glyphpath,
+                              CF2_HintMap    hintmap,
+                              FT_Vector*     nextP0,
+                              FT_Vector      nextP1,
+                              FT_Bool        close )
+  {
+    CF2_CallbackParamsRec  params;
+
+    FT_Vector*  prevP0;
+    FT_Vector*  prevP1;
+
+    FT_Vector  intersection    = { 0, 0 };
+    FT_Bool    useIntersection = FALSE;
+
+
+    FT_ASSERT( glyphpath->prevElemOp == CF2_PathOpLineTo ||
+               glyphpath->prevElemOp == CF2_PathOpCubeTo );
+
+    if ( glyphpath->prevElemOp == CF2_PathOpLineTo )
+    {
+      prevP0 = &glyphpath->prevElemP0;
+      prevP1 = &glyphpath->prevElemP1;
+    }
+    else
+    {
+      prevP0 = &glyphpath->prevElemP2;
+      prevP1 = &glyphpath->prevElemP3;
+    }
+
+    /* optimization: if previous and next elements are offset by the same */
+    /* amount, then there will be no gap, and no need to compute an       */
+    /* intersection.                                                      */
+    if ( prevP1->x != nextP0->x || prevP1->y != nextP0->y )
+    {
+      /* previous element does not join next element:             */
+      /* adjust end point of previous element to the intersection */
+      useIntersection = cf2_glyphpath_computeIntersection( glyphpath,
+                                                           prevP0,
+                                                           prevP1,
+                                                           nextP0,
+                                                           &nextP1,
+                                                           &intersection );
+      if ( useIntersection )
+      {
+        /* modify the last point of the cached element (either line or */
+        /* curve)                                                      */
+        *prevP1 = intersection;
+      }
+    }
+
+    params.pt0 = glyphpath->currentDS;
+
+    switch( glyphpath->prevElemOp )
+    {
+    case CF2_PathOpLineTo:
+      params.op = CF2_PathOpLineTo;
+
+      /* note: pt2 and pt3 are unused */
+      cf2_glyphpath_hintPoint( glyphpath,
+                               hintmap,
+                               &params.pt1,
+                               glyphpath->prevElemP1.x,
+                               glyphpath->prevElemP1.y );
+
+      glyphpath->callbacks->lineTo( glyphpath->callbacks, &params );
+
+      glyphpath->currentDS = params.pt1;
+
+      break;
+
+    case CF2_PathOpCubeTo:
+      params.op = CF2_PathOpCubeTo;
+
+      /* TODO: should we intersect the interior joins (p1-p2 and p2-p3)? */
+      cf2_glyphpath_hintPoint( glyphpath,
+                               hintmap,
+                               &params.pt1,
+                               glyphpath->prevElemP1.x,
+                               glyphpath->prevElemP1.y );
+      cf2_glyphpath_hintPoint( glyphpath,
+                               hintmap,
+                               &params.pt2,
+                               glyphpath->prevElemP2.x,
+                               glyphpath->prevElemP2.y );
+      cf2_glyphpath_hintPoint( glyphpath,
+                               hintmap,
+                               &params.pt3,
+                               glyphpath->prevElemP3.x,
+                               glyphpath->prevElemP3.y );
+
+      glyphpath->callbacks->cubeTo( glyphpath->callbacks, &params );
+
+      glyphpath->currentDS = params.pt3;
+
+      break;
+    }
+
+    if ( !useIntersection || close )
+    {
+      /* insert connecting line between end of previous element and start */
+      /* of current one                                                   */
+      /* note: at the end of a subpath, we might do both, so use `nextP0' */
+      /* before we change it, below                                       */
+
+      cf2_glyphpath_hintPoint( glyphpath,
+                               hintmap,
+                               &params.pt1,
+                               nextP0->x,
+                               nextP0->y );
+
+      if ( params.pt1.x != glyphpath->currentDS.x ||
+           params.pt1.y != glyphpath->currentDS.y )
+      {
+        /* length is nonzero */
+        params.op  = CF2_PathOpLineTo;
+        params.pt0 = glyphpath->currentDS;
+
+        /* note: pt2 and pt3 are unused */
+        glyphpath->callbacks->lineTo( glyphpath->callbacks, &params );
+
+        glyphpath->currentDS = params.pt1;
+      }
+    }
+
+    if ( useIntersection )
+    {
+      /* return intersection point to caller */
+      *nextP0 = intersection;
+    }
+  }
+
+
+  /* push a MoveTo element based on current point and offset of current */
+  /* element                                                            */
+  static void
+  cf2_glyphpath_pushMove( CF2_GlyphPath  glyphpath,
+                          FT_Vector      start )
+  {
+    CF2_CallbackParamsRec  params;
+
+
+    params.op  = CF2_PathOpMoveTo;
+    params.pt0 = glyphpath->currentDS;
+
+    /* Test if move has really happened yet; it would have called */
+    /* `cf2_hintmap_build' to set `isValid'.                   */
+    if ( !cf2_hintmap_isValid( &glyphpath->hintMap ) )
+    {
+      /* we are here iff first subpath is missing a moveto operator: */
+      /* synthesize first moveTo to finish initialization of hintMap */
+      cf2_glyphpath_moveTo( glyphpath,
+                            glyphpath->start.x,
+                            glyphpath->start.y );
+    }
+
+    cf2_glyphpath_hintPoint( glyphpath,
+                             &glyphpath->hintMap,
+                             &params.pt1,
+                             start.x,
+                             start.y );
+
+    /* note: pt2 and pt3 are unused */
+    glyphpath->callbacks->moveTo( glyphpath->callbacks, &params );
+
+    glyphpath->currentDS    = params.pt1;
+    glyphpath->offsetStart0 = start;
+  }
+
+
+  /*
+   * All coordinates are in character space.
+   * On input, (x1, y1) and (x2, y2) give line segment.
+   * On output, (x, y) give offset vector.
+   * We use a piecewise approximation to trig functions.
+   *
+   * TODO: Offset true perpendicular and proper length
+   *       supply the y-translation for hinting here, too,
+   *       that adds yOffset unconditionally to *y.
+   */
+  static void
+  cf2_glyphpath_computeOffset( CF2_GlyphPath  glyphpath,
+                               CF2_Fixed      x1,
+                               CF2_Fixed      y1,
+                               CF2_Fixed      x2,
+                               CF2_Fixed      y2,
+                               CF2_Fixed*     x,
+                               CF2_Fixed*     y )
+  {
+    CF2_Fixed  dx = x2 - x1;
+    CF2_Fixed  dy = y2 - y1;
+
+
+    /* note: negative offsets don't work here; negate deltas to change */
+    /* quadrants, below                                                */
+    if ( glyphpath->font->reverseWinding )
+    {
+      dx = -dx;
+      dy = -dy;
+    }
+
+    *x = *y = 0;
+
+    if ( !glyphpath->darken )
+        return;
+
+    /* add momentum for this path element */
+    glyphpath->callbacks->windingMomentum +=
+      cf2_getWindingMomentum( x1, y1, x2, y2 );
+
+    /* note: allow mixed integer and fixed multiplication here */
+    if ( dx >= 0 )
+    {
+      if ( dy >= 0 )
+      {
+        /* first quadrant, +x +y */
+
+        if ( dx > 2 * dy )
+        {
+          /* +x */
+          *x = 0;
+          *y = 0;
+        }
+        else if ( dy > 2 * dx )
+        {
+          /* +y */
+          *x = glyphpath->xOffset;
+          *y = glyphpath->yOffset;
+        }
+        else
+        {
+          /* +x +y */
+          *x = FT_MulFix( cf2_floatToFixed( 0.7 ),
+                          glyphpath->xOffset );
+          *y = FT_MulFix( cf2_floatToFixed( 1.0 - 0.7 ),
+                          glyphpath->yOffset );
+        }
+      }
+      else
+      {
+        /* fourth quadrant, +x -y */
+
+        if ( dx > -2 * dy )
+        {
+          /* +x */
+          *x = 0;
+          *y = 0;
+        }
+        else if ( -dy > 2 * dx )
+        {
+          /* -y */
+          *x = -glyphpath->xOffset;
+          *y = glyphpath->yOffset;
+        }
+        else
+        {
+          /* +x -y */
+          *x = FT_MulFix( cf2_floatToFixed( -0.7 ),
+                          glyphpath->xOffset );
+          *y = FT_MulFix( cf2_floatToFixed( 1.0 - 0.7 ),
+                          glyphpath->yOffset );
+        }
+      }
+    }
+    else
+    {
+      if ( dy >= 0 )
+      {
+        /* second quadrant, -x +y */
+
+        if ( -dx > 2 * dy )
+        {
+          /* -x */
+          *x = 0;
+          *y = 2 * glyphpath->yOffset;
+        }
+        else if ( dy > -2 * dx )
+        {
+          /* +y */
+          *x = glyphpath->xOffset;
+          *y = glyphpath->yOffset;
+        }
+        else
+        {
+          /* -x +y */
+          *x = FT_MulFix( cf2_floatToFixed( 0.7 ),
+                          glyphpath->xOffset );
+          *y = FT_MulFix( cf2_floatToFixed( 1.0 + 0.7 ),
+                          glyphpath->yOffset );
+        }
+      }
+      else
+      {
+        /* third quadrant, -x -y */
+
+        if ( -dx > -2 * dy )
+        {
+          /* -x */
+          *x = 0;
+          *y = 2 * glyphpath->yOffset;
+        }
+        else if ( -dy > -2 * dx )
+        {
+          /* -y */
+          *x = -glyphpath->xOffset;
+          *y = glyphpath->xOffset;
+        }
+        else
+        {
+          /* -x -y */
+          *x = FT_MulFix( cf2_floatToFixed( -0.7 ),
+                          glyphpath->xOffset );
+          *y = FT_MulFix( cf2_floatToFixed( 1.0 + 0.7 ),
+                          glyphpath->yOffset );
+        }
+      }
+    }
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_glyphpath_moveTo( CF2_GlyphPath  glyphpath,
+                        CF2_Fixed      x,
+                        CF2_Fixed      y )
+  {
+    cf2_glyphpath_closeOpenPath( glyphpath );
+
+    /* save the parameters of the move for later, when we'll know how to */
+    /* offset it;                                                        */
+    /* also save last move point */
+    glyphpath->currentCS.x = glyphpath->start.x = x;
+    glyphpath->currentCS.y = glyphpath->start.y = y;
+
+    glyphpath->moveIsPending = TRUE;
+
+    /* ensure we have a valid map with current mask */
+    if ( !cf2_hintmap_isValid( &glyphpath->hintMap ) ||
+         cf2_hintmask_isNew( glyphpath->hintMask )   )
+      cf2_hintmap_build( &glyphpath->hintMap,
+                         glyphpath->hStemHintArray,
+                         glyphpath->vStemHintArray,
+                         glyphpath->hintMask,
+                         glyphpath->hintOriginY,
+                         FALSE );
+
+    /* save a copy of current HintMap to use when drawing initial point */
+    glyphpath->firstHintMap = glyphpath->hintMap;     /* structure copy */
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_glyphpath_lineTo( CF2_GlyphPath  glyphpath,
+                        CF2_Fixed      x,
+                        CF2_Fixed      y )
+  {
+    CF2_Fixed  xOffset, yOffset;
+    FT_Vector  P0, P1;
+
+
+    /* can't compute offset of zero length line, so ignore them */
+    if ( glyphpath->currentCS.x == x && glyphpath->currentCS.y == y )
+      return;
+
+    cf2_glyphpath_computeOffset( glyphpath,
+                                 glyphpath->currentCS.x,
+                                 glyphpath->currentCS.y,
+                                 x,
+                                 y,
+                                 &xOffset,
+                                 &yOffset );
+
+    /* construct offset points */
+    P0.x = glyphpath->currentCS.x + xOffset;
+    P0.y = glyphpath->currentCS.y + yOffset;
+    P1.x = x + xOffset;
+    P1.y = y + yOffset;
+
+    if ( glyphpath->moveIsPending )
+    {
+      /* emit offset 1st point as MoveTo */
+      cf2_glyphpath_pushMove( glyphpath, P0 );
+
+      glyphpath->moveIsPending = FALSE;  /* adjust state machine */
+      glyphpath->pathIsOpen    = TRUE;
+
+      glyphpath->offsetStart1 = P1;              /* record second point */
+    }
+
+    if ( glyphpath->elemIsQueued )
+    {
+      FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) );
+
+      cf2_glyphpath_pushPrevElem( glyphpath,
+                                  &glyphpath->hintMap,
+                                  &P0,
+                                  P1,
+                                  FALSE );
+    }
+
+    /* queue the current element with offset points */
+    glyphpath->elemIsQueued = TRUE;
+    glyphpath->prevElemOp   = CF2_PathOpLineTo;
+    glyphpath->prevElemP0   = P0;
+    glyphpath->prevElemP1   = P1;
+
+    /* update current map */
+    if ( cf2_hintmask_isNew( glyphpath->hintMask ) )
+      cf2_hintmap_build( &glyphpath->hintMap,
+                         glyphpath->hStemHintArray,
+                         glyphpath->vStemHintArray,
+                         glyphpath->hintMask,
+                         glyphpath->hintOriginY,
+                         FALSE );
+
+    glyphpath->currentCS.x = x;     /* pre-offset current point */
+    glyphpath->currentCS.y = y;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_glyphpath_curveTo( CF2_GlyphPath  glyphpath,
+                         CF2_Fixed      x1,
+                         CF2_Fixed      y1,
+                         CF2_Fixed      x2,
+                         CF2_Fixed      y2,
+                         CF2_Fixed      x3,
+                         CF2_Fixed      y3 )
+  {
+    CF2_Fixed  xOffset1, yOffset1, xOffset3, yOffset3;
+    FT_Vector  P0, P1, P2, P3;
+
+
+    /* TODO: ignore zero length portions of curve?? */
+    cf2_glyphpath_computeOffset( glyphpath,
+                                 glyphpath->currentCS.x,
+                                 glyphpath->currentCS.y,
+                                 x1,
+                                 y1,
+                                 &xOffset1,
+                                 &yOffset1 );
+    cf2_glyphpath_computeOffset( glyphpath,
+                                 x2,
+                                 y2,
+                                 x3,
+                                 y3,
+                                 &xOffset3,
+                                 &yOffset3 );
+
+    /* add momentum from the middle segment */
+    glyphpath->callbacks->windingMomentum +=
+      cf2_getWindingMomentum( x1, y1, x2, y2 );
+
+    /* construct offset points */
+    P0.x = glyphpath->currentCS.x + xOffset1;
+    P0.y = glyphpath->currentCS.y + yOffset1;
+    P1.x = x1 + xOffset1;
+    P1.y = y1 + yOffset1;
+    /* note: preserve angle of final segment by using offset3 at both ends */
+    P2.x = x2 + xOffset3;
+    P2.y = y2 + yOffset3;
+    P3.x = x3 + xOffset3;
+    P3.y = y3 + yOffset3;
+
+    if ( glyphpath->moveIsPending )
+    {
+      /* emit offset 1st point as MoveTo */
+      cf2_glyphpath_pushMove( glyphpath, P0 );
+
+      glyphpath->moveIsPending = FALSE;
+      glyphpath->pathIsOpen    = TRUE;
+
+      glyphpath->offsetStart1 = P1;              /* record second point */
+    }
+
+    if ( glyphpath->elemIsQueued )
+    {
+      FT_ASSERT( cf2_hintmap_isValid( &glyphpath->hintMap ) );
+
+      cf2_glyphpath_pushPrevElem( glyphpath,
+                                  &glyphpath->hintMap,
+                                  &P0,
+                                  P1,
+                                  FALSE );
+    }
+
+    /* queue the current element with offset points */
+    glyphpath->elemIsQueued = TRUE;
+    glyphpath->prevElemOp   = CF2_PathOpCubeTo;
+    glyphpath->prevElemP0   = P0;
+    glyphpath->prevElemP1   = P1;
+    glyphpath->prevElemP2   = P2;
+    glyphpath->prevElemP3   = P3;
+
+    /* update current map */
+    if ( cf2_hintmask_isNew( glyphpath->hintMask ) )
+      cf2_hintmap_build( &glyphpath->hintMap,
+                         glyphpath->hStemHintArray,
+                         glyphpath->vStemHintArray,
+                         glyphpath->hintMask,
+                         glyphpath->hintOriginY,
+                         FALSE );
+
+    glyphpath->currentCS.x = x3;       /* pre-offset current point */
+    glyphpath->currentCS.y = y3;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_glyphpath_closeOpenPath( CF2_GlyphPath  glyphpath )
+  {
+    if ( glyphpath->pathIsOpen )
+    {
+      FT_ASSERT( cf2_hintmap_isValid( &glyphpath->firstHintMap ) );
+
+      /* since we need to apply an offset to the implicit lineto, we make */
+      /* it explicit here                                                 */
+      cf2_glyphpath_lineTo( glyphpath,
+                            glyphpath->start.x,
+                            glyphpath->start.y );
+
+      /* Draw previous element (the explicit LineTo we just created,      */
+      /* above) and connect it to the start point, but with the offset we */
+      /* saved from the first element.                                    */
+      /* Use the saved HintMap, too. */
+      FT_ASSERT( glyphpath->elemIsQueued );
+
+      cf2_glyphpath_pushPrevElem( glyphpath,
+                                  &glyphpath->firstHintMap,
+                                  &glyphpath->offsetStart0,
+                                  glyphpath->offsetStart1,
+                                  TRUE );
+
+      /* reset state machine */
+      glyphpath->moveIsPending = TRUE;
+      glyphpath->pathIsOpen    = FALSE;
+      glyphpath->elemIsQueued  = FALSE;
+    }
+  }
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2hints.h
@@ -1,0 +1,287 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2hints.h                                                             */
+/*                                                                         */
+/*    Adobe's code for handling CFF hints (body).                          */
+/*                                                                         */
+/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __CF2HINTS_H__
+#define __CF2HINTS_H__
+
+
+FT_BEGIN_HEADER
+
+
+  enum
+  {
+    CF2_MAX_HINTS = 96    /* maximum # of hints */
+  };
+
+
+  /*
+   * A HintMask object stores a bit mask that specifies which hints in the
+   * charstring are active at a given time.  Hints in CFF must be declared
+   * at the start, before any drawing operators, with horizontal hints
+   * preceding vertical hints.  The HintMask is ordered the same way, with
+   * horizontal hints immediately followed by vertical hints.  Clients are
+   * responsible for knowing how many of each type are present.
+   *
+   * The maximum total number of hints is 96, as specified by the CFF
+   * specification.
+   *
+   * A HintMask is built 0 or more times while interpreting a charstring, by
+   * the HintMask operator.  There is only one HintMask, but it is built or
+   * rebuilt each time there is a hint substitution (HintMask operator) in
+   * the charstring.  A default HintMask with all bits set is built if there
+   * has been no HintMask operator prior to the first drawing operator.
+   *
+   */
+
+  typedef struct  CF2_HintMaskRec_
+  {
+    FT_Error*  error;
+
+    FT_Bool  isValid;
+    FT_Bool  isNew;
+
+    size_t  bitCount;
+    size_t  byteCount;
+
+    FT_Byte  mask[( CF2_MAX_HINTS + 7 ) / 8];
+
+  } CF2_HintMaskRec, *CF2_HintMask;
+
+
+  typedef struct  CF2_StemHintRec_
+  {
+    FT_Bool  used;     /* DS positions are valid         */
+
+    CF2_Fixed  min;    /* original character space value */
+    CF2_Fixed  max;
+
+    CF2_Fixed  minDS;  /* DS position after first use    */
+    CF2_Fixed  maxDS;
+
+  } CF2_StemHintRec, *CF2_StemHint;
+
+
+  /*
+   * A HintMap object stores a piecewise linear function for mapping
+   * y-coordinates from character space to device space, providing
+   * appropriate pixel alignment to stem edges.
+   *
+   * The map is implemented as an array of `CF2_Hint' elements, each
+   * representing an edge.  When edges are paired, as from stem hints, the
+   * bottom edge must immediately precede the top edge in the array.
+   * Element character space AND device space positions must both increase
+   * monotonically in the array.  `CF2_Hint' elements are also used as
+   * parameters to `cf2_blues_capture'.
+   *
+   * The `cf2_hintmap_build' method must be called before any drawing
+   * operation (beginning with a Move operator) and at each hint
+   * substitution (HintMask operator).
+   *
+   * The `cf2_hintmap_map' method is called to transform y-coordinates at
+   * each drawing operation (move, line, curve).
+   *
+   */
+
+  /* TODO: make this a CF2_ArrStack and add a deep copy method */
+  enum
+  {
+    CF2_MAX_HINT_EDGES = CF2_MAX_HINTS * 2
+  };
+
+
+  typedef struct  CF2_HintMapRec_
+  {
+    CF2_Font  font;
+
+    /* initial map based on blue zones */
+    struct CF2_HintMapRec_*  initialHintMap;
+
+    /* working storage for 2nd pass adjustHints */
+    CF2_ArrStack  hintMoves;
+
+    FT_Bool  isValid;
+    FT_Bool  hinted;
+
+    CF2_Fixed  scale;
+    CF2_UInt   count;
+
+    /* start search from this index */
+    CF2_UInt  lastIndex;
+
+    CF2_HintRec  edge[CF2_MAX_HINT_EDGES]; /* 192 */
+
+  } CF2_HintMapRec, *CF2_HintMap;
+
+
+  FT_LOCAL( FT_Bool )
+  cf2_hint_isValid( const CF2_Hint  hint );
+  FT_LOCAL( FT_Bool )
+  cf2_hint_isTop( const CF2_Hint  hint );
+  FT_LOCAL( FT_Bool )
+  cf2_hint_isBottom( const CF2_Hint  hint );
+  FT_LOCAL( void )
+  cf2_hint_lock( CF2_Hint  hint );
+
+
+  FT_LOCAL( void )
+  cf2_hintmap_init( CF2_HintMap   hintmap,
+                    CF2_Font      font,
+                    CF2_HintMap   initialMap,
+                    CF2_ArrStack  hintMoves,
+                    CF2_Fixed     scale );
+  FT_LOCAL( void )
+  cf2_hintmap_build( CF2_HintMap   hintmap,
+                     CF2_ArrStack  hStemHintArray,
+                     CF2_ArrStack  vStemHintArray,
+                     CF2_HintMask  hintMask,
+                     CF2_Fixed     hintOrigin,
+                     FT_Bool       initialMap );
+
+
+  /*
+   * GlyphPath is a wrapper for drawing operations that scales the
+   * coordinates according to the render matrix and HintMap.  It also tracks
+   * open paths to control ClosePath and to insert MoveTo for broken fonts.
+   *
+   */
+  typedef struct  CF2_GlyphPathRec_
+  {
+    /* TODO: gather some of these into a hinting context */
+
+    CF2_Font              font;           /* font instance    */
+    CF2_OutlineCallbacks  callbacks;      /* outline consumer */
+
+
+    CF2_HintMapRec  hintMap;        /* current hint map            */
+    CF2_HintMapRec  firstHintMap;   /* saved copy                  */
+    CF2_HintMapRec  initialHintMap; /* based on all captured hints */
+
+    CF2_ArrStackRec  hintMoves;  /* list of hint moves for 2nd pass */
+
+    CF2_Fixed  scaleX;         /* matrix a */
+    CF2_Fixed  scaleC;         /* matrix c */
+    CF2_Fixed  scaleY;         /* matrix d */
+
+    FT_Vector  fractionalTranslation;  /* including deviceXScale */
+#if 0
+    CF2_Fixed  hShift;    /* character space horizontal shift */
+                          /* (for fauxing)                    */
+#endif
+
+    FT_Bool  pathIsOpen;     /* true after MoveTo                     */
+    FT_Bool  darken;         /* true if stem darkening                */
+    FT_Bool  moveIsPending;  /* true between MoveTo and offset MoveTo */
+
+    /* references used to call `cf2_hintmap_build', if necessary */
+    CF2_ArrStack         hStemHintArray;
+    CF2_ArrStack         vStemHintArray;
+    CF2_HintMask         hintMask;     /* ptr to the current mask */
+    CF2_Fixed            hintOriginY;  /* copy of current origin  */
+    const CF2_BluesRec*  blues;
+
+    CF2_Fixed  xOffset;        /* character space offsets */
+    CF2_Fixed  yOffset;
+
+    /* character space miter limit threshold */
+    CF2_Fixed  miterLimit;
+    /* vertical/horzizontal snap distance in character space */
+    CF2_Fixed  snapThreshold;
+
+    FT_Vector  offsetStart0;  /* first and second points of first */
+    FT_Vector  offsetStart1;  /* element with offset applied      */
+
+    /* current point, character space, before offset */
+    FT_Vector  currentCS;
+    /* current point, device space */
+    FT_Vector  currentDS;
+    FT_Vector  start;         /* start point of subpath */
+
+    /* the following members constitute the `queue' of one element */
+    FT_Bool  elemIsQueued;
+    CF2_Int  prevElemOp;
+
+    FT_Vector  prevElemP0;
+    FT_Vector  prevElemP1;
+    FT_Vector  prevElemP2;
+    FT_Vector  prevElemP3;
+
+  } CF2_GlyphPathRec, *CF2_GlyphPath;
+
+
+  FT_LOCAL( void )
+  cf2_glyphpath_init( CF2_GlyphPath         glyphpath,
+                      CF2_Font              font,
+                      CF2_OutlineCallbacks  callbacks,
+                      CF2_Fixed             scaleY,
+                      /* CF2_Fixed hShift, */
+                      CF2_ArrStack          hStemHintArray,
+                      CF2_ArrStack          vStemHintArray,
+                      CF2_HintMask          hintMask,
+                      CF2_Fixed             hintOrigin,
+                      const CF2_Blues       blues,
+                      const FT_Vector*      fractionalTranslation );
+  FT_LOCAL( void )
+  cf2_glyphpath_finalize( CF2_GlyphPath  glyphpath );
+
+  FT_LOCAL( void )
+  cf2_glyphpath_moveTo( CF2_GlyphPath  glyphpath,
+                        CF2_Fixed      x,
+                        CF2_Fixed      y );
+  FT_LOCAL( void )
+  cf2_glyphpath_lineTo( CF2_GlyphPath  glyphpath,
+                        CF2_Fixed      x,
+                        CF2_Fixed      y );
+  FT_LOCAL( void )
+  cf2_glyphpath_curveTo( CF2_GlyphPath  glyphpath,
+                         CF2_Fixed      x1,
+                         CF2_Fixed      y1,
+                         CF2_Fixed      x2,
+                         CF2_Fixed      y2,
+                         CF2_Fixed      x3,
+                         CF2_Fixed      y3 );
+  FT_LOCAL( void )
+  cf2_glyphpath_closeOpenPath( CF2_GlyphPath  glyphpath );
+
+
+FT_END_HEADER
+
+
+#endif /* __CF2HINTS_H__ */
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2intrp.c
@@ -1,0 +1,1493 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2intrp.c                                                             */
+/*                                                                         */
+/*    Adobe's CFF Interpreter (body).                                      */
+/*                                                                         */
+/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#include "cf2ft.h"
+#include FT_INTERNAL_DEBUG_H
+
+#include "cf2glue.h"
+#include "cf2font.h"
+#include "cf2stack.h"
+#include "cf2hints.h"
+
+#include "cf2error.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  trace_cf2interp
+
+
+  /* some operators are not implemented yet */
+#define CF2_FIXME  FT_TRACE4(( "cf2_interpT2CharString:"            \
+                               " operator not implemented yet\n" ))
+
+
+
+  FT_LOCAL_DEF( void )
+  cf2_hintmask_init( CF2_HintMask  hintmask,
+                     FT_Error*     error )
+  {
+    FT_ZERO( hintmask );
+
+    hintmask->error = error;
+  }
+
+
+  FT_LOCAL_DEF( FT_Bool )
+  cf2_hintmask_isValid( const CF2_HintMask  hintmask )
+  {
+    return hintmask->isValid;
+  }
+
+
+  FT_LOCAL_DEF( FT_Bool )
+  cf2_hintmask_isNew( const CF2_HintMask  hintmask )
+  {
+    return hintmask->isNew;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_hintmask_setNew( CF2_HintMask  hintmask,
+                       FT_Bool       val )
+  {
+    hintmask->isNew = val;
+  }
+
+
+  /* clients call `getMaskPtr' in order to iterate */
+  /* through hint mask                             */
+
+  FT_LOCAL_DEF( FT_Byte* )
+  cf2_hintmask_getMaskPtr( CF2_HintMask  hintmask )
+  {
+    return hintmask->mask;
+  }
+
+
+  static size_t
+  cf2_hintmask_setCounts( CF2_HintMask  hintmask,
+                          size_t        bitCount )
+  {
+    if ( bitCount > CF2_MAX_HINTS )
+    {
+      /* total of h and v stems must be <= 96 */
+      CF2_SET_ERROR( hintmask->error, Invalid_Glyph_Format );
+      return 0;
+    }
+
+    hintmask->bitCount  = bitCount;
+    hintmask->byteCount = ( hintmask->bitCount + 7 ) / 8;
+
+    hintmask->isValid = TRUE;
+    hintmask->isNew   = TRUE;
+
+    return bitCount;
+  }
+
+
+  /* consume the hintmask bytes from the charstring, advancing the src */
+  /* pointer                                                           */
+  static void
+  cf2_hintmask_read( CF2_HintMask  hintmask,
+                     CF2_Buffer    charstring,
+                     size_t        bitCount )
+  {
+    size_t  i;
+
+#ifndef CF2_NDEBUG
+    /* these are the bits in the final mask byte that should be zero  */
+    /* Note: this variable is only used in an assert expression below */
+    /* and then only if CF2_NDEBUG is not defined                     */
+    CF2_UInt  mask = ( 1 << ( -(CF2_Int)bitCount & 7 ) ) - 1;
+#endif
+
+
+    /* initialize counts and isValid */
+    if ( cf2_hintmask_setCounts( hintmask, bitCount ) == 0 )
+      return;
+
+    FT_ASSERT( hintmask->byteCount > 0 );
+
+    FT_TRACE4(( " (maskbytes:" ));
+
+    /* set mask and advance interpreter's charstring pointer */
+    for ( i = 0; i < hintmask->byteCount; i++ )
+    {
+      hintmask->mask[i] = (FT_Byte)cf2_buf_readByte( charstring );
+      FT_TRACE4(( " 0x%02X", hintmask->mask[i] ));
+    }
+
+    FT_TRACE4(( ")\n" ));
+
+    /* assert any unused bits in last byte are zero unless there's a prior */
+    /* error                                                               */
+    /* bitCount -> mask, 0 -> 0, 1 -> 7f, 2 -> 3f, ... 6 -> 3, 7 -> 1      */
+#ifndef CF2_NDEBUG
+    FT_ASSERT( ( hintmask->mask[hintmask->byteCount - 1] & mask ) == 0 ||
+               *hintmask->error                                        );
+#endif
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_hintmask_setAll( CF2_HintMask  hintmask,
+                       size_t        bitCount )
+  {
+    size_t    i;
+    CF2_UInt  mask = ( 1 << ( -(CF2_Int)bitCount & 7 ) ) - 1;
+
+
+    /* initialize counts and isValid */
+    if ( cf2_hintmask_setCounts( hintmask, bitCount ) == 0 )
+      return;
+
+    FT_ASSERT( hintmask->byteCount > 0 );
+    FT_ASSERT( hintmask->byteCount <
+                 sizeof ( hintmask->mask ) / sizeof ( hintmask->mask[0] ) );
+
+    /* set mask to all ones */
+    for ( i = 0; i < hintmask->byteCount; i++ )
+      hintmask->mask[i] = 0xFF;
+
+    /* clear unused bits                                              */
+    /* bitCount -> mask, 0 -> 0, 1 -> 7f, 2 -> 3f, ... 6 -> 3, 7 -> 1 */
+    hintmask->mask[hintmask->byteCount - 1] &= ~mask;
+  }
+
+
+  /* Type2 charstring opcodes */
+  enum
+  {
+    cf2_cmdRESERVED_0,   /* 0 */
+    cf2_cmdHSTEM,        /* 1 */
+    cf2_cmdRESERVED_2,   /* 2 */
+    cf2_cmdVSTEM,        /* 3 */
+    cf2_cmdVMOVETO,      /* 4 */
+    cf2_cmdRLINETO,      /* 5 */
+    cf2_cmdHLINETO,      /* 6 */
+    cf2_cmdVLINETO,      /* 7 */
+    cf2_cmdRRCURVETO,    /* 8 */
+    cf2_cmdRESERVED_9,   /* 9 */
+    cf2_cmdCALLSUBR,     /* 10 */
+    cf2_cmdRETURN,       /* 11 */
+    cf2_cmdESC,          /* 12 */
+    cf2_cmdRESERVED_13,  /* 13 */
+    cf2_cmdENDCHAR,      /* 14 */
+    cf2_cmdRESERVED_15,  /* 15 */
+    cf2_cmdRESERVED_16,  /* 16 */
+    cf2_cmdRESERVED_17,  /* 17 */
+    cf2_cmdHSTEMHM,      /* 18 */
+    cf2_cmdHINTMASK,     /* 19 */
+    cf2_cmdCNTRMASK,     /* 20 */
+    cf2_cmdRMOVETO,      /* 21 */
+    cf2_cmdHMOVETO,      /* 22 */
+    cf2_cmdVSTEMHM,      /* 23 */
+    cf2_cmdRCURVELINE,   /* 24 */
+    cf2_cmdRLINECURVE,   /* 25 */
+    cf2_cmdVVCURVETO,    /* 26 */
+    cf2_cmdHHCURVETO,    /* 27 */
+    cf2_cmdEXTENDEDNMBR, /* 28 */
+    cf2_cmdCALLGSUBR,    /* 29 */
+    cf2_cmdVHCURVETO,    /* 30 */
+    cf2_cmdHVCURVETO     /* 31 */
+  };
+
+  enum
+  {
+    cf2_escDOTSECTION,   /* 0 */
+    cf2_escRESERVED_1,   /* 1 */
+    cf2_escRESERVED_2,   /* 2 */
+    cf2_escAND,          /* 3 */
+    cf2_escOR,           /* 4 */
+    cf2_escNOT,          /* 5 */
+    cf2_escRESERVED_6,   /* 6 */
+    cf2_escRESERVED_7,   /* 7 */
+    cf2_escRESERVED_8,   /* 8 */
+    cf2_escABS,          /* 9 */
+    cf2_escADD,          /* 10     like otherADD */
+    cf2_escSUB,          /* 11     like otherSUB */
+    cf2_escDIV,          /* 12 */
+    cf2_escRESERVED_13,  /* 13 */
+    cf2_escNEG,          /* 14 */
+    cf2_escEQ,           /* 15 */
+    cf2_escRESERVED_16,  /* 16 */
+    cf2_escRESERVED_17,  /* 17 */
+    cf2_escDROP,         /* 18 */
+    cf2_escRESERVED_19,  /* 19 */
+    cf2_escPUT,          /* 20     like otherPUT    */
+    cf2_escGET,          /* 21     like otherGET    */
+    cf2_escIFELSE,       /* 22     like otherIFELSE */
+    cf2_escRANDOM,       /* 23     like otherRANDOM */
+    cf2_escMUL,          /* 24     like otherMUL    */
+    cf2_escRESERVED_25,  /* 25 */
+    cf2_escSQRT,         /* 26 */
+    cf2_escDUP,          /* 27     like otherDUP    */
+    cf2_escEXCH,         /* 28     like otherEXCH   */
+    cf2_escINDEX,        /* 29 */
+    cf2_escROLL,         /* 30 */
+    cf2_escRESERVED_31,  /* 31 */
+    cf2_escRESERVED_32,  /* 32 */
+    cf2_escRESERVED_33,  /* 33 */
+    cf2_escHFLEX,        /* 34 */
+    cf2_escFLEX,         /* 35 */
+    cf2_escHFLEX1,       /* 36 */
+    cf2_escFLEX1         /* 37 */
+  };
+
+
+  /* `stemHintArray' does not change once we start drawing the outline. */
+  static void
+  cf2_doStems( const CF2_Font  font,
+               CF2_Stack       opStack,
+               CF2_ArrStack    stemHintArray,
+               CF2_Fixed*      width,
+               FT_Bool*        haveWidth,
+               CF2_Fixed       hintOffset )
+  {
+    CF2_UInt  i;
+    CF2_UInt  count       = cf2_stack_count( opStack );
+    FT_Bool   hasWidthArg = count & 1;
+
+    /* variable accumulates delta values from operand stack */
+    CF2_Fixed  position = hintOffset;
+
+
+    for ( i = hasWidthArg ? 1 : 0; i < count; i += 2 )
+    {
+      /* construct a CF2_StemHint and push it onto the list */
+      CF2_StemHintRec  stemhint;
+
+
+      stemhint.min  =
+      position     += cf2_stack_getReal( opStack, i );
+      stemhint.max  =
+      position     += cf2_stack_getReal( opStack, i + 1 );
+
+      stemhint.used  = FALSE;
+      stemhint.maxDS =
+      stemhint.minDS = 0;
+
+      cf2_arrstack_push( stemHintArray, &stemhint ); /* defer error check */
+    }
+
+    if ( hasWidthArg && ! *haveWidth )
+      *width = cf2_stack_getReal( opStack, 0 ) +
+                 cf2_getNominalWidthX( font->decoder );
+
+    *haveWidth = TRUE;
+
+    cf2_stack_clear( opStack );
+  }
+
+
+  static void
+  cf2_doFlex( CF2_Stack       opStack,
+              CF2_Fixed*      curX,
+              CF2_Fixed*      curY,
+              CF2_GlyphPath   glyphPath,
+              const FT_Bool*  readFromStack,
+              FT_Bool         doConditionalLastRead )
+  {
+    CF2_Fixed  vals[14];
+    CF2_UInt   index;
+    FT_Bool    isHFlex;
+    CF2_Int    top, i, j;
+
+
+    vals[0] = *curX;
+    vals[1] = *curY;
+    index   = 0;
+    isHFlex = readFromStack[9] == FALSE;
+    top     = isHFlex ? 9 : 10;
+
+    for ( i = 0; i < top; i++ )
+    {
+      vals[i + 2] = vals[i];
+      if ( readFromStack[i] )
+        vals[i + 2] += cf2_stack_getReal( opStack, index++ );
+    }
+
+    if ( isHFlex )
+      vals[9 + 2] = *curY;
+
+    if ( doConditionalLastRead )
+    {
+      FT_Bool    lastIsX = cf2_fixedAbs( vals[10] - *curX ) >
+                             cf2_fixedAbs( vals[11] - *curY );
+      CF2_Fixed  lastVal = cf2_stack_getReal( opStack, index );
+
+
+      if ( lastIsX )
+      {
+        vals[12] = vals[10] + lastVal;
+        vals[13] = *curY;
+      }
+      else
+      {
+        vals[12] = *curX;
+        vals[13] = vals[11] + lastVal;
+      }
+    }
+    else
+    {
+      if ( readFromStack[10] )
+        vals[12] = vals[10] + cf2_stack_getReal( opStack, index++ );
+      else
+        vals[12] = *curX;
+
+      if ( readFromStack[11] )
+        vals[13] = vals[11] + cf2_stack_getReal( opStack, index );
+      else
+        vals[13] = *curY;
+    }
+
+    for ( j = 0; j < 2; j++ )
+      cf2_glyphpath_curveTo( glyphPath, vals[j * 6 + 2],
+                                        vals[j * 6 + 3],
+                                        vals[j * 6 + 4],
+                                        vals[j * 6 + 5],
+                                        vals[j * 6 + 6],
+                                        vals[j * 6 + 7] );
+
+    cf2_stack_clear( opStack );
+
+    *curX = vals[12];
+    *curY = vals[13];
+  }
+
+
+  /*
+   * `error' is a shared error code used by many objects in this
+   * routine.  Before the code continues from an error, it must check and
+   * record the error in `*error'.  The idea is that this shared
+   * error code will record the first error encountered.  If testing
+   * for an error anyway, the cost of `goto exit' is small, so we do it,
+   * even if continuing would be safe.  In this case, `lastError' is
+   * set, so the testing and storing can be done in one place, at `exit'.
+   *
+   * Continuing after an error is intended for objects which do their own
+   * testing of `*error', e.g., array stack functions.  This allows us to
+   * avoid an extra test after the call.
+   *
+   * Unimplemented opcodes are ignored.
+   *
+   */
+  FT_LOCAL_DEF( void )
+  cf2_interpT2CharString( CF2_Font              font,
+                          CF2_Buffer            buf,
+                          CF2_OutlineCallbacks  callbacks,
+                          const FT_Vector*      translation,
+                          FT_Bool               doingSeac,
+                          CF2_Fixed             curX,
+                          CF2_Fixed             curY,
+                          CF2_Fixed*            width )
+  {
+    /* lastError is used for errors that are immediately tested */
+    FT_Error  lastError = FT_Err_Ok;
+
+    /* pointer to parsed font object */
+    CFF_Decoder*  decoder = font->decoder;
+
+    FT_Error*  error  = &font->error;
+    FT_Memory  memory = font->memory;
+
+    CF2_Fixed  scaleY        = font->innerTransform.d;
+    CF2_Fixed  nominalWidthX = cf2_getNominalWidthX( decoder );
+
+    /* save this for hinting seac accents */
+    CF2_Fixed  hintOriginY = curY;
+
+    CF2_Stack  opStack = NULL;
+    FT_Byte    op1;                       /* first opcode byte */
+
+    /* instruction limit; 20,000,000 matches Avalon */
+    FT_UInt32  instructionLimit = 20000000UL;
+
+    CF2_ArrStackRec  subrStack;
+
+    FT_Bool     haveWidth;
+    CF2_Buffer  charstring = NULL;
+
+    CF2_Int  charstringIndex = -1;       /* initialize to empty */
+
+    /* TODO: placeholders for hint structures */
+
+    /* objects used for hinting */
+    CF2_ArrStackRec  hStemHintArray;
+    CF2_ArrStackRec  vStemHintArray;
+
+    CF2_HintMaskRec   hintMask;
+    CF2_GlyphPathRec  glyphPath;
+
+
+    /* initialize the remaining objects */
+    cf2_arrstack_init( &subrStack,
+                       memory,
+                       error,
+                       sizeof ( CF2_BufferRec ) );
+    cf2_arrstack_init( &hStemHintArray,
+                       memory,
+                       error,
+                       sizeof ( CF2_StemHintRec ) );
+    cf2_arrstack_init( &vStemHintArray,
+                       memory,
+                       error,
+                       sizeof ( CF2_StemHintRec ) );
+
+    /* initialize CF2_StemHint arrays */
+    cf2_hintmask_init( &hintMask, error );
+
+    /* initialize path map to manage drawing operations */
+
+    /* Note: last 4 params are used to handle `MoveToPermissive', which */
+    /*       may need to call `hintMap.Build'                           */
+    /* TODO: MoveToPermissive is gone; are these still needed?          */
+    cf2_glyphpath_init( &glyphPath,
+                        font,
+                        callbacks,
+                        scaleY,
+                        /* hShift, */
+                        &hStemHintArray,
+                        &vStemHintArray,
+                        &hintMask,
+                        hintOriginY,
+                        &font->blues,
+                        translation );
+
+    /*
+     * Initialize state for width parsing.  From the CFF Spec:
+     *
+     *   The first stack-clearing operator, which must be one of hstem,
+     *   hstemhm, vstem, vstemhm, cntrmask, hintmask, hmoveto, vmoveto,
+     *   rmoveto, or endchar, takes an additional argument - the width (as
+     *   described earlier), which may be expressed as zero or one numeric
+     *   argument.
+     *
+     * What we implement here uses the first validly specified width, but
+     * does not detect errors for specifying more than one width.
+     *
+     */
+    haveWidth = FALSE;
+    *width    = cf2_getDefaultWidthX( decoder );
+
+    /*
+     * Note: at this point, all pointers to resources must be NULL
+     * and all local objects must be initialized.
+     * There must be no branches to exit: above this point.
+     *
+     */
+
+    /* allocate an operand stack */
+    opStack = cf2_stack_init( memory, error );
+    if ( !opStack )
+    {
+      lastError = FT_THROW( Out_Of_Memory );
+      goto exit;
+    }
+
+    /* initialize subroutine stack by placing top level charstring as */
+    /* first element (max depth plus one for the charstring)          */
+    /* Note: Caller owns and must finalize the first charstring.      */
+    /*       Our copy of it does not change that requirement.         */
+    cf2_arrstack_setCount( &subrStack, CF2_MAX_SUBR + 1 );
+
+    charstring  = (CF2_Buffer)cf2_arrstack_getBuffer( &subrStack );
+    *charstring = *buf;    /* structure copy */
+
+    charstringIndex = 0;       /* entry is valid now */
+
+    /* catch errors so far */
+    if ( *error )
+      goto exit;
+
+    /* main interpreter loop */
+    while ( 1 )
+    {
+      if ( cf2_buf_isEnd( charstring ) )
+      {
+        /* If we've reached the end of the charstring, simulate a */
+        /* cf2_cmdRETURN or cf2_cmdENDCHAR.                       */
+        if ( charstringIndex )
+          op1 = cf2_cmdRETURN;  /* end of buffer for subroutine */
+        else
+          op1 = cf2_cmdENDCHAR; /* end of buffer for top level charstring */
+      }
+      else
+        op1 = (FT_Byte)cf2_buf_readByte( charstring );
+
+      /* check for errors once per loop */
+      if ( *error )
+        goto exit;
+
+      instructionLimit--;
+      if ( instructionLimit == 0 )
+      {
+        lastError = FT_THROW( Invalid_Glyph_Format );
+        goto exit;
+      }
+
+      switch( op1 )
+      {
+      case cf2_cmdRESERVED_0:
+      case cf2_cmdRESERVED_2:
+      case cf2_cmdRESERVED_9:
+      case cf2_cmdRESERVED_13:
+      case cf2_cmdRESERVED_15:
+      case cf2_cmdRESERVED_16:
+      case cf2_cmdRESERVED_17:
+        /* we may get here if we have a prior error */
+        FT_TRACE4(( " unknown op (%d)\n", op1 ));
+        break;
+
+      case cf2_cmdHSTEMHM:
+      case cf2_cmdHSTEM:
+        FT_TRACE4(( op1 == cf2_cmdHSTEMHM ? " hstemhm\n" : " hstem\n" ));
+
+        /* never add hints after the mask is computed */
+        if ( cf2_hintmask_isValid( &hintMask ) )
+          FT_TRACE4(( "cf2_interpT2CharString:"
+                      " invalid horizontal hint mask\n" ));
+
+        cf2_doStems( font,
+                     opStack,
+                     &hStemHintArray,
+                     width,
+                     &haveWidth,
+                     0 );
+        break;
+
+      case cf2_cmdVSTEMHM:
+      case cf2_cmdVSTEM:
+        FT_TRACE4(( op1 == cf2_cmdVSTEMHM ? " vstemhm\n" : " vstem\n" ));
+
+        /* never add hints after the mask is computed */
+        if ( cf2_hintmask_isValid( &hintMask ) )
+          FT_TRACE4(( "cf2_interpT2CharString:"
+                      " invalid vertical hint mask\n" ));
+
+        cf2_doStems( font,
+                     opStack,
+                     &vStemHintArray,
+                     width,
+                     &haveWidth,
+                     0 );
+        break;
+
+      case cf2_cmdVMOVETO:
+        FT_TRACE4(( " vmoveto\n" ));
+
+        curY += cf2_stack_popFixed( opStack );
+
+        cf2_glyphpath_moveTo( &glyphPath, curX, curY );
+
+        if ( cf2_stack_count( opStack ) > 0 && !haveWidth )
+          *width = cf2_stack_popFixed( opStack ) + nominalWidthX;
+
+        haveWidth = TRUE;
+        break;
+
+      case cf2_cmdRLINETO:
+        {
+          CF2_UInt  index;
+          CF2_UInt  count = cf2_stack_count( opStack );
+
+
+          FT_TRACE4(( " rlineto\n" ));
+
+          for ( index = 0; index < count; index += 2 )
+          {
+            curX += cf2_stack_getReal( opStack, index + 0 );
+            curY += cf2_stack_getReal( opStack, index + 1 );
+
+            cf2_glyphpath_lineTo( &glyphPath, curX, curY );
+          }
+
+          cf2_stack_clear( opStack );
+        }
+        continue; /* no need to clear stack again */
+
+      case cf2_cmdHLINETO:
+      case cf2_cmdVLINETO:
+        {
+          CF2_UInt  index;
+          CF2_UInt  count = cf2_stack_count( opStack );
+
+          FT_Bool  isX = op1 == cf2_cmdHLINETO;
+
+
+          FT_TRACE4(( isX ? " hlineto\n" : " vlineto\n" ));
+
+          for ( index = 0; index < count; index++ )
+          {
+            CF2_Fixed  v = cf2_stack_getReal( opStack, index );
+
+
+            if ( isX )
+              curX += v;
+            else
+              curY += v;
+
+            isX = !isX;
+
+            cf2_glyphpath_lineTo( &glyphPath, curX, curY );
+          }
+
+          cf2_stack_clear( opStack );
+        }
+        continue;
+
+      case cf2_cmdRCURVELINE:
+      case cf2_cmdRRCURVETO:
+        {
+          CF2_UInt  count = cf2_stack_count( opStack );
+          CF2_UInt  index = 0;
+
+
+          FT_TRACE4(( op1 == cf2_cmdRCURVELINE ? " rcurveline\n"
+                                               : " rrcurveto\n" ));
+
+          while ( index + 6 <= count )
+          {
+            CF2_Fixed  x1 = cf2_stack_getReal( opStack, index + 0 ) + curX;
+            CF2_Fixed  y1 = cf2_stack_getReal( opStack, index + 1 ) + curY;
+            CF2_Fixed  x2 = cf2_stack_getReal( opStack, index + 2 ) + x1;
+            CF2_Fixed  y2 = cf2_stack_getReal( opStack, index + 3 ) + y1;
+            CF2_Fixed  x3 = cf2_stack_getReal( opStack, index + 4 ) + x2;
+            CF2_Fixed  y3 = cf2_stack_getReal( opStack, index + 5 ) + y2;
+
+
+            cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+            curX   = x3;
+            curY   = y3;
+            index += 6;
+          }
+
+          if ( op1 == cf2_cmdRCURVELINE )
+          {
+            curX += cf2_stack_getReal( opStack, index + 0 );
+            curY += cf2_stack_getReal( opStack, index + 1 );
+
+            cf2_glyphpath_lineTo( &glyphPath, curX, curY );
+          }
+
+          cf2_stack_clear( opStack );
+        }
+        continue; /* no need to clear stack again */
+
+      case cf2_cmdCALLGSUBR:
+      case cf2_cmdCALLSUBR:
+        {
+          CF2_UInt  subrIndex;
+
+
+          FT_TRACE4(( op1 == cf2_cmdCALLGSUBR ? " callgsubr"
+                                              : " callsubr" ));
+
+          if ( charstringIndex > CF2_MAX_SUBR )
+          {
+            /* max subr plus one for charstring */
+            lastError = FT_THROW( Invalid_Glyph_Format );
+            goto exit;                      /* overflow of stack */
+          }
+
+          /* push our current CFF charstring region on subrStack */
+          charstring = (CF2_Buffer)
+                         cf2_arrstack_getPointer( &subrStack,
+                                                  charstringIndex + 1 );
+
+          /* set up the new CFF region and pointer */
+          subrIndex = cf2_stack_popInt( opStack );
+
+          switch ( op1 )
+          {
+          case cf2_cmdCALLGSUBR:
+            FT_TRACE4(( "(%d)\n", subrIndex + decoder->globals_bias ));
+
+            if ( cf2_initGlobalRegionBuffer( decoder,
+                                             subrIndex,
+                                             charstring ) )
+            {
+              lastError = FT_THROW( Invalid_Glyph_Format );
+              goto exit;  /* subroutine lookup or stream error */
+            }
+            break;
+
+          default:
+            /* cf2_cmdCALLSUBR */
+            FT_TRACE4(( "(%d)\n", subrIndex + decoder->locals_bias ));
+
+            if ( cf2_initLocalRegionBuffer( decoder,
+                                            subrIndex,
+                                            charstring ) )
+            {
+              lastError = FT_THROW( Invalid_Glyph_Format );
+              goto exit;  /* subroutine lookup or stream error */
+            }
+          }
+
+          charstringIndex += 1;       /* entry is valid now */
+        }
+        continue; /* do not clear the stack */
+
+      case cf2_cmdRETURN:
+        FT_TRACE4(( " return\n" ));
+
+        if ( charstringIndex < 1 )
+        {
+          /* Note: cannot return from top charstring */
+          lastError = FT_THROW( Invalid_Glyph_Format );
+          goto exit;                      /* underflow of stack */
+        }
+
+        /* restore position in previous charstring */
+        charstring = (CF2_Buffer)
+                       cf2_arrstack_getPointer( &subrStack,
+                                                --charstringIndex );
+        continue;     /* do not clear the stack */
+
+      case cf2_cmdESC:
+        {
+          FT_Byte  op2 = cf2_buf_readByte( charstring );
+
+
+          switch ( op2 )
+          {
+          case cf2_escDOTSECTION:
+            /* something about `flip type of locking' -- ignore it */
+            FT_TRACE4(( " dotsection\n" ));
+
+            break;
+
+          /* TODO: should these operators be supported? */
+          case cf2_escAND: /* in spec */
+            FT_TRACE4(( " and\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escOR: /* in spec */
+            FT_TRACE4(( " or\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escNOT: /* in spec */
+            FT_TRACE4(( " not\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escABS: /* in spec */
+            FT_TRACE4(( " abs\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escADD: /* in spec */
+            FT_TRACE4(( " add\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escSUB: /* in spec */
+            FT_TRACE4(( " sub\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escDIV: /* in spec */
+            FT_TRACE4(( " div\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escNEG: /* in spec */
+            FT_TRACE4(( " neg\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escEQ: /* in spec */
+            FT_TRACE4(( " eq\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escDROP: /* in spec */
+            FT_TRACE4(( " drop\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escPUT: /* in spec */
+            FT_TRACE4(( " put\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escGET: /* in spec */
+            FT_TRACE4(( " get\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escIFELSE: /* in spec */
+            FT_TRACE4(( " ifelse\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escRANDOM: /* in spec */
+            FT_TRACE4(( " random\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escMUL: /* in spec */
+            FT_TRACE4(( " mul\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escSQRT: /* in spec */
+            FT_TRACE4(( " sqrt\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escDUP: /* in spec */
+            FT_TRACE4(( " dup\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escEXCH: /* in spec */
+            FT_TRACE4(( " exch\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escINDEX: /* in spec */
+            FT_TRACE4(( " index\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escROLL: /* in spec */
+            FT_TRACE4(( " roll\n" ));
+
+            CF2_FIXME;
+            break;
+
+          case cf2_escHFLEX:
+            {
+              static const FT_Bool  readFromStack[12] =
+              {
+                TRUE /* dx1 */, FALSE /* dy1 */,
+                TRUE /* dx2 */, TRUE  /* dy2 */,
+                TRUE /* dx3 */, FALSE /* dy3 */,
+                TRUE /* dx4 */, FALSE /* dy4 */,
+                TRUE /* dx5 */, FALSE /* dy5 */,
+                TRUE /* dx6 */, FALSE /* dy6 */
+              };
+
+
+              FT_TRACE4(( " hflex\n" ));
+
+              cf2_doFlex( opStack,
+                          &curX,
+                          &curY,
+                          &glyphPath,
+                          readFromStack,
+                          FALSE /* doConditionalLastRead */ );
+            }
+            continue;
+
+          case cf2_escFLEX:
+            {
+              static const FT_Bool  readFromStack[12] =
+              {
+                TRUE /* dx1 */, TRUE /* dy1 */,
+                TRUE /* dx2 */, TRUE /* dy2 */,
+                TRUE /* dx3 */, TRUE /* dy3 */,
+                TRUE /* dx4 */, TRUE /* dy4 */,
+                TRUE /* dx5 */, TRUE /* dy5 */,
+                TRUE /* dx6 */, TRUE /* dy6 */
+              };
+
+
+              FT_TRACE4(( " flex\n" ));
+
+              cf2_doFlex( opStack,
+                          &curX,
+                          &curY,
+                          &glyphPath,
+                          readFromStack,
+                          FALSE /* doConditionalLastRead */ );
+            }
+            break;      /* TODO: why is this not a continue? */
+
+          case cf2_escHFLEX1:
+            {
+              static const FT_Bool  readFromStack[12] =
+              {
+                TRUE /* dx1 */, TRUE  /* dy1 */,
+                TRUE /* dx2 */, TRUE  /* dy2 */,
+                TRUE /* dx3 */, FALSE /* dy3 */,
+                TRUE /* dx4 */, FALSE /* dy4 */,
+                TRUE /* dx5 */, TRUE  /* dy5 */,
+                TRUE /* dx6 */, FALSE /* dy6 */
+              };
+
+
+              FT_TRACE4(( " hflex1\n" ));
+
+              cf2_doFlex( opStack,
+                          &curX,
+                          &curY,
+                          &glyphPath,
+                          readFromStack,
+                          FALSE /* doConditionalLastRead */ );
+            }
+            continue;
+
+          case cf2_escFLEX1:
+            {
+              static const FT_Bool  readFromStack[12] =
+              {
+                TRUE  /* dx1 */, TRUE  /* dy1 */,
+                TRUE  /* dx2 */, TRUE  /* dy2 */,
+                TRUE  /* dx3 */, TRUE  /* dy3 */,
+                TRUE  /* dx4 */, TRUE  /* dy4 */,
+                TRUE  /* dx5 */, TRUE  /* dy5 */,
+                FALSE /* dx6 */, FALSE /* dy6 */
+              };
+
+
+              FT_TRACE4(( " flex1\n" ));
+
+              cf2_doFlex( opStack,
+                          &curX,
+                          &curY,
+                          &glyphPath,
+                          readFromStack,
+                          TRUE /* doConditionalLastRead */ );
+            }
+            continue;
+
+          case cf2_escRESERVED_1:
+          case cf2_escRESERVED_2:
+          case cf2_escRESERVED_6:
+          case cf2_escRESERVED_7:
+          case cf2_escRESERVED_8:
+          case cf2_escRESERVED_13:
+          case cf2_escRESERVED_16:
+          case cf2_escRESERVED_17:
+          case cf2_escRESERVED_19:
+          case cf2_escRESERVED_25:
+          case cf2_escRESERVED_31:
+          case cf2_escRESERVED_32:
+          case cf2_escRESERVED_33:
+          default:
+            FT_TRACE4(( " unknown op (12, %d)\n", op2 ));
+
+          }; /* end of switch statement checking `op2' */
+
+        } /* case cf2_cmdESC */
+        break;
+
+      case cf2_cmdENDCHAR:
+        FT_TRACE4(( " endchar\n" ));
+
+        if ( cf2_stack_count( opStack ) == 1 ||
+             cf2_stack_count( opStack ) == 5 )
+        {
+          if ( !haveWidth )
+            *width = cf2_stack_getReal( opStack, 0 ) + nominalWidthX;
+        }
+
+        haveWidth = TRUE;
+
+        /* close path if still open */
+        cf2_glyphpath_closeOpenPath( &glyphPath );
+
+        if ( cf2_stack_count( opStack ) > 1 )
+        {
+          /* must be either 4 or 5 --                       */
+          /* this is a (deprecated) implied `seac' operator */
+
+          CF2_UInt       achar;
+          CF2_UInt       bchar;
+          CF2_BufferRec  component;
+          CF2_Fixed      dummyWidth;   /* ignore component width */
+          FT_Error       error2;
+
+
+          if ( doingSeac )
+          {
+            lastError = FT_THROW( Invalid_Glyph_Format );
+            goto exit;      /* nested seac */
+          }
+
+          achar = cf2_stack_popInt( opStack );
+          bchar = cf2_stack_popInt( opStack );
+
+          curY = cf2_stack_popFixed( opStack );
+          curX = cf2_stack_popFixed( opStack );
+
+          error2 = cf2_getSeacComponent( decoder, achar, &component );
+          if ( error2 )
+          {
+             lastError = error2;      /* pass FreeType error through */
+             goto exit;
+          }
+          cf2_interpT2CharString( font,
+                                  &component,
+                                  callbacks,
+                                  translation,
+                                  TRUE,
+                                  curX,
+                                  curY,
+                                  &dummyWidth );
+          cf2_freeSeacComponent( decoder, &component );
+
+          error2 = cf2_getSeacComponent( decoder, bchar, &component );
+          if ( error2 )
+          {
+            lastError = error2;      /* pass FreeType error through */
+            goto exit;
+          }
+          cf2_interpT2CharString( font,
+                                  &component,
+                                  callbacks,
+                                  translation,
+                                  TRUE,
+                                  0,
+                                  0,
+                                  &dummyWidth );
+          cf2_freeSeacComponent( decoder, &component );
+        }
+        goto exit;
+
+      case cf2_cmdCNTRMASK:
+      case cf2_cmdHINTMASK:
+        /* the final \n in the tracing message gets added in      */
+        /* `cf2_hintmask_read' (which also traces the mask bytes) */
+        FT_TRACE4(( op1 == cf2_cmdCNTRMASK ? " cntrmask" : " hintmask" ));
+
+        /* if there are arguments on the stack, there this is an */
+        /* implied cf2_cmdVSTEMHM                                */
+        if ( cf2_stack_count( opStack ) != 0 )
+        {
+          /* never add hints after the mask is computed */
+          if ( cf2_hintmask_isValid( &hintMask ) )
+            FT_TRACE4(( "cf2_interpT2CharString: invalid hint mask\n" ));
+        }
+
+        cf2_doStems( font,
+                     opStack,
+                     &vStemHintArray,
+                     width,
+                     &haveWidth,
+                     0 );
+
+        if ( op1 == cf2_cmdHINTMASK )
+        {
+          /* consume the hint mask bytes which follow the operator */
+          cf2_hintmask_read( &hintMask,
+                             charstring,
+                             cf2_arrstack_size( &hStemHintArray ) +
+                               cf2_arrstack_size( &vStemHintArray ) );
+        }
+        else
+        {
+          /*
+           * Consume the counter mask bytes which follow the operator:
+           * Build a temporary hint map, just to place and lock those
+           * stems participating in the counter mask.  These are most
+           * likely the dominant hstems, and are grouped together in a
+           * few counter groups, not necessarily in correspondence
+           * with the hint groups.  This reduces the chances of
+           * conflicts between hstems that are initially placed in
+           * separate hint groups and then brought together.  The
+           * positions are copied back to `hStemHintArray', so we can
+           * discard `counterMask' and `counterHintMap'.
+           *
+           */
+          CF2_HintMapRec   counterHintMap;
+          CF2_HintMaskRec  counterMask;
+
+
+          cf2_hintmap_init( &counterHintMap,
+                            font,
+                            &glyphPath.initialHintMap,
+                            &glyphPath.hintMoves,
+                            scaleY );
+          cf2_hintmask_init( &counterMask, error );
+
+          cf2_hintmask_read( &counterMask,
+                             charstring,
+                             cf2_arrstack_size( &hStemHintArray ) +
+                               cf2_arrstack_size( &vStemHintArray ) );
+          cf2_hintmap_build( &counterHintMap,
+                             &hStemHintArray,
+                             &vStemHintArray,
+                             &counterMask,
+                             0,
+                             FALSE );
+        }
+        break;
+
+      case cf2_cmdRMOVETO:
+        FT_TRACE4(( " rmoveto\n" ));
+
+        curY += cf2_stack_popFixed( opStack );
+        curX += cf2_stack_popFixed( opStack );
+
+        cf2_glyphpath_moveTo( &glyphPath, curX, curY );
+
+        if ( cf2_stack_count( opStack ) > 0 && !haveWidth )
+          *width = cf2_stack_popFixed( opStack ) + nominalWidthX;
+
+        haveWidth = TRUE;
+        break;
+
+      case cf2_cmdHMOVETO:
+        FT_TRACE4(( " hmoveto\n" ));
+
+        curX += cf2_stack_popFixed( opStack );
+
+        cf2_glyphpath_moveTo( &glyphPath, curX, curY );
+
+        if ( cf2_stack_count( opStack ) > 0 && !haveWidth )
+          *width = cf2_stack_popFixed( opStack ) + nominalWidthX;
+
+        haveWidth = TRUE;
+        break;
+
+      case cf2_cmdRLINECURVE:
+        {
+          CF2_UInt  count = cf2_stack_count( opStack );
+          CF2_UInt  index = 0;
+
+
+          FT_TRACE4(( " rlinecurve\n" ));
+
+          while ( index + 6 < count )
+          {
+            curX += cf2_stack_getReal( opStack, index + 0 );
+            curY += cf2_stack_getReal( opStack, index + 1 );
+
+            cf2_glyphpath_lineTo( &glyphPath, curX, curY );
+            index += 2;
+          }
+
+          while ( index < count )
+          {
+            CF2_Fixed  x1 = cf2_stack_getReal( opStack, index + 0 ) + curX;
+            CF2_Fixed  y1 = cf2_stack_getReal( opStack, index + 1 ) + curY;
+            CF2_Fixed  x2 = cf2_stack_getReal( opStack, index + 2 ) + x1;
+            CF2_Fixed  y2 = cf2_stack_getReal( opStack, index + 3 ) + y1;
+            CF2_Fixed  x3 = cf2_stack_getReal( opStack, index + 4 ) + x2;
+            CF2_Fixed  y3 = cf2_stack_getReal( opStack, index + 5 ) + y2;
+
+
+            cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+            curX   = x3;
+            curY   = y3;
+            index += 6;
+          }
+
+          cf2_stack_clear( opStack );
+        }
+        continue; /* no need to clear stack again */
+
+      case cf2_cmdVVCURVETO:
+        {
+          CF2_UInt  count = cf2_stack_count( opStack );
+          CF2_UInt  index = 0;
+
+
+          FT_TRACE4(( " vvcurveto\n" ));
+
+          while ( index < count )
+          {
+            CF2_Fixed  x1, y1, x2, y2, x3, y3;
+
+
+            if ( ( count - index ) & 1 )
+            {
+              x1 = cf2_stack_getReal( opStack, index ) + curX;
+
+              ++index;
+            }
+            else
+              x1 = curX;
+
+            y1 = cf2_stack_getReal( opStack, index + 0 ) + curY;
+            x2 = cf2_stack_getReal( opStack, index + 1 ) + x1;
+            y2 = cf2_stack_getReal( opStack, index + 2 ) + y1;
+            x3 = x2;
+            y3 = cf2_stack_getReal( opStack, index + 3 ) + y2;
+
+            cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+            curX   = x3;
+            curY   = y3;
+            index += 4;
+          }
+
+          cf2_stack_clear( opStack );
+        }
+        continue; /* no need to clear stack again */
+
+      case cf2_cmdHHCURVETO:
+        {
+          CF2_UInt  count = cf2_stack_count( opStack );
+          CF2_UInt  index = 0;
+
+
+          FT_TRACE4(( " hhcurveto\n" ));
+
+          while ( index < count )
+          {
+            CF2_Fixed  x1, y1, x2, y2, x3, y3;
+
+
+            if ( ( count - index ) & 1 )
+            {
+              y1 = cf2_stack_getReal( opStack, index ) + curY;
+
+              ++index;
+            }
+            else
+              y1 = curY;
+
+            x1 = cf2_stack_getReal( opStack, index + 0 ) + curX;
+            x2 = cf2_stack_getReal( opStack, index + 1 ) + x1;
+            y2 = cf2_stack_getReal( opStack, index + 2 ) + y1;
+            x3 = cf2_stack_getReal( opStack, index + 3 ) + x2;
+            y3 = y2;
+
+            cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+            curX   = x3;
+            curY   = y3;
+            index += 4;
+          }
+
+          cf2_stack_clear( opStack );
+        }
+        continue; /* no need to clear stack again */
+
+      case cf2_cmdVHCURVETO:
+      case cf2_cmdHVCURVETO:
+        {
+          CF2_UInt  count = cf2_stack_count( opStack );
+          CF2_UInt  index = 0;
+
+          FT_Bool  alternate = op1 == cf2_cmdHVCURVETO;
+
+
+          FT_TRACE4(( alternate ? " hvcurveto\n" : " vhcurveto\n" ));
+
+          while ( index < count )
+          {
+            CF2_Fixed x1, x2, x3, y1, y2, y3;
+
+
+            if ( alternate )
+            {
+              x1 = cf2_stack_getReal( opStack, index + 0 ) + curX;
+              y1 = curY;
+              x2 = cf2_stack_getReal( opStack, index + 1 ) + x1;
+              y2 = cf2_stack_getReal( opStack, index + 2 ) + y1;
+              y3 = cf2_stack_getReal( opStack, index + 3 ) + y2;
+
+              if ( count - index == 5 )
+              {
+                x3 = cf2_stack_getReal( opStack, index + 4 ) + x2;
+
+                ++index;
+              }
+              else
+                x3 = x2;
+
+              alternate = FALSE;
+            }
+            else
+            {
+              x1 = curX;
+              y1 = cf2_stack_getReal( opStack, index + 0 ) + curY;
+              x2 = cf2_stack_getReal( opStack, index + 1 ) + x1;
+              y2 = cf2_stack_getReal( opStack, index + 2 ) + y1;
+              x3 = cf2_stack_getReal( opStack, index + 3 ) + x2;
+
+              if ( count - index == 5 )
+              {
+                y3 = cf2_stack_getReal( opStack, index + 4 ) + y2;
+
+                ++index;
+              }
+              else
+                y3 = y2;
+
+              alternate = TRUE;
+            }
+
+            cf2_glyphpath_curveTo( &glyphPath, x1, y1, x2, y2, x3, y3 );
+
+            curX   = x3;
+            curY   = y3;
+            index += 4;
+          }
+
+          cf2_stack_clear( opStack );
+        }
+        continue;     /* no need to clear stack again */
+
+      case cf2_cmdEXTENDEDNMBR:
+        {
+          CF2_Int  v;
+
+
+          /* sign-extend first byte */
+          v   = (FT_Char)cf2_buf_readByte( charstring );
+          v <<= 8;
+          v  += cf2_buf_readByte( charstring );
+
+          FT_TRACE4(( " %d", v ));
+
+          cf2_stack_pushInt( opStack, v );
+        }
+        continue;
+
+      default:
+        /* numbers */
+        {
+          CF2_Int  v;
+
+
+          if ( /* op1 >= 32 && */ op1 <= 246 )
+          {
+            FT_TRACE4(( " %d", op1 - 139 ));
+
+            /* -107 .. 107 */
+            cf2_stack_pushInt( opStack, op1 - 139 );
+          }
+
+          else if ( /* op1 >= 247 && */ op1 <= 250 )
+          {
+            v  = op1;
+            v -= 247;
+            v *= 256;
+            v += cf2_buf_readByte( charstring );
+
+            FT_TRACE4(( " %ld", v + 108 ));
+
+            /* 108 .. 1131 */
+            cf2_stack_pushInt( opStack, v + 108 );
+          }
+
+          else if ( /* op1 >= 251 && */ op1 <= 254 )
+          {
+            v  = op1;
+            v -= 251;
+            v *= 256;
+            v += cf2_buf_readByte( charstring );
+
+            FT_TRACE4(( " %ld", -v - 108 ));
+
+            /* -1131 .. -108 */
+            cf2_stack_pushInt( opStack, -v - 108 );
+          }
+
+          else /* op1 == 255 */
+          {
+            v  = cf2_buf_readByte( charstring );
+            v *= 256;
+            v += cf2_buf_readByte( charstring );
+            v *= 256;
+            v += cf2_buf_readByte( charstring );
+            v *= 256;
+            v += cf2_buf_readByte( charstring );
+
+            FT_TRACE4(( " %.2f", v / 65536.0 ));
+
+            /* interpret the value as a CF2_Fixed */
+            cf2_stack_pushFixed( opStack, v );
+          }
+        }
+        continue;   /* don't clear stack */
+
+      } /* end of switch statement checking `op1' */
+
+      cf2_stack_clear( opStack );
+
+    } /* end of main interpreter loop */
+
+    /* we get here if the charstring ends without cf2_cmdENDCHAR */
+    FT_TRACE4(( "cf2_interpT2CharString:"
+                "  charstring ends without ENDCHAR\n" ));
+
+  exit:
+    /* check whether last error seen is also the first one */
+    cf2_setError( error, lastError );
+
+    /* free resources from objects we've used */
+    cf2_glyphpath_finalize( &glyphPath );
+    cf2_arrstack_finalize( &vStemHintArray );
+    cf2_arrstack_finalize( &hStemHintArray );
+    cf2_arrstack_finalize( &subrStack );
+    cf2_stack_free( opStack );
+
+    FT_TRACE4(( "\n" ));
+
+    return;
+  }
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2intrp.h
@@ -1,0 +1,83 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2font.h                                                              */
+/*                                                                         */
+/*    Adobe's CFF Interpreter (specification).                             */
+/*                                                                         */
+/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __CF2INTRP_H__
+#define __CF2INTRP_H__
+
+
+#include "cf2ft.h"
+#include "cf2hints.h"
+
+
+FT_BEGIN_HEADER
+
+
+  FT_LOCAL( void )
+  cf2_hintmask_init( CF2_HintMask  hintmask,
+                     FT_Error*     error );
+  FT_LOCAL( FT_Bool )
+  cf2_hintmask_isValid( const CF2_HintMask  hintmask );
+  FT_LOCAL( FT_Bool )
+  cf2_hintmask_isNew( const CF2_HintMask  hintmask );
+  FT_LOCAL( void )
+  cf2_hintmask_setNew( CF2_HintMask  hintmask,
+                       FT_Bool       val );
+  FT_LOCAL( FT_Byte* )
+  cf2_hintmask_getMaskPtr( CF2_HintMask  hintmask );
+  FT_LOCAL( void )
+  cf2_hintmask_setAll( CF2_HintMask  hintmask,
+                       size_t        bitCount );
+
+  FT_LOCAL( void )
+  cf2_interpT2CharString( CF2_Font              font,
+                          CF2_Buffer            charstring,
+                          CF2_OutlineCallbacks  callbacks,
+                          const FT_Vector*      translation,
+                          FT_Bool               doingSeac,
+                          CF2_Fixed             curX,
+                          CF2_Fixed             curY,
+                          CF2_Fixed*            width );
+
+
+FT_END_HEADER
+
+
+#endif /* __CF2INTRP_H__ */
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2read.c
@@ -1,0 +1,112 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2read.c                                                              */
+/*                                                                         */
+/*    Adobe's code for stream handling (body).                             */
+/*                                                                         */
+/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#include "cf2ft.h"
+#include FT_INTERNAL_DEBUG_H
+
+#include "cf2glue.h"
+
+#include "cf2error.h"
+
+
+  /* Define CF2_IO_FAIL as 1 to enable random errors and random */
+  /* value errors in I/O.                                       */
+#define CF2_IO_FAIL  0
+
+
+#if CF2_IO_FAIL
+
+  /* set the .00 value to a nonzero probability */
+  static int
+  randomError2( void )
+  {
+    /* for region buffer ReadByte (interp) function */
+    return (double)rand() / RAND_MAX < .00;
+  }
+
+  /* set the .00 value to a nonzero probability */
+  static CF2_Int
+  randomValue()
+  {
+    return (double)rand() / RAND_MAX < .00 ? rand() : 0;
+  }
+
+#endif /* CF2_IO_FAIL */
+
+
+  /* Region Buffer                                      */
+  /*                                                    */
+  /* Can be constructed from a copied buffer managed by */
+  /* `FCM_getDatablock'.                                */
+  /* Reads bytes with check for end of buffer.          */
+
+  /* reading past the end of the buffer sets error and returns zero */
+  FT_LOCAL_DEF( CF2_Int )
+  cf2_buf_readByte( CF2_Buffer  buf )
+  {
+    if ( buf->ptr < buf->end )
+    {
+#if CF2_IO_FAIL
+      if ( randomError2() )
+      {
+        CF2_SET_ERROR( buf->error, Invalid_Stream_Operation );
+        return 0;
+      }
+
+      return *(buf->ptr)++ + randomValue();
+#else
+      return *(buf->ptr)++;
+#endif
+    }
+    else
+    {
+      CF2_SET_ERROR( buf->error, Invalid_Stream_Operation );
+      return 0;
+    }
+  }
+
+
+  /* note: end condition can occur without error */
+  FT_LOCAL_DEF( FT_Bool )
+  cf2_buf_isEnd( CF2_Buffer  buf )
+  {
+    return buf->ptr >= buf->end;
+  }
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2read.h
@@ -1,0 +1,68 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2read.h                                                              */
+/*                                                                         */
+/*    Adobe's code for stream handling (specification).                    */
+/*                                                                         */
+/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __CF2READ_H__
+#define __CF2READ_H__
+
+
+FT_BEGIN_HEADER
+
+
+  typedef struct  CF2_BufferRec_
+  {
+    FT_Error*       error;
+    const FT_Byte*  start;
+    const FT_Byte*  end;
+    const FT_Byte*  ptr;
+
+  } CF2_BufferRec, *CF2_Buffer;
+
+
+  FT_LOCAL( CF2_Int )
+  cf2_buf_readByte( CF2_Buffer  buf );
+  FT_LOCAL( FT_Bool )
+  cf2_buf_isEnd( CF2_Buffer  buf );
+
+
+FT_END_HEADER
+
+
+#endif /* __CF2READ_H__ */
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2stack.c
@@ -1,0 +1,205 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2stack.c                                                             */
+/*                                                                         */
+/*    Adobe's code for emulating a CFF stack (body).                       */
+/*                                                                         */
+/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#include "cf2ft.h"
+#include FT_INTERNAL_DEBUG_H
+
+#include "cf2glue.h"
+#include "cf2font.h"
+#include "cf2stack.h"
+
+#include "cf2error.h"
+
+
+  /* Allocate and initialize an instance of CF2_Stack.       */
+  /* Note: This function returns NULL on error (does not set */
+  /* `error').                                               */
+  FT_LOCAL_DEF( CF2_Stack )
+  cf2_stack_init( FT_Memory  memory,
+                  FT_Error*  e )
+  {
+    FT_Error  error = FT_Err_Ok;     /* for FT_QNEW */
+
+    CF2_Stack  stack = NULL;
+
+
+    if ( !FT_QNEW( stack ) )
+    {
+      /* initialize the structure; FT_QNEW zeroes it */
+      stack->memory = memory;
+      stack->error  = e;
+      stack->top    = &stack->buffer[0]; /* empty stack */
+    }
+
+    return stack;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_stack_free( CF2_Stack  stack )
+  {
+    if ( stack )
+    {
+      FT_Memory  memory = stack->memory;
+
+
+      /* free the main structure */
+      FT_FREE( stack );
+    }
+  }
+
+
+  FT_LOCAL_DEF( CF2_UInt )
+  cf2_stack_count( CF2_Stack  stack )
+  {
+    return (CF2_UInt)( stack->top - &stack->buffer[0] );
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_stack_pushInt( CF2_Stack  stack,
+                     CF2_Int    val )
+  {
+    if ( stack->top == &stack->buffer[CF2_OPERAND_STACK_SIZE] )
+    {
+      CF2_SET_ERROR( stack->error, Stack_Overflow );
+      return;     /* stack overflow */
+    }
+
+    stack->top->u.i  = val;
+    stack->top->type = CF2_NumberInt;
+    ++stack->top;
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_stack_pushFixed( CF2_Stack  stack,
+                       CF2_Fixed  val )
+  {
+    if ( stack->top == &stack->buffer[CF2_OPERAND_STACK_SIZE] )
+    {
+      CF2_SET_ERROR( stack->error, Stack_Overflow );
+      return;     /* stack overflow */
+    }
+
+    stack->top->u.r  = val;
+    stack->top->type = CF2_NumberFixed;
+    ++stack->top;
+  }
+
+
+  /* this function is only allowed to pop an integer type */
+  FT_LOCAL_DEF( CF2_Int )
+  cf2_stack_popInt( CF2_Stack  stack )
+  {
+    if ( stack->top == &stack->buffer[0] )
+    {
+      CF2_SET_ERROR( stack->error, Stack_Underflow );
+      return 0;   /* underflow */
+    }
+    if ( stack->top[-1].type != CF2_NumberInt )
+    {
+      CF2_SET_ERROR( stack->error, Syntax_Error );
+      return 0;   /* type mismatch */
+    }
+
+    --stack->top;
+
+    return stack->top->u.i;
+  }
+
+
+  /* Note: type mismatch is silently cast */
+  /* TODO: check this */
+  FT_LOCAL_DEF( CF2_Fixed )
+  cf2_stack_popFixed( CF2_Stack  stack )
+  {
+    if ( stack->top == &stack->buffer[0] )
+    {
+      CF2_SET_ERROR( stack->error, Stack_Underflow );
+      return cf2_intToFixed( 0 );    /* underflow */
+    }
+
+    --stack->top;
+
+    switch ( stack->top->type )
+    {
+    case CF2_NumberInt:
+      return cf2_intToFixed( stack->top->u.i );
+    case CF2_NumberFrac:
+      return cf2_fracToFixed( stack->top->u.f );
+    default:
+      return stack->top->u.r;
+    }
+  }
+
+
+  /* Note: type mismatch is silently cast */
+  /* TODO: check this */
+  FT_LOCAL_DEF( CF2_Fixed )
+  cf2_stack_getReal( CF2_Stack  stack,
+                     CF2_UInt   idx )
+  {
+    FT_ASSERT( cf2_stack_count( stack ) <= CF2_OPERAND_STACK_SIZE );
+
+    if ( idx >= cf2_stack_count( stack ) )
+    {
+      CF2_SET_ERROR( stack->error, Stack_Overflow );
+      return cf2_intToFixed( 0 );    /* bounds error */
+    }
+
+    switch ( stack->buffer[idx].type )
+    {
+    case CF2_NumberInt:
+      return cf2_intToFixed( stack->buffer[idx].u.i );
+    case CF2_NumberFrac:
+      return cf2_fracToFixed( stack->buffer[idx].u.f );
+    default:
+      return stack->buffer[idx].u.r;
+    }
+  }
+
+
+  FT_LOCAL_DEF( void )
+  cf2_stack_clear( CF2_Stack  stack )
+  {
+    stack->top = &stack->buffer[0];
+  }
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2stack.h
@@ -1,0 +1,106 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2stack.h                                                             */
+/*                                                                         */
+/*    Adobe's code for emulating a CFF stack (specification).              */
+/*                                                                         */
+/*  Copyright 2007-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __CF2STACK_H__
+#define __CF2STACK_H__
+
+
+FT_BEGIN_HEADER
+
+
+  /* CFF operand stack; specified maximum of 48 or 192 values */
+  typedef struct  CF2_StackNumber_
+  {
+    union
+    {
+      CF2_Fixed  r;      /* 16.16 fixed point */
+      CF2_Frac   f;      /* 2.30 fixed point (for font matrix) */
+      CF2_Int    i;
+    } u;
+
+    CF2_NumberType  type;
+
+  } CF2_StackNumber;
+
+
+  typedef struct  CF2_StackRec_
+  {
+    FT_Memory         memory;
+    FT_Error*         error;
+    CF2_StackNumber   buffer[CF2_OPERAND_STACK_SIZE];
+    CF2_StackNumber*  top;
+
+  } CF2_StackRec, *CF2_Stack;
+
+
+  FT_LOCAL( CF2_Stack )
+  cf2_stack_init( FT_Memory  memory,
+                  FT_Error*  error );
+  FT_LOCAL( void )
+  cf2_stack_free( CF2_Stack  stack );
+
+  FT_LOCAL( CF2_UInt )
+  cf2_stack_count( CF2_Stack  stack );
+
+  FT_LOCAL( void )
+  cf2_stack_pushInt( CF2_Stack  stack,
+                     CF2_Int    val );
+  FT_LOCAL( void )
+  cf2_stack_pushFixed( CF2_Stack  stack,
+                       CF2_Fixed  val );
+
+  FT_LOCAL( CF2_Int )
+  cf2_stack_popInt( CF2_Stack  stack );
+  FT_LOCAL( CF2_Fixed )
+  cf2_stack_popFixed( CF2_Stack  stack );
+
+  FT_LOCAL( CF2_Fixed )
+  cf2_stack_getReal( CF2_Stack  stack,
+                     CF2_UInt   idx );
+
+  FT_LOCAL( void )
+  cf2_stack_clear( CF2_Stack  stack );
+
+
+FT_END_HEADER
+
+
+#endif /* __CF2STACK_H__ */
+
+
+/* END */
--- /dev/null
+++ b/src/cff/cf2types.h
@@ -1,0 +1,78 @@
+/***************************************************************************/
+/*                                                                         */
+/*  cf2types.h                                                             */
+/*                                                                         */
+/*    Adobe's code for defining data types (specification only).           */
+/*                                                                         */
+/*  Copyright 2011-2013 Adobe Systems Incorporated.                        */
+/*                                                                         */
+/*  This software, and all works of authorship, whether in source or       */
+/*  object code form as indicated by the copyright notice(s) included      */
+/*  herein (collectively, the "Work") is made available, and may only be   */
+/*  used, modified, and distributed under the FreeType Project License,    */
+/*  LICENSE.TXT.  Additionally, subject to the terms and conditions of the */
+/*  FreeType Project License, each contributor to the Work hereby grants   */
+/*  to any individual or legal entity exercising permissions granted by    */
+/*  the FreeType Project License and this section (hereafter, "You" or     */
+/*  "Your") a perpetual, worldwide, non-exclusive, no-charge,              */
+/*  royalty-free, irrevocable (except as stated in this section) patent    */
+/*  license to make, have made, use, offer to sell, sell, import, and      */
+/*  otherwise transfer the Work, where such license applies only to those  */
+/*  patent claims licensable by such contributor that are necessarily      */
+/*  infringed by their contribution(s) alone or by combination of their    */
+/*  contribution(s) with the Work to which such contribution(s) was        */
+/*  submitted.  If You institute patent litigation against any entity      */
+/*  (including a cross-claim or counterclaim in a lawsuit) alleging that   */
+/*  the Work or a contribution incorporated within the Work constitutes    */
+/*  direct or contributory patent infringement, then any patent licenses   */
+/*  granted to You under this License for that Work shall terminate as of  */
+/*  the date such litigation is filed.                                     */
+/*                                                                         */
+/*  By using, modifying, or distributing the Work you indicate that you    */
+/*  have read and understood the terms and conditions of the               */
+/*  FreeType Project License as well as those provided in this section,    */
+/*  and you accept them fully.                                             */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __CF2TYPES_H__
+#define __CF2TYPES_H__
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+
+FT_BEGIN_HEADER
+
+
+  /*
+   * The data models that we expect to support are as follows:
+   *
+   *   name  char short int long long-long pointer example
+   *  -----------------------------------------------------
+   *   ILP32  8    16    32  32     64*      32    32-bit MacOS, x86
+   *   LLP64  8    16    32  32     64       64    x64
+   *   LP64   8    16    32  64     64       64    64-bit MacOS
+   *
+   *    *) type may be supported by emulation on a 32-bit architecture
+   *
+   */
+
+
+  /* integers at least 32 bits wide */
+#define CF2_UInt  FT_UFast
+#define CF2_Int   FT_Fast
+
+
+  /* fixed-float numbers */
+  typedef FT_Int32  CF2_F16Dot16;
+
+
+FT_END_HEADER
+
+
+#endif /* __CF2TYPES_H__ */
+
+
+/* END */