shithub: freetype+ttf2subf

Download patch

ref: 24aa9c665e3a8bbfbc5ae016354ffba7a1377fd3
parent: de5999411a12779fb38eebfd47c2ec38f08bf015
author: Werner Lemberg <[email protected]>
date: Sun Dec 6 13:52:41 EST 2015

[autofit] Rewrite HarfBuzz interface to support character clusters.

Scripts like Khmer have blue zones that can't be directly
represented by Unicode characters.  Instead, it is necessary to let
HarfBuzz convert character clusters into proper glyph representation
forms, then deriving the blue zone information from the resulting
glyphs.

* src/autofit/hbshim.c, src/autofit/hbshim.h: Replaced by...
* src/autofit/afshaper.c, src/autofit/afshaper.h: ... these two new
files, providing a new API to access HarfBuzz.

The new API manages a HarfBuzz buffer with `af_shaper_buf_create'
and `af_shaper_buf_destroy'.  The buffer receives a UTF8 encoded
string with function `af_shaper_get_cluster', and the resulting
glyph data (indices, advance widths, vertical offsets) can be
iteratively accessed with function `af_shaper_get_elem'.

* src/autofit/afcjk.c (af_cjk_metrics_init_widths,
af_cjk_metrics_init_blues, af_cjk_metrics_check_digits): Updated.

* src/autofit/aflatin.c (af_latin_metrics_init_widths,
af_latin_metrics_init_blues, af_latin_metrics_check_digits):
Updated.

* include/freetype/internal/fttrace.h: s/afharfbuzz/afshaper/.

* src/autofit/afglobal.c: s/hbshim.h/afshaper.h/.
(af_face_globals_compute_style_coverage): Updated.

* src/autofit/afglocal.h: s/hbshim.h/afshaper.h/.

* src/autofit/autofit.c: s/hbshim.c/afshaper.c/.

* src/autofit/Jamfile, src/autofit/rules.mk (AUTOF_DRV_SRC):
Updated.

git/fs: mount .git/fs: mount/attach disallowed
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,5 +1,44 @@
 2015-12-06  Werner Lemberg  <[email protected]>
 
+	[autofit] Rewrite HarfBuzz interface to support character clusters.
+
+	Scripts like Khmer have blue zones that can't be directly
+	represented by Unicode characters.  Instead, it is necessary to let
+	HarfBuzz convert character clusters into proper glyph representation
+	forms, then deriving the blue zone information from the resulting
+	glyphs.
+
+	* src/autofit/hbshim.c, src/autofit/hbshim.h: Replaced by...
+	* src/autofit/afshaper.c, src/autofit/afshaper.h: ... these two new
+	files, providing a new API to access HarfBuzz.
+
+	The new API manages a HarfBuzz buffer with `af_shaper_buf_create'
+	and `af_shaper_buf_destroy'.  The buffer receives a UTF8 encoded
+	string with function `af_shaper_get_cluster', and the resulting
+	glyph data (indices, advance widths, vertical offsets) can be
+	iteratively accessed with function `af_shaper_get_elem'.
+
+	* src/autofit/afcjk.c (af_cjk_metrics_init_widths,
+	af_cjk_metrics_init_blues, af_cjk_metrics_check_digits): Updated.
+
+	* src/autofit/aflatin.c (af_latin_metrics_init_widths,
+	af_latin_metrics_init_blues, af_latin_metrics_check_digits):
+	Updated.
+
+	* include/freetype/internal/fttrace.h: s/afharfbuzz/afshaper/.
+
+	* src/autofit/afglobal.c: s/hbshim.h/afshaper.h/.
+	(af_face_globals_compute_style_coverage): Updated.
+
+	* src/autofit/afglocal.h: s/hbshim.h/afshaper.h/.
+
+	* src/autofit/autofit.c: s/hbshim.c/afshaper.c/.
+
+	* src/autofit/Jamfile, src/autofit/rules.mk (AUTOF_DRV_SRC):
+	Updated.
+
+2015-12-06  Werner Lemberg  <[email protected]>
+
 	[autofit] Prepare forthcoming changes.
 
 	This makes it easier to control the commits.
--- a/include/freetype/internal/fttrace.h
+++ b/include/freetype/internal/fttrace.h
@@ -148,7 +148,7 @@
 FT_TRACE_DEF( aflatin )
 FT_TRACE_DEF( aflatin2 )
 FT_TRACE_DEF( afwarp )
-FT_TRACE_DEF( afharfbuzz )
+FT_TRACE_DEF( afshaper )
 FT_TRACE_DEF( afglobal )
 
 /* END */
--- a/src/autofit/Jamfile
+++ b/src/autofit/Jamfile
@@ -33,8 +33,8 @@
                afmodule
                afpic
                afranges
+               afshaper
                afwarp
-               hbshim
                ;
 
     if $(FT2_AUTOFIT2)
--- a/src/autofit/afcjk.c
+++ b/src/autofit/afcjk.c
@@ -88,7 +88,6 @@
     {
       FT_Error          error;
       FT_ULong          glyph_index;
-      FT_Long           y_offset;
       int               dim;
       AF_CJKMetricsRec  dummy[1];
       AF_Scaler         scaler = &dummy->root.scaler;
@@ -101,34 +100,59 @@
       AF_ScriptClass  script_class = AF_SCRIPT_CLASSES_GET
                                        [style_class->script];
 
+      void*        shaper_buf;
       const char*  p;
 
+#ifdef FT_DEBUG_LEVEL_TRACE
       FT_ULong  ch;
+#endif
 
+      p          = script_class->standard_charstring;
+      shaper_buf = af_shaper_buf_create( face );
 
-      p = script_class->standard_charstring;
-
       /* We check a list of standard characters.  The first match wins. */
 
       glyph_index = 0;
       while ( *p )
       {
+        unsigned int  num_idx;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+        const char*  p_old;
+#endif
+
+
         while ( *p == ' ' )
           p++;
 
-        GET_UTF8_CHAR( ch, p );
+#ifdef FT_DEBUG_LEVEL_TRACE
+        p_old = p;
+        GET_UTF8_CHAR( ch, p_old );
+#endif
 
-        af_get_char_index( &metrics->root,
-                           ch,
-                           &glyph_index,
-                           &y_offset );
+        /* reject input that maps to more than a single glyph */
+        p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
+        if ( num_idx > 1 )
+          continue;
+
+        /* otherwise exit loop if we have a result */
+        glyph_index = af_shaper_get_elem( &metrics->root,
+                                          shaper_buf,
+                                          0,
+                                          NULL,
+                                          NULL );
         if ( glyph_index )
           break;
       }
 
+      af_shaper_buf_destroy( face, shaper_buf );
+
       if ( !glyph_index )
         goto Exit;
 
+      if ( !glyph_index )
+        goto Exit;
+
       FT_TRACE5(( "standard character: U+%04lX (glyph index %d)\n",
                   ch, glyph_index ));
 
@@ -168,6 +192,12 @@
         if ( error )
           goto Exit;
 
+        /*
+         *  We assume that the glyphs selected for the stem width
+         *  computation are `featureless' enough so that the linking
+         *  algorithm works fine without adjustments of its scoring
+         *  function.
+         */
         af_latin_hints_link_segments( hints,
                                       0,
                                       NULL,
@@ -266,7 +296,9 @@
     AF_Blue_Stringset         bss = sc->blue_stringset;
     const AF_Blue_StringRec*  bs  = &af_blue_stringsets[bss];
 
+    void*  shaper_buf;
 
+
     /* we walk over the blue character strings as specified in the   */
     /* style's entry in the `af_blue_stringset' array, computing its */
     /* extremum points (depending on the string properties)          */
@@ -275,6 +307,8 @@
                 "==========================\n"
                 "\n" ));
 
+    shaper_buf = af_shaper_buf_create( face );
+
     for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
     {
       const char*  p = &af_blue_strings[bs->string];
@@ -313,26 +347,47 @@
 
       while ( *p )
       {
-        FT_ULong    ch;
         FT_ULong    glyph_index;
-        FT_Long     y_offset;
         FT_Pos      best_pos;       /* same as points.y or points.x, resp. */
         FT_Int      best_point;
         FT_Vector*  points;
 
+        unsigned int  num_idx;
 
-        GET_UTF8_CHAR( ch, p );
+#ifdef FT_DEBUG_LEVEL_TRACE
+        const char*  p_old;
+        FT_ULong     ch;
+#endif
 
+
+        while ( *p == ' ' )
+          p++;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+        p_old = p;
+        GET_UTF8_CHAR( ch, p_old );
+#endif
+
         /* switch to characters that define flat values */
-        if ( ch == '|' )
+        if ( *p == '|' )
         {
           fill = 0;
           FT_TRACE5(( "  [reference values]\n" ));
+          p++;
           continue;
         }
 
+        /* reject input that maps to more than a single glyph */
+        p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
+        if ( num_idx > 1 )
+          continue;
+
         /* load the character in the face -- skip unknown or empty ones */
-        af_get_char_index( &metrics->root, ch, &glyph_index, &y_offset );
+        glyph_index = af_shaper_get_elem( &metrics->root,
+                                          shaper_buf,
+                                          0,
+                                          NULL,
+                                          NULL );
         if ( glyph_index == 0 )
         {
           FT_TRACE5(( "  U+%04lX unavailable\n", ch ));
@@ -341,9 +396,9 @@
 
         error   = FT_Load_Glyph( face, glyph_index, FT_LOAD_NO_SCALE );
         outline = face->glyph->outline;
-        if ( error || outline.n_points <= 0 )
+        if ( error || outline.n_points <= 2 )
         {
-          FT_TRACE5(( "  U+%04lX contains no outlines\n", ch ));
+          FT_TRACE5(( "  U+%04lX contains no (usable) outlines\n", ch ));
           continue;
         }
 
@@ -422,8 +477,9 @@
           fills[num_fills++] = best_pos;
         else
           flats[num_flats++] = best_pos;
-      }
 
+      } /* end while loop */
+
       if ( num_flats == 0 && num_fills == 0 )
       {
         /*
@@ -489,8 +545,11 @@
       FT_TRACE5(( "    -> reference = %ld\n"
                   "       overshoot = %ld\n",
                   *blue_ref, *blue_shoot ));
-    }
 
+    } /* end for loop */
+
+    af_shaper_buf_destroy( face, shaper_buf );
+
     FT_TRACE5(( "\n" ));
 
     return;
@@ -503,27 +562,36 @@
   af_cjk_metrics_check_digits( AF_CJKMetrics  metrics,
                                FT_Face        face )
   {
-    FT_UInt   i;
     FT_Bool   started = 0, same_width = 1;
     FT_Fixed  advance, old_advance = 0;
 
+    void*  shaper_buf;
 
-    /* digit `0' is 0x30 in all supported charmaps */
-    for ( i = 0x30; i <= 0x39; i++ )
+    /* in all supported charmaps, digits have character codes 0x30-0x39 */
+    const char   digits[] = "0 1 2 3 4 5 6 7 8 9";
+    const char*  p;
+
+
+    p          = digits;
+    shaper_buf = af_shaper_buf_create( face );
+
+    while ( *p )
     {
-      FT_ULong  glyph_index;
-      FT_Long   y_offset;
+      FT_ULong      glyph_index;
+      unsigned int  num_idx;
 
 
-      af_get_char_index( &metrics->root, i, &glyph_index, &y_offset );
-      if ( glyph_index == 0 )
+      /* reject input that maps to more than a single glyph */
+      p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
+      if ( num_idx > 1 )
         continue;
 
-      if ( FT_Get_Advance( face, glyph_index,
-                           FT_LOAD_NO_SCALE         |
-                           FT_LOAD_NO_HINTING       |
-                           FT_LOAD_IGNORE_TRANSFORM,
-                           &advance ) )
+      glyph_index = af_shaper_get_elem( &metrics->root,
+                                        shaper_buf,
+                                        0,
+                                        &advance,
+                                        NULL );
+      if ( !glyph_index )
         continue;
 
       if ( started )
@@ -540,6 +608,8 @@
         started     = 1;
       }
     }
+
+    af_shaper_buf_destroy( face, shaper_buf );
 
     metrics->root.digits_have_same_width = same_width;
   }
--- a/src/autofit/afglobal.c
+++ b/src/autofit/afglobal.c
@@ -18,7 +18,7 @@
 
 #include "afglobal.h"
 #include "afranges.h"
-#include "hbshim.h"
+#include "afshaper.h"
 #include FT_INTERNAL_DEBUG_H
 
 
@@ -240,12 +240,12 @@
       else
       {
         /* get glyphs not directly addressable by cmap */
-        af_get_coverage( globals, style_class, gstyles );
+        af_shaper_get_coverage( globals, style_class, gstyles );
       }
     }
 
     /* handle the default OpenType features of the default script ... */
-    af_get_coverage( globals, AF_STYLE_CLASSES_GET[dflt], gstyles );
+    af_shaper_get_coverage( globals, AF_STYLE_CLASSES_GET[dflt], gstyles );
 
     /* ... and the remaining default OpenType features */
     for ( ss = 0; AF_STYLE_CLASSES_GET[ss]; ss++ )
@@ -254,7 +254,7 @@
 
 
       if ( ss != dflt && style_class->coverage == AF_COVERAGE_DEFAULT )
-        af_get_coverage( globals, style_class, gstyles );
+        af_shaper_get_coverage( globals, style_class, gstyles );
     }
 
     /* mark ASCII digits */
--- a/src/autofit/afglobal.h
+++ b/src/autofit/afglobal.h
@@ -23,7 +23,7 @@
 
 #include "aftypes.h"
 #include "afmodule.h"
-#include "hbshim.h"
+#include "afshaper.h"
 
 
 FT_BEGIN_HEADER
--- a/src/autofit/aflatin.c
+++ b/src/autofit/aflatin.c
@@ -79,7 +79,6 @@
     {
       FT_Error            error;
       FT_ULong            glyph_index;
-      FT_Long             y_offset;
       int                 dim;
       AF_LatinMetricsRec  dummy[1];
       AF_Scaler           scaler = &dummy->root.scaler;
@@ -92,13 +91,16 @@
       AF_ScriptClass  script_class = AF_SCRIPT_CLASSES_GET
                                        [style_class->script];
 
+      void*        shaper_buf;
       const char*  p;
 
+#ifdef FT_DEBUG_LEVEL_TRACE
       FT_ULong  ch;
+#endif
 
+      p          = script_class->standard_charstring;
+      shaper_buf = af_shaper_buf_create( face );
 
-      p = script_class->standard_charstring;
-
       /*
        * We check a list of standard characters to catch features like
        * `c2sc' (small caps from caps) that don't contain lowercase letters
@@ -109,22 +111,44 @@
       glyph_index = 0;
       while ( *p )
       {
+        unsigned int  num_idx;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+        const char*  p_old;
+#endif
+
+
         while ( *p == ' ' )
           p++;
 
-        GET_UTF8_CHAR( ch, p );
+#ifdef FT_DEBUG_LEVEL_TRACE
+        p_old = p;
+        GET_UTF8_CHAR( ch, p_old );
+#endif
 
-        af_get_char_index( &metrics->root,
-                           ch,
-                           &glyph_index,
-                           &y_offset );
+        /* reject input that maps to more than a single glyph */
+        p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
+        if ( num_idx > 1 )
+          continue;
+
+        /* otherwise exit loop if we have a result */
+        glyph_index = af_shaper_get_elem( &metrics->root,
+                                          shaper_buf,
+                                          0,
+                                          NULL,
+                                          NULL );
         if ( glyph_index )
           break;
       }
 
+      af_shaper_buf_destroy( face, shaper_buf );
+
       if ( !glyph_index )
         goto Exit;
 
+      if ( !glyph_index )
+        goto Exit;
+
       FT_TRACE5(( "standard character: U+%04lX (glyph index %d)\n",
                   ch, glyph_index ));
 
@@ -269,7 +293,9 @@
 
     FT_Pos  flat_threshold = FLAT_THRESHOLD( metrics->units_per_em );
 
+    void*  shaper_buf;
 
+
     /* we walk over the blue character strings as specified in the */
     /* style's entry in the `af_blue_stringset' array              */
 
@@ -277,6 +303,8 @@
                 "============================\n"
                 "\n" ));
 
+    shaper_buf = af_shaper_buf_create( face );
+
     for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
     {
       const char*  p = &af_blue_strings[bs->string];
@@ -340,24 +368,51 @@
 
       while ( *p )
       {
-        FT_ULong    ch;
         FT_ULong    glyph_index;
         FT_Long     y_offset;
-        FT_Pos      best_y;                            /* same as points.y */
         FT_Int      best_point, best_contour_first, best_contour_last;
         FT_Vector*  points;
-        FT_Bool     round = 0;
 
+        FT_Pos   best_y_extremum;                      /* same as points.y */
+        FT_Bool  best_round = 0;
+
         unsigned int  i, num_idx;
 
+#ifdef FT_DEBUG_LEVEL_TRACE
+        const char*  p_old;
+        FT_ULong     ch;
+#endif
 
-        GET_UTF8_CHAR( ch, p );
 
-        num_idx = 1;
+        while ( *p == ' ' )
+          p++;
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+        p_old = p;
+        GET_UTF8_CHAR( ch, p_old );
+#endif
+
+        p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
+
+        if ( AF_LATIN_IS_TOP_BLUE( bs ) )
+          best_y_extremum = FT_INT_MIN;
+        else
+          best_y_extremum = FT_INT_MAX;
+
+        /* iterate over all glyph elements of the character cluster */
+        /* and get the data of the `biggest' one                    */
         for ( i = 0; i < num_idx; i++ )
         {
+          FT_Pos   best_y;
+          FT_Bool  round = 0;
+
+
           /* load the character in the face -- skip unknown or empty ones */
-          af_get_char_index( &metrics->root, ch, &glyph_index, &y_offset );
+          glyph_index = af_shaper_get_elem( &metrics->root,
+                                            shaper_buf,
+                                            i,
+                                            NULL,
+                                            &y_offset );
           if ( glyph_index == 0 )
           {
             FT_TRACE5(( "  U+%04lX unavailable\n", ch ));
@@ -369,7 +424,13 @@
           /* reject glyphs that don't produce any rendering */
           if ( error || outline.n_points <= 2 )
           {
-            FT_TRACE5(( "  U+%04lX contains no (usable) outlines\n", ch ));
+#ifdef FT_DEBUG_LEVEL_TRACE
+            if ( num_idx == 1 )
+              FT_TRACE5(( "  U+%04lX contains no (usable) outlines\n", ch ));
+            else
+              FT_TRACE5(( "  component %d of cluster starting with U+%04lX"
+                          " contains no (usable) outlines\n", i, ch ));
+#endif
             continue;
           }
 
@@ -394,9 +455,10 @@
 
               last = outline.contours[nn];
 
-              /* Avoid single-point contours since they are never rasterized. */
-              /* In some fonts, they correspond to mark attachment points     */
-              /* that are way outside of the glyph's real outline.            */
+              /* Avoid single-point contours since they are never      */
+              /* rasterized.  In some fonts, they correspond to mark   */
+              /* attachment points that are way outside of the glyph's */
+              /* real outline.                                         */
               if ( last <= first )
                 continue;
 
@@ -648,8 +710,8 @@
                   if ( l2r == left2right     &&
                        d >= length_threshold )
                   {
-                    /* all constraints are met; update segment after finding */
-                    /* its end                                               */
+                    /* all constraints are met; update segment after */
+                    /* finding its end                               */
                     do
                     {
                       if ( last < best_contour_last )
@@ -735,13 +797,32 @@
             FT_TRACE5(( " (%s)\n", round ? "round" : "flat" ));
           }
 
-          if ( round )
-            rounds[num_rounds++] = best_y;
+          if ( AF_LATIN_IS_TOP_BLUE( bs ) )
+          {
+            if ( best_y > best_y_extremum )
+            {
+              best_y_extremum = best_y;
+              best_round      = round;
+            }
+          }
           else
-            flats[num_flats++]   = best_y;
-        }
-      }
+          {
+            if ( best_y < best_y_extremum )
+            {
+              best_y_extremum = best_y;
+              best_round      = round;
+            }
+          }
 
+        } /* end for loop */
+
+        if ( best_round )
+          rounds[num_rounds++] = best_y_extremum;
+        else
+          flats[num_flats++]   = best_y_extremum;
+
+      } /* end while loop */
+
       if ( num_flats == 0 && num_rounds == 0 )
       {
         /*
@@ -820,8 +901,11 @@
       FT_TRACE5(( "    -> reference = %ld\n"
                   "       overshoot = %ld\n",
                   *blue_ref, *blue_shoot ));
-    }
 
+    } /* end for loop */
+
+    af_shaper_buf_destroy( face, shaper_buf );
+
     FT_TRACE5(( "\n" ));
 
     return;
@@ -834,27 +918,36 @@
   af_latin_metrics_check_digits( AF_LatinMetrics  metrics,
                                  FT_Face          face )
   {
-    FT_UInt   i;
     FT_Bool   started = 0, same_width = 1;
     FT_Fixed  advance, old_advance = 0;
 
+    void*  shaper_buf;
 
-    /* digit `0' is 0x30 in all supported charmaps */
-    for ( i = 0x30; i <= 0x39; i++ )
+    /* in all supported charmaps, digits have character codes 0x30-0x39 */
+    const char   digits[] = "0 1 2 3 4 5 6 7 8 9";
+    const char*  p;
+
+
+    p          = digits;
+    shaper_buf = af_shaper_buf_create( face );
+
+    while ( *p )
     {
-      FT_ULong  glyph_index;
-      FT_Long   y_offset;
+      FT_ULong      glyph_index;
+      unsigned int  num_idx;
 
 
-      af_get_char_index( &metrics->root, i, &glyph_index, &y_offset );
-      if ( glyph_index == 0 )
+      /* reject input that maps to more than a single glyph */
+      p = af_shaper_get_cluster( p, &metrics->root, shaper_buf, &num_idx );
+      if ( num_idx > 1 )
         continue;
 
-      if ( FT_Get_Advance( face, glyph_index,
-                           FT_LOAD_NO_SCALE         |
-                           FT_LOAD_NO_HINTING       |
-                           FT_LOAD_IGNORE_TRANSFORM,
-                           &advance ) )
+      glyph_index = af_shaper_get_elem( &metrics->root,
+                                        shaper_buf,
+                                        0,
+                                        &advance,
+                                        NULL );
+      if ( !glyph_index )
         continue;
 
       if ( started )
@@ -871,6 +964,8 @@
         started     = 1;
       }
     }
+
+    af_shaper_buf_destroy( face, shaper_buf );
 
     metrics->root.digits_have_same_width = same_width;
   }
--- /dev/null
+++ b/src/autofit/afshaper.c
@@ -1,0 +1,635 @@
+/***************************************************************************/
+/*                                                                         */
+/*  afshaper.c                                                             */
+/*                                                                         */
+/*    HarfBuzz interface for accessing OpenType features (body).           */
+/*                                                                         */
+/*  Copyright 2013-2015 by                                                 */
+/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
+/*                                                                         */
+/*  This file is part of the FreeType project, and may only be used,       */
+/*  modified, and distributed under the terms of the FreeType project      */
+/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include "afglobal.h"
+#include "aftypes.h"
+#include "afshaper.h"
+
+#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
+
+
+  /*************************************************************************/
+  /*                                                                       */
+  /* 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_afshaper
+
+
+  /*
+   * We use `sets' (in the HarfBuzz sense, which comes quite near to the
+   * usual mathematical meaning) to manage both lookups and glyph indices.
+   *
+   * 1. For each coverage, collect lookup IDs in a set.  Note that an
+   *    auto-hinter `coverage' is represented by one `feature', and a
+   *    feature consists of an arbitrary number of (font specific) `lookup's
+   *    that actually do the mapping job.  Please check the OpenType
+   *    specification for more details on features and lookups.
+   *
+   * 2. Create glyph ID sets from the corresponding lookup sets.
+   *
+   * 3. The glyph set corresponding to AF_COVERAGE_DEFAULT is computed
+   *    with all lookups specific to the OpenType script activated.  It
+   *    relies on the order of AF_DEFINE_STYLE_CLASS entries so that
+   *    special coverages (like `oldstyle figures') don't get overwritten.
+   *
+   */
+
+
+  /* load coverage tags */
+#undef  COVERAGE
+#define COVERAGE( name, NAME, description,             \
+                  tag1, tag2, tag3, tag4 )             \
+          static const hb_tag_t  name ## _coverage[] = \
+          {                                            \
+            HB_TAG( tag1, tag2, tag3, tag4 ),          \
+            HB_TAG_NONE                                \
+          };
+
+
+#include "afcover.h"
+
+
+  /* define mapping between coverage tags and AF_Coverage */
+#undef  COVERAGE
+#define COVERAGE( name, NAME, description, \
+                  tag1, tag2, tag3, tag4 ) \
+          name ## _coverage,
+
+
+  static const hb_tag_t*  coverages[] =
+  {
+#include "afcover.h"
+
+    NULL /* AF_COVERAGE_DEFAULT */
+  };
+
+
+  /* load HarfBuzz script tags */
+#undef  SCRIPT
+#define SCRIPT( s, S, d, h, ss )  h,
+
+
+  static const hb_script_t  scripts[] =
+  {
+#include "afscript.h"
+  };
+
+
+  FT_Error
+  af_shaper_get_coverage( AF_FaceGlobals  globals,
+                          AF_StyleClass   style_class,
+                          FT_UShort*      gstyles )
+  {
+    hb_face_t*  face;
+
+    hb_set_t*  gsub_lookups;  /* GSUB lookups for a given script */
+    hb_set_t*  gsub_glyphs;   /* glyphs covered by GSUB lookups  */
+    hb_set_t*  gpos_lookups;  /* GPOS lookups for a given script */
+    hb_set_t*  gpos_glyphs;   /* glyphs covered by GPOS lookups  */
+
+    hb_script_t      script;
+    const hb_tag_t*  coverage_tags;
+    hb_tag_t         script_tags[] = { HB_TAG_NONE,
+                                       HB_TAG_NONE,
+                                       HB_TAG_NONE,
+                                       HB_TAG_NONE };
+
+    hb_codepoint_t  idx;
+#ifdef FT_DEBUG_LEVEL_TRACE
+    int             count;
+#endif
+
+
+    if ( !globals || !style_class || !gstyles )
+      return FT_THROW( Invalid_Argument );
+
+    face = hb_font_get_face( globals->hb_font );
+
+    gsub_lookups = hb_set_create();
+    gsub_glyphs  = hb_set_create();
+    gpos_lookups = hb_set_create();
+    gpos_glyphs  = hb_set_create();
+
+    coverage_tags = coverages[style_class->coverage];
+    script        = scripts[style_class->script];
+
+    /* Convert a HarfBuzz script tag into the corresponding OpenType */
+    /* tag or tags -- some Indic scripts like Devanagari have an old */
+    /* and a new set of features.                                    */
+    hb_ot_tags_from_script( script,
+                            &script_tags[0],
+                            &script_tags[1] );
+
+    /* `hb_ot_tags_from_script' usually returns HB_OT_TAG_DEFAULT_SCRIPT */
+    /* as the second tag.  We change that to HB_TAG_NONE except for the  */
+    /* default script.                                                   */
+    if ( style_class->script == globals->module->default_script &&
+         style_class->coverage == AF_COVERAGE_DEFAULT           )
+    {
+      if ( script_tags[0] == HB_TAG_NONE )
+        script_tags[0] = HB_OT_TAG_DEFAULT_SCRIPT;
+      else
+      {
+        if ( script_tags[1] == HB_TAG_NONE )
+          script_tags[1] = HB_OT_TAG_DEFAULT_SCRIPT;
+        else if ( script_tags[1] != HB_OT_TAG_DEFAULT_SCRIPT )
+          script_tags[2] = HB_OT_TAG_DEFAULT_SCRIPT;
+      }
+    }
+    else
+    {
+      if ( script_tags[1] == HB_OT_TAG_DEFAULT_SCRIPT )
+        script_tags[1] = HB_TAG_NONE;
+    }
+
+    hb_ot_layout_collect_lookups( face,
+                                  HB_OT_TAG_GSUB,
+                                  script_tags,
+                                  NULL,
+                                  coverage_tags,
+                                  gsub_lookups );
+
+    if ( hb_set_is_empty( gsub_lookups ) )
+      goto Exit; /* nothing to do */
+
+    hb_ot_layout_collect_lookups( face,
+                                  HB_OT_TAG_GPOS,
+                                  script_tags,
+                                  NULL,
+                                  coverage_tags,
+                                  gpos_lookups );
+
+    FT_TRACE4(( "GSUB lookups (style `%s'):\n"
+                " ",
+                af_style_names[style_class->style] ));
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+    count = 0;
+#endif
+
+    for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups, &idx ); )
+    {
+#ifdef FT_DEBUG_LEVEL_TRACE
+      FT_TRACE4(( " %d", idx ));
+      count++;
+#endif
+
+      /* get output coverage of GSUB feature */
+      hb_ot_layout_lookup_collect_glyphs( face,
+                                          HB_OT_TAG_GSUB,
+                                          idx,
+                                          NULL,
+                                          NULL,
+                                          NULL,
+                                          gsub_glyphs );
+    }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+    if ( !count )
+      FT_TRACE4(( " (none)" ));
+    FT_TRACE4(( "\n\n" ));
+#endif
+
+    FT_TRACE4(( "GPOS lookups (style `%s'):\n"
+                " ",
+                af_style_names[style_class->style] ));
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+    count = 0;
+#endif
+
+    for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gpos_lookups, &idx ); )
+    {
+#ifdef FT_DEBUG_LEVEL_TRACE
+      FT_TRACE4(( " %d", idx ));
+      count++;
+#endif
+
+      /* get input coverage of GPOS feature */
+      hb_ot_layout_lookup_collect_glyphs( face,
+                                          HB_OT_TAG_GPOS,
+                                          idx,
+                                          NULL,
+                                          gpos_glyphs,
+                                          NULL,
+                                          NULL );
+    }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+    if ( !count )
+      FT_TRACE4(( " (none)" ));
+    FT_TRACE4(( "\n\n" ));
+#endif
+
+    /*
+     * We now check whether we can construct blue zones, using glyphs
+     * covered by the feature only.  In case there is not a single zone
+     * (this is, not a single character is covered), we skip this coverage.
+     *
+     */
+    if ( style_class->coverage != AF_COVERAGE_DEFAULT )
+    {
+      AF_Blue_Stringset         bss = style_class->blue_stringset;
+      const AF_Blue_StringRec*  bs  = &af_blue_stringsets[bss];
+
+      FT_Bool  found = 0;
+
+
+      for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
+      {
+        const char*  p = &af_blue_strings[bs->string];
+
+
+        while ( *p )
+        {
+          hb_codepoint_t  ch;
+
+
+          GET_UTF8_CHAR( ch, p );
+
+          for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups,
+                                                         &idx ); )
+          {
+            hb_codepoint_t  gidx = FT_Get_Char_Index( globals->face, ch );
+
+
+            if ( hb_ot_layout_lookup_would_substitute( face, idx,
+                                                       &gidx, 1, 1 ) )
+            {
+              found = 1;
+              break;
+            }
+          }
+        }
+      }
+
+      if ( !found )
+      {
+        FT_TRACE4(( "  no blue characters found; style skipped\n" ));
+        goto Exit;
+      }
+    }
+
+    /*
+     * Various OpenType features might use the same glyphs at different
+     * vertical positions; for example, superscript and subscript glyphs
+     * could be the same.  However, the auto-hinter is completely
+     * agnostic of OpenType features after the feature analysis has been
+     * completed: The engine then simply receives a glyph index and returns a
+     * hinted and usually rendered glyph.
+     *
+     * Consider the superscript feature of font `pala.ttf': Some of the
+     * glyphs are `real', this is, they have a zero vertical offset, but
+     * most of them are small caps glyphs shifted up to the superscript
+     * position (this is, the `sups' feature is present in both the GSUB and
+     * GPOS tables).  The code for blue zones computation actually uses a
+     * feature's y offset so that the `real' glyphs get correct hints.  But
+     * later on it is impossible to decide whether a glyph index belongs to,
+     * say, the small caps or superscript feature.
+     *
+     * For this reason, we don't assign a style to a glyph if the current
+     * feature covers the glyph in both the GSUB and the GPOS tables.  This
+     * is quite a broad condition, assuming that
+     *
+     *   (a) glyphs that get used in multiple features are present in a
+     *       feature without vertical shift,
+     *
+     * and
+     *
+     *   (b) a feature's GPOS data really moves the glyph vertically.
+     *
+     * Not fulfilling condition (a) makes a font larger; it would also
+     * reduce the number of glyphs that could be addressed directly without
+     * using OpenType features, so this assumption is rather strong.
+     *
+     * Condition (b) is much weaker, and there might be glyphs which get
+     * missed.  However, the OpenType features we are going to handle are
+     * primarily located in GSUB, and HarfBuzz doesn't provide an API to
+     * directly get the necessary information from the GPOS table.  A
+     * possible solution might be to directly parse the GPOS table to find
+     * out whether a glyph gets shifted vertically, but this is something I
+     * would like to avoid if not really necessary.
+     *
+     * Note that we don't follow this logic for the default coverage.
+     * Complex scripts like Devanagari have mandatory GPOS features to
+     * position many glyph elements, using mark-to-base or mark-to-ligature
+     * tables; the number of glyphs missed due to condition (b) would be far
+     * too large.
+     *
+     */
+    if ( style_class->coverage != AF_COVERAGE_DEFAULT )
+      hb_set_subtract( gsub_glyphs, gpos_glyphs );
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+    FT_TRACE4(( "  glyphs without GPOS data (`*' means already assigned)" ));
+    count = 0;
+#endif
+
+    for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_glyphs, &idx ); )
+    {
+#ifdef FT_DEBUG_LEVEL_TRACE
+      if ( !( count % 10 ) )
+        FT_TRACE4(( "\n"
+                    "   " ));
+
+      FT_TRACE4(( " %d", idx ));
+      count++;
+#endif
+
+      /* glyph indices returned by `hb_ot_layout_lookup_collect_glyphs' */
+      /* can be arbitrary: some fonts use fake indices for processing   */
+      /* internal to GSUB or GPOS, which is fully valid                 */
+      if ( idx >= (hb_codepoint_t)globals->glyph_count )
+        continue;
+
+      if ( gstyles[idx] == AF_STYLE_UNASSIGNED )
+        gstyles[idx] = (FT_UShort)style_class->style;
+#ifdef FT_DEBUG_LEVEL_TRACE
+      else
+        FT_TRACE4(( "*" ));
+#endif
+    }
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+    if ( !count )
+      FT_TRACE4(( "\n"
+                  "    (none)" ));
+    FT_TRACE4(( "\n\n" ));
+#endif
+
+  Exit:
+    hb_set_destroy( gsub_lookups );
+    hb_set_destroy( gsub_glyphs  );
+    hb_set_destroy( gpos_lookups );
+    hb_set_destroy( gpos_glyphs  );
+
+    return FT_Err_Ok;
+  }
+
+
+  /* construct HarfBuzz features */
+#undef  COVERAGE
+#define COVERAGE( name, NAME, description,                \
+                  tag1, tag2, tag3, tag4 )                \
+          static const hb_feature_t  name ## _feature[] = \
+          {                                               \
+            {                                             \
+              HB_TAG( tag1, tag2, tag3, tag4 ),           \
+              1, 0, (unsigned int)-1                      \
+            }                                             \
+          };
+
+
+#include "afcover.h"
+
+
+  /* define mapping between HarfBuzz features and AF_Coverage */
+#undef  COVERAGE
+#define COVERAGE( name, NAME, description, \
+                  tag1, tag2, tag3, tag4 ) \
+          name ## _feature,
+
+
+  static const hb_feature_t*  features[] =
+  {
+#include "afcover.h"
+
+    NULL /* AF_COVERAGE_DEFAULT */
+  };
+
+
+  void*
+  af_shaper_buf_create( FT_Face  face )
+  {
+    FT_UNUSED( face );
+
+    return (void*)hb_buffer_create();
+  }
+
+
+  void
+  af_shaper_buf_destroy( FT_Face  face,
+                         void*    buf )
+  {
+    FT_UNUSED( face );
+
+    hb_buffer_destroy( (hb_buffer_t*)buf );
+  }
+
+
+  const char*
+  af_shaper_get_cluster( const char*      p,
+                         AF_StyleMetrics  metrics,
+                         void*            buf_,
+                         unsigned int*    count )
+  {
+    AF_StyleClass        style_class;
+    const hb_feature_t*  feature;
+    FT_Int               upem;
+    const char*          q;
+    int                  len;
+
+    hb_buffer_t*    buf = (hb_buffer_t*)buf_;
+    hb_font_t*      font;
+    hb_codepoint_t  dummy;
+
+
+    upem        = (FT_Int)metrics->globals->face->units_per_EM;
+    style_class = metrics->style_class;
+    feature     = features[style_class->coverage];
+
+    font = metrics->globals->hb_font;
+
+    /* we shape at a size of units per EM; this means font units */
+    hb_font_set_scale( font, upem, upem );
+
+    while ( *p == ' ' )
+      p++;
+
+    /* count characters up to next space (or end of buffer) */
+    q = p;
+    while ( !( *q == ' ' || *q == '\0' ) )
+      GET_UTF8_CHAR( dummy, q );
+    len = (int)( q - p );
+
+    /* feed character(s) to the HarfBuzz buffer */
+    hb_buffer_clear_contents( buf );
+    hb_buffer_add_utf8( buf, p, len, 0, len );
+
+    /* we let HarfBuzz guess the script and writing direction */
+    hb_buffer_guess_segment_properties( buf );
+
+    /* shape buffer, which means conversion from character codes to */
+    /* glyph indices, possibly applying a feature                   */
+    hb_shape( font, buf, feature, feature ? 1 : 0 );
+
+    *count = hb_buffer_get_length( buf );
+
+#ifdef FT_DEBUG_LEVEL_TRACE
+      if ( feature && *count > 1 )
+        FT_TRACE1(( "af_get_char_index:"
+                    " input character mapped to multiple glyphs\n" ));
+#endif
+
+    return q;
+  }
+
+
+  FT_ULong
+  af_shaper_get_elem( AF_StyleMetrics  metrics,
+                      void*            buf_,
+                      unsigned int     idx,
+                      FT_Long*         advance,
+                      FT_Long*         y_offset )
+  {
+    hb_buffer_t*          buf = (hb_buffer_t*)buf_;
+    hb_glyph_info_t*      ginfo;
+    hb_glyph_position_t*  gpos;
+    unsigned int          gcount;
+
+    FT_UNUSED( metrics );
+
+
+    ginfo = hb_buffer_get_glyph_infos( buf, &gcount );
+    gpos  = hb_buffer_get_glyph_positions( buf, &gcount );
+
+    if ( idx >= gcount )
+      return 0;
+
+    if ( advance )
+      *advance = gpos[idx].x_advance;
+    if ( y_offset )
+      *y_offset = gpos[idx].y_offset;
+
+    return ginfo[idx].codepoint;
+  }
+
+
+#else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
+
+
+  FT_Error
+  af_shaper_get_coverage( AF_FaceGlobals  globals,
+                          AF_StyleClass   style_class,
+                          FT_UShort*      gstyles )
+  {
+    FT_UNUSED( globals );
+    FT_UNUSED( style_class );
+    FT_UNUSED( gstyles );
+
+    return FT_Err_Ok;
+  }
+
+
+  void*
+  af_shaper_buf_create( FT_Face  face )
+  {
+    FT_Memory  memory = face->memory;
+    FT_ULong*  buf;
+
+
+    FT_ALLOC( buf, sizeof ( FT_ULong ) );
+
+    return (void*)buf;
+  }
+
+
+  void
+  af_shaper_buf_destroy( FT_Face  face,
+                         void*    buf )
+  {
+    FT_Memory  memory = face->memory;
+
+
+    FT_FREE( buf );
+  }
+
+
+  const char*
+  af_shaper_get_cluster( const char*      p,
+                         AF_StyleMetrics  metrics,
+                         void*            buf_,
+                         unsigned int*    count )
+  {
+    FT_Face    face      = metrics->globals->face;
+    FT_ULong   ch, dummy = 0;
+    FT_ULong*  buf       = (FT_ULong*)buf_;
+
+
+    while ( *p == ' ' )
+      p++;
+
+    GET_UTF8_CHAR( ch, p );
+
+    /* since we don't have an engine to handle clusters, */
+    /* we scan the characters but return zero            */
+    while ( !( *p == ' ' || *p == '\0' ) )
+      GET_UTF8_CHAR( dummy, p );
+
+    if ( dummy )
+    {
+      *buf   = 0;
+      *count = 0;
+    }
+    else
+    {
+      *buf   = FT_Get_Char_Index( face, ch );
+      *count = 1;
+    }
+
+    return p;
+  }
+
+
+  FT_ULong
+  af_shaper_get_elem( AF_StyleMetrics  metrics,
+                      void*            buf_,
+                      unsigned int     idx,
+                      FT_Long*         advance,
+                      FT_Long*         y_offset )
+  {
+    FT_Face   face        = metrics->globals->face;
+    FT_ULong  glyph_index = *(FT_ULong*)buf_;
+
+    FT_UNUSED( idx );
+
+
+    if ( advance )
+      FT_Get_Advance( face,
+                      glyph_index,
+                      FT_LOAD_NO_SCALE         |
+                      FT_LOAD_NO_HINTING       |
+                      FT_LOAD_IGNORE_TRANSFORM,
+                      advance ) )
+
+    if ( y_offset )
+      *y_offset = 0;
+
+    return glyph_index;
+  }
+
+
+#endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
+
+
+/* END */
--- /dev/null
+++ b/src/autofit/afshaper.h
@@ -1,0 +1,71 @@
+/***************************************************************************/
+/*                                                                         */
+/*  afshaper.h                                                             */
+/*                                                                         */
+/*    HarfBuzz interface for accessing OpenType features (specification).  */
+/*                                                                         */
+/*  Copyright 2013-2015 by                                                 */
+/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
+/*                                                                         */
+/*  This file is part of the FreeType project, and may only be used,       */
+/*  modified, and distributed under the terms of the FreeType project      */
+/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
+/*  this file you indicate that you have read the license and              */
+/*  understand and accept it fully.                                        */
+/*                                                                         */
+/***************************************************************************/
+
+
+#ifndef __AFSHAPER_H__
+#define __AFSHAPER_H__
+
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+
+#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
+
+#include <hb.h>
+#include <hb-ot.h>
+#include <hb-ft.h>
+
+#endif
+
+
+FT_BEGIN_HEADER
+
+  FT_Error
+  af_shaper_get_coverage( AF_FaceGlobals  globals,
+                          AF_StyleClass   style_class,
+                          FT_UShort*      gstyles );
+
+
+  void*
+  af_shaper_buf_create( FT_Face  face );
+
+  void
+  af_shaper_buf_destroy( FT_Face  face,
+                         void*    buf );
+
+  const char*
+  af_shaper_get_cluster( const char*      p,
+                         AF_StyleMetrics  metrics,
+                         void*            buf_,
+                         unsigned int*    count );
+
+  FT_ULong
+  af_shaper_get_elem( AF_StyleMetrics  metrics,
+                      void*            buf_,
+                      unsigned int     idx,
+                      FT_Long*         x_advance,
+                      FT_Long*         y_offset );
+
+ /* */
+
+FT_END_HEADER
+
+#endif /* __AFSHAPER_H__ */
+
+
+/* END */
--- a/src/autofit/autofit.c
+++ b/src/autofit/autofit.c
@@ -34,7 +34,7 @@
 #include "afcjk.c"
 #include "afindic.c"
 
-#include "hbshim.c"
+#include "afshaper.c"
 
 #include "afloader.c"
 #include "afmodule.c"
--- a/src/autofit/hbshim.c
+++ /dev/null
@@ -1,546 +1,0 @@
-/***************************************************************************/
-/*                                                                         */
-/*  hbshim.c                                                               */
-/*                                                                         */
-/*    HarfBuzz interface for accessing OpenType features (body).           */
-/*                                                                         */
-/*  Copyright 2013-2015 by                                                 */
-/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
-/*                                                                         */
-/*  This file is part of the FreeType project, and may only be used,       */
-/*  modified, and distributed under the terms of the FreeType project      */
-/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
-/*  this file you indicate that you have read the license and              */
-/*  understand and accept it fully.                                        */
-/*                                                                         */
-/***************************************************************************/
-
-
-#include <ft2build.h>
-#include FT_FREETYPE_H
-#include "afglobal.h"
-#include "aftypes.h"
-#include "hbshim.h"
-
-#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
-
-
-  /*************************************************************************/
-  /*                                                                       */
-  /* 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_afharfbuzz
-
-
-  /*
-   * We use `sets' (in the HarfBuzz sense, which comes quite near to the
-   * usual mathematical meaning) to manage both lookups and glyph indices.
-   *
-   * 1. For each coverage, collect lookup IDs in a set.  Note that an
-   *    auto-hinter `coverage' is represented by one `feature', and a
-   *    feature consists of an arbitrary number of (font specific) `lookup's
-   *    that actually do the mapping job.  Please check the OpenType
-   *    specification for more details on features and lookups.
-   *
-   * 2. Create glyph ID sets from the corresponding lookup sets.
-   *
-   * 3. The glyph set corresponding to AF_COVERAGE_DEFAULT is computed
-   *    with all lookups specific to the OpenType script activated.  It
-   *    relies on the order of AF_DEFINE_STYLE_CLASS entries so that
-   *    special coverages (like `oldstyle figures') don't get overwritten.
-   *
-   */
-
-
-  /* load coverage tags */
-#undef  COVERAGE
-#define COVERAGE( name, NAME, description,             \
-                  tag1, tag2, tag3, tag4 )             \
-          static const hb_tag_t  name ## _coverage[] = \
-          {                                            \
-            HB_TAG( tag1, tag2, tag3, tag4 ),          \
-            HB_TAG_NONE                                \
-          };
-
-
-#include "afcover.h"
-
-
-  /* define mapping between coverage tags and AF_Coverage */
-#undef  COVERAGE
-#define COVERAGE( name, NAME, description, \
-                  tag1, tag2, tag3, tag4 ) \
-          name ## _coverage,
-
-
-  static const hb_tag_t*  coverages[] =
-  {
-#include "afcover.h"
-
-    NULL /* AF_COVERAGE_DEFAULT */
-  };
-
-
-  /* load HarfBuzz script tags */
-#undef  SCRIPT
-#define SCRIPT( s, S, d, h, ss )  h,
-
-
-  static const hb_script_t  scripts[] =
-  {
-#include "afscript.h"
-  };
-
-
-  FT_Error
-  af_get_coverage( AF_FaceGlobals  globals,
-                   AF_StyleClass   style_class,
-                   FT_UShort*      gstyles )
-  {
-    hb_face_t*  face;
-
-    hb_set_t*  gsub_lookups;  /* GSUB lookups for a given script */
-    hb_set_t*  gsub_glyphs;   /* glyphs covered by GSUB lookups  */
-    hb_set_t*  gpos_lookups;  /* GPOS lookups for a given script */
-    hb_set_t*  gpos_glyphs;   /* glyphs covered by GPOS lookups  */
-
-    hb_script_t      script;
-    const hb_tag_t*  coverage_tags;
-    hb_tag_t         script_tags[] = { HB_TAG_NONE,
-                                       HB_TAG_NONE,
-                                       HB_TAG_NONE,
-                                       HB_TAG_NONE };
-
-    hb_codepoint_t  idx;
-#ifdef FT_DEBUG_LEVEL_TRACE
-    int             count;
-#endif
-
-
-    if ( !globals || !style_class || !gstyles )
-      return FT_THROW( Invalid_Argument );
-
-    face = hb_font_get_face( globals->hb_font );
-
-    gsub_lookups = hb_set_create();
-    gsub_glyphs  = hb_set_create();
-    gpos_lookups = hb_set_create();
-    gpos_glyphs  = hb_set_create();
-
-    coverage_tags = coverages[style_class->coverage];
-    script        = scripts[style_class->script];
-
-    /* Convert a HarfBuzz script tag into the corresponding OpenType */
-    /* tag or tags -- some Indic scripts like Devanagari have an old */
-    /* and a new set of features.                                    */
-    hb_ot_tags_from_script( script,
-                            &script_tags[0],
-                            &script_tags[1] );
-
-    /* `hb_ot_tags_from_script' usually returns HB_OT_TAG_DEFAULT_SCRIPT */
-    /* as the second tag.  We change that to HB_TAG_NONE except for the  */
-    /* default script.                                                   */
-    if ( style_class->script == globals->module->default_script &&
-         style_class->coverage == AF_COVERAGE_DEFAULT           )
-    {
-      if ( script_tags[0] == HB_TAG_NONE )
-        script_tags[0] = HB_OT_TAG_DEFAULT_SCRIPT;
-      else
-      {
-        if ( script_tags[1] == HB_TAG_NONE )
-          script_tags[1] = HB_OT_TAG_DEFAULT_SCRIPT;
-        else if ( script_tags[1] != HB_OT_TAG_DEFAULT_SCRIPT )
-          script_tags[2] = HB_OT_TAG_DEFAULT_SCRIPT;
-      }
-    }
-    else
-    {
-      if ( script_tags[1] == HB_OT_TAG_DEFAULT_SCRIPT )
-        script_tags[1] = HB_TAG_NONE;
-    }
-
-    hb_ot_layout_collect_lookups( face,
-                                  HB_OT_TAG_GSUB,
-                                  script_tags,
-                                  NULL,
-                                  coverage_tags,
-                                  gsub_lookups );
-
-    if ( hb_set_is_empty( gsub_lookups ) )
-      goto Exit; /* nothing to do */
-
-    hb_ot_layout_collect_lookups( face,
-                                  HB_OT_TAG_GPOS,
-                                  script_tags,
-                                  NULL,
-                                  coverage_tags,
-                                  gpos_lookups );
-
-    FT_TRACE4(( "GSUB lookups (style `%s'):\n"
-                " ",
-                af_style_names[style_class->style] ));
-
-#ifdef FT_DEBUG_LEVEL_TRACE
-    count = 0;
-#endif
-
-    for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups, &idx ); )
-    {
-#ifdef FT_DEBUG_LEVEL_TRACE
-      FT_TRACE4(( " %d", idx ));
-      count++;
-#endif
-
-      /* get output coverage of GSUB feature */
-      hb_ot_layout_lookup_collect_glyphs( face,
-                                          HB_OT_TAG_GSUB,
-                                          idx,
-                                          NULL,
-                                          NULL,
-                                          NULL,
-                                          gsub_glyphs );
-    }
-
-#ifdef FT_DEBUG_LEVEL_TRACE
-    if ( !count )
-      FT_TRACE4(( " (none)" ));
-    FT_TRACE4(( "\n\n" ));
-#endif
-
-    FT_TRACE4(( "GPOS lookups (style `%s'):\n"
-                " ",
-                af_style_names[style_class->style] ));
-
-#ifdef FT_DEBUG_LEVEL_TRACE
-    count = 0;
-#endif
-
-    for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gpos_lookups, &idx ); )
-    {
-#ifdef FT_DEBUG_LEVEL_TRACE
-      FT_TRACE4(( " %d", idx ));
-      count++;
-#endif
-
-      /* get input coverage of GPOS feature */
-      hb_ot_layout_lookup_collect_glyphs( face,
-                                          HB_OT_TAG_GPOS,
-                                          idx,
-                                          NULL,
-                                          gpos_glyphs,
-                                          NULL,
-                                          NULL );
-    }
-
-#ifdef FT_DEBUG_LEVEL_TRACE
-    if ( !count )
-      FT_TRACE4(( " (none)" ));
-    FT_TRACE4(( "\n\n" ));
-#endif
-
-    /*
-     * We now check whether we can construct blue zones, using glyphs
-     * covered by the feature only.  In case there is not a single zone
-     * (this is, not a single character is covered), we skip this coverage.
-     *
-     */
-    if ( style_class->coverage != AF_COVERAGE_DEFAULT )
-    {
-      AF_Blue_Stringset         bss = style_class->blue_stringset;
-      const AF_Blue_StringRec*  bs  = &af_blue_stringsets[bss];
-
-      FT_Bool  found = 0;
-
-
-      for ( ; bs->string != AF_BLUE_STRING_MAX; bs++ )
-      {
-        const char*  p = &af_blue_strings[bs->string];
-
-
-        while ( *p )
-        {
-          hb_codepoint_t  ch;
-
-
-          GET_UTF8_CHAR( ch, p );
-
-          for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_lookups,
-                                                         &idx ); )
-          {
-            hb_codepoint_t  gidx = FT_Get_Char_Index( globals->face, ch );
-
-
-            if ( hb_ot_layout_lookup_would_substitute( face, idx,
-                                                       &gidx, 1, 1 ) )
-            {
-              found = 1;
-              break;
-            }
-          }
-        }
-      }
-
-      if ( !found )
-      {
-        FT_TRACE4(( "  no blue characters found; style skipped\n" ));
-        goto Exit;
-      }
-    }
-
-    /*
-     * Various OpenType features might use the same glyphs at different
-     * vertical positions; for example, superscript and subscript glyphs
-     * could be the same.  However, the auto-hinter is completely
-     * agnostic of OpenType features after the feature analysis has been
-     * completed: The engine then simply receives a glyph index and returns a
-     * hinted and usually rendered glyph.
-     *
-     * Consider the superscript feature of font `pala.ttf': Some of the
-     * glyphs are `real', this is, they have a zero vertical offset, but
-     * most of them are small caps glyphs shifted up to the superscript
-     * position (this is, the `sups' feature is present in both the GSUB and
-     * GPOS tables).  The code for blue zones computation actually uses a
-     * feature's y offset so that the `real' glyphs get correct hints.  But
-     * later on it is impossible to decide whether a glyph index belongs to,
-     * say, the small caps or superscript feature.
-     *
-     * For this reason, we don't assign a style to a glyph if the current
-     * feature covers the glyph in both the GSUB and the GPOS tables.  This
-     * is quite a broad condition, assuming that
-     *
-     *   (a) glyphs that get used in multiple features are present in a
-     *       feature without vertical shift,
-     *
-     * and
-     *
-     *   (b) a feature's GPOS data really moves the glyph vertically.
-     *
-     * Not fulfilling condition (a) makes a font larger; it would also
-     * reduce the number of glyphs that could be addressed directly without
-     * using OpenType features, so this assumption is rather strong.
-     *
-     * Condition (b) is much weaker, and there might be glyphs which get
-     * missed.  However, the OpenType features we are going to handle are
-     * primarily located in GSUB, and HarfBuzz doesn't provide an API to
-     * directly get the necessary information from the GPOS table.  A
-     * possible solution might be to directly parse the GPOS table to find
-     * out whether a glyph gets shifted vertically, but this is something I
-     * would like to avoid if not really necessary.
-     *
-     * Note that we don't follow this logic for the default coverage.
-     * Complex scripts like Devanagari have mandatory GPOS features to
-     * position many glyph elements, using mark-to-base or mark-to-ligature
-     * tables; the number of glyphs missed due to condition (b) would be far
-     * too large.
-     *
-     */
-    if ( style_class->coverage != AF_COVERAGE_DEFAULT )
-      hb_set_subtract( gsub_glyphs, gpos_glyphs );
-
-#ifdef FT_DEBUG_LEVEL_TRACE
-    FT_TRACE4(( "  glyphs without GPOS data (`*' means already assigned)" ));
-    count = 0;
-#endif
-
-    for ( idx = HB_SET_VALUE_INVALID; hb_set_next( gsub_glyphs, &idx ); )
-    {
-#ifdef FT_DEBUG_LEVEL_TRACE
-      if ( !( count % 10 ) )
-        FT_TRACE4(( "\n"
-                    "   " ));
-
-      FT_TRACE4(( " %d", idx ));
-      count++;
-#endif
-
-      /* glyph indices returned by `hb_ot_layout_lookup_collect_glyphs' */
-      /* can be arbitrary: some fonts use fake indices for processing   */
-      /* internal to GSUB or GPOS, which is fully valid                 */
-      if ( idx >= (hb_codepoint_t)globals->glyph_count )
-        continue;
-
-      if ( gstyles[idx] == AF_STYLE_UNASSIGNED )
-        gstyles[idx] = (FT_UShort)style_class->style;
-#ifdef FT_DEBUG_LEVEL_TRACE
-      else
-        FT_TRACE4(( "*" ));
-#endif
-    }
-
-#ifdef FT_DEBUG_LEVEL_TRACE
-    if ( !count )
-      FT_TRACE4(( "\n"
-                  "    (none)" ));
-    FT_TRACE4(( "\n\n" ));
-#endif
-
-  Exit:
-    hb_set_destroy( gsub_lookups );
-    hb_set_destroy( gsub_glyphs  );
-    hb_set_destroy( gpos_lookups );
-    hb_set_destroy( gpos_glyphs  );
-
-    return FT_Err_Ok;
-  }
-
-
-  /* construct HarfBuzz features */
-#undef  COVERAGE
-#define COVERAGE( name, NAME, description,                \
-                  tag1, tag2, tag3, tag4 )                \
-          static const hb_feature_t  name ## _feature[] = \
-          {                                               \
-            {                                             \
-              HB_TAG( tag1, tag2, tag3, tag4 ),           \
-              1, 0, (unsigned int)-1                      \
-            }                                             \
-          };
-
-
-#include "afcover.h"
-
-
-  /* define mapping between HarfBuzz features and AF_Coverage */
-#undef  COVERAGE
-#define COVERAGE( name, NAME, description, \
-                  tag1, tag2, tag3, tag4 ) \
-          name ## _feature,
-
-
-  static const hb_feature_t*  features[] =
-  {
-#include "afcover.h"
-
-    NULL /* AF_COVERAGE_DEFAULT */
-  };
-
-
-  FT_Error
-  af_get_char_index( AF_StyleMetrics  metrics,
-                     FT_ULong         charcode,
-                     FT_ULong        *codepoint,
-                     FT_Long         *y_offset )
-  {
-    AF_StyleClass  style_class;
-
-    const hb_feature_t*  feature;
-
-    FT_ULong  in_idx, out_idx;
-
-
-    if ( !metrics )
-      return FT_THROW( Invalid_Argument );
-
-    in_idx = FT_Get_Char_Index( metrics->globals->face, charcode );
-
-    style_class = metrics->style_class;
-
-    feature = features[style_class->coverage];
-
-    if ( feature )
-    {
-      FT_Int  upem = (FT_Int)metrics->globals->face->units_per_EM;
-
-      hb_font_t*    font = metrics->globals->hb_font;
-      hb_buffer_t*  buf  = hb_buffer_create();
-
-      uint32_t  c = (uint32_t)charcode;
-
-      hb_glyph_info_t*      ginfo;
-      hb_glyph_position_t*  gpos;
-      unsigned int          gcount;
-
-
-      /* we shape at a size of units per EM; this means font units */
-      hb_font_set_scale( font, upem, upem );
-
-      /* XXX: is this sufficient for a single character of any script? */
-      hb_buffer_set_direction( buf, HB_DIRECTION_LTR );
-      hb_buffer_set_script( buf, scripts[style_class->script] );
-
-      /* we add one character to `buf' ... */
-      hb_buffer_add_utf32( buf, &c, 1, 0, 1 );
-
-      /* ... and apply one feature */
-      hb_shape( font, buf, feature, 1 );
-
-      ginfo = hb_buffer_get_glyph_infos( buf, &gcount );
-      gpos  = hb_buffer_get_glyph_positions( buf, &gcount );
-
-      out_idx = ginfo[0].codepoint;
-
-      /* getting the same index indicates no substitution,         */
-      /* which means that the glyph isn't available in the feature */
-      if ( in_idx == out_idx )
-      {
-        *codepoint = 0;
-        *y_offset  = 0;
-      }
-      else
-      {
-        *codepoint = out_idx;
-        *y_offset  = gpos[0].y_offset;
-      }
-
-      hb_buffer_destroy( buf );
-
-#ifdef FT_DEBUG_LEVEL_TRACE
-      if ( gcount > 1 )
-        FT_TRACE1(( "af_get_char_index:"
-                    " input character mapped to multiple glyphs\n" ));
-#endif
-    }
-    else
-    {
-      *codepoint = in_idx;
-      *y_offset  = 0;
-    }
-
-    return FT_Err_Ok;
-  }
-
-
-#else /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
-
-
-  FT_Error
-  af_get_coverage( AF_FaceGlobals  globals,
-                   AF_StyleClass   style_class,
-                   FT_UShort*      gstyles )
-  {
-    FT_UNUSED( globals );
-    FT_UNUSED( style_class );
-    FT_UNUSED( gstyles );
-
-    return FT_Err_Ok;
-  }
-
-
-  FT_Error
-  af_get_char_index( AF_StyleMetrics  metrics,
-                     FT_ULong         charcode,
-                     FT_ULong        *codepoint,
-                     FT_Long         *y_offset )
-  {
-    FT_Face  face;
-
-
-    if ( !metrics )
-      return FT_THROW( Invalid_Argument );
-
-    face = metrics->globals->face;
-
-    *codepoint = FT_Get_Char_Index( face, charcode );
-    *y_offset  = 0;
-
-    return FT_Err_Ok;
-  }
-
-
-#endif /* !FT_CONFIG_OPTION_USE_HARFBUZZ */
-
-
-/* END */
--- a/src/autofit/hbshim.h
+++ /dev/null
@@ -1,56 +1,0 @@
-/***************************************************************************/
-/*                                                                         */
-/*  hbshim.h                                                               */
-/*                                                                         */
-/*    HarfBuzz interface for accessing OpenType features (specification).  */
-/*                                                                         */
-/*  Copyright 2013-2015 by                                                 */
-/*  David Turner, Robert Wilhelm, and Werner Lemberg.                      */
-/*                                                                         */
-/*  This file is part of the FreeType project, and may only be used,       */
-/*  modified, and distributed under the terms of the FreeType project      */
-/*  license, LICENSE.TXT.  By continuing to use, modify, or distribute     */
-/*  this file you indicate that you have read the license and              */
-/*  understand and accept it fully.                                        */
-/*                                                                         */
-/***************************************************************************/
-
-
-#ifndef __HBSHIM_H__
-#define __HBSHIM_H__
-
-
-#include <ft2build.h>
-#include FT_FREETYPE_H
-
-
-#ifdef FT_CONFIG_OPTION_USE_HARFBUZZ
-
-#include <hb.h>
-#include <hb-ot.h>
-#include <hb-ft.h>
-
-#endif
-
-
-FT_BEGIN_HEADER
-
-  FT_Error
-  af_get_coverage( AF_FaceGlobals  globals,
-                   AF_StyleClass   style_class,
-                   FT_UShort*      gstyles );
-
-  FT_Error
-  af_get_char_index( AF_StyleMetrics  metrics,
-                     FT_ULong         charcode,
-                     FT_ULong        *codepoint,
-                     FT_Long         *y_offset );
-
- /* */
-
-FT_END_HEADER
-
-#endif /* __HBSHIM_H__ */
-
-
-/* END */
--- a/src/autofit/rules.mk
+++ b/src/autofit/rules.mk
@@ -40,8 +40,8 @@
                  $(AUTOF_DIR)/afmodule.c \
                  $(AUTOF_DIR)/afpic.c    \
                  $(AUTOF_DIR)/afranges.c \
-                 $(AUTOF_DIR)/afwarp.c   \
-                 $(AUTOF_DIR)/hbshim.c
+                 $(AUTOF_DIR)/afshaper.c \
+                 $(AUTOF_DIR)/afwarp.c
 
 # AUTOF driver headers
 #