[HarfBuzz] harfbuzz-ng: Branch 'master' - 15 commits

Behdad Esfahbod behdad at kemper.freedesktop.org
Thu Apr 21 13:59:40 PDT 2011


 TODO                                    |   22 -
 src/Makefile.am                         |   19 
 src/hb-blob-private.h                   |   59 --
 src/hb-blob-private.hh                  |   59 ++
 src/hb-blob.c                           |  362 -----------------
 src/hb-blob.cc                          |  356 ++++++++++++++++
 src/hb-blob.h                           |    3 
 src/hb-buffer-private.hh                |    2 
 src/hb-buffer.cc                        |    6 
 src/hb-buffer.h                         |    3 
 src/hb-common.c                         |  222 ----------
 src/hb-common.cc                        |  222 ++++++++++
 src/hb-font-private.h                   |   97 ----
 src/hb-font-private.hh                  |   97 ++++
 src/hb-font.cc                          |   38 -
 src/hb-font.h                           |   24 -
 src/hb-ft.c                             |  262 ------------
 src/hb-ft.cc                            |  261 ++++++++++++
 src/hb-ft.h                             |    2 
 src/hb-glib.cc                          |    2 
 src/hb-icu.cc                           |  139 ------
 src/hb-object-private.h                 |  141 ------
 src/hb-object-private.hh                |  130 ++++++
 src/hb-open-type-private.hh             |    5 
 src/hb-ot-layout-common-private.hh      |    6 
 src/hb-ot-layout-gdef-private.hh        |    2 
 src/hb-ot-layout-private.hh             |    6 
 src/hb-ot-layout.cc                     |    6 
 src/hb-ot-shape-complex-arabic-table.h  |  674 -------------------------------
 src/hb-ot-shape-complex-arabic-table.hh |  674 +++++++++++++++++++++++++++++++
 src/hb-ot-shape-complex-arabic.cc       |    2 
 src/hb-ot-shape-complex-private.hh      |    2 
 src/hb-ot-shape-private.hh              |    2 
 src/hb-ot-tag.c                         |  677 --------------------------------
 src/hb-ot-tag.cc                        |  677 ++++++++++++++++++++++++++++++++
 src/hb-private.h                        |  301 --------------
 src/hb-private.hh                       |  303 ++++++++++++++
 src/hb-shape.cc                         |    4 
 src/hb-unicode-private.hh               |    2 
 src/hb-unicode.cc                       |    8 
 src/hb-unicode.h                        |    3 
 src/hb-view.c                           |   12 
 test/test-unicode.c                     |   24 -
 43 files changed, 2847 insertions(+), 3071 deletions(-)

New commits:
commit 08da7a3841ca7dfcb627314cae1c3a668b9c7236
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Apr 21 16:59:10 2011 -0400

    [hb-view] Accept numbers in feature tag name
    
    Reported by Adam Twardoch.

diff --git a/src/hb-view.c b/src/hb-view.c
index ac55908..41adf20 100644
--- a/src/hb-view.c
+++ b/src/hb-view.c
@@ -233,10 +233,10 @@ parse_feature_tag (char **pp, hb_feature_t *feature)
 
   parse_space (pp);
 
-#define ISALPHA(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z'))
-  while (c = **pp, ISALPHA(c))
+#define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9'))
+  while (c = **pp, ISALNUM(c))
     (*pp)++;
-#undef ISALPHA
+#undef ISALNUM
 
   if (p == *pp)
     return FALSE;
commit 24229eb13268a422efffbcb28a094b726824c7f0
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Apr 21 16:55:17 2011 -0400

    Remove obsolete comment
    
    Talking to Ryan Lortie, he thinks my comment doesn't make sense.
    
    So I'm making the getter const.  Note that g_atomic_int_get()
    casts that away itself, so we don't need to worry about that
    (which kinda makes me uncomfortable actually).

diff --git a/src/hb-object-private.hh b/src/hb-object-private.hh
index 74bc3ad..0a055e9 100644
--- a/src/hb-object-private.hh
+++ b/src/hb-object-private.hh
@@ -46,20 +46,8 @@ typedef struct {
   inline int dec (void) { return hb_atomic_int_fetch_and_add (ref_count, -1); }
   inline void set (int v) { return hb_atomic_int_set (ref_count, v); }
 
-  /* XXX
-   *
-   * One thing I'm not sure.  The following two methods should be declared
-   * const.  However, that assumes that hb_atomic_int_get() is const.  I have
-   * a vague memory hearing from Chris Wilson or Jeff Muizelaar that atomic get
-   * is implemented as a fetch_and_add(0).  In which case it does write to the
-   * memory, and hence cannot be called on .rodata section.  But that's how we
-   * use it.
-   *
-   * If that is indeed the case, then perhaps is_invalid() should do a
-   * non-protected read of the location.
-   */
-  inline int get (void) { return hb_atomic_int_get (ref_count); }
-  inline bool is_invalid (void) { return get () == HB_REFERENCE_COUNT_INVALID_VALUE; }
+  inline int get (void) const { return hb_atomic_int_get (ref_count); }
+  inline bool is_invalid (void) const { return get () == HB_REFERENCE_COUNT_INVALID_VALUE; }
 
 } hb_reference_count_t;
 
commit dcb7026f33cbcdf60e9b7fcdd44c64cc08702c74
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Apr 21 16:34:22 2011 -0400

    Add ASSERT_STATIC_EXPR macro
    
    Unused right now.

diff --git a/src/hb-private.hh b/src/hb-private.hh
index ae52889..85561d2 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -78,6 +78,8 @@ HB_BEGIN_DECLS
 #define _ASSERT_STATIC0(_line, _cond) _ASSERT_STATIC1 (_line, (_cond))
 #define ASSERT_STATIC(_cond) _ASSERT_STATIC0 (__LINE__, (_cond))
 
+#define ASSERT_STATIC_EXPR(_cond) ((void) sizeof (char[(_cond) ? 1 : -1]))
+
 
 /* Lets assert int types.  Saves trouble down the road. */
 
commit 3e8bdbf9414291da5cf61213d5f4275c1ae23ae5
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Apr 21 16:16:21 2011 -0400

    Cleanup hb_refrence_count_t

diff --git a/src/hb-object-private.hh b/src/hb-object-private.hh
index 930f616..74bc3ad 100644
--- a/src/hb-object-private.hh
+++ b/src/hb-object-private.hh
@@ -38,18 +38,31 @@ HB_BEGIN_DECLS
 typedef struct {
   hb_atomic_int_t ref_count;
 
+#define HB_REFERENCE_COUNT_INVALID_VALUE ((hb_atomic_int_t) -1)
+#define HB_REFERENCE_COUNT_INVALID {HB_REFERENCE_COUNT_INVALID_VALUE}
+
   inline void init (int v) { ref_count = v; /* non-atomic is fine */ }
   inline int inc (void) { return hb_atomic_int_fetch_and_add (ref_count,  1); }
   inline int dec (void) { return hb_atomic_int_fetch_and_add (ref_count, -1); }
   inline void set (int v) { return hb_atomic_int_set (ref_count, v); }
+
+  /* XXX
+   *
+   * One thing I'm not sure.  The following two methods should be declared
+   * const.  However, that assumes that hb_atomic_int_get() is const.  I have
+   * a vague memory hearing from Chris Wilson or Jeff Muizelaar that atomic get
+   * is implemented as a fetch_and_add(0).  In which case it does write to the
+   * memory, and hence cannot be called on .rodata section.  But that's how we
+   * use it.
+   *
+   * If that is indeed the case, then perhaps is_invalid() should do a
+   * non-protected read of the location.
+   */
   inline int get (void) { return hb_atomic_int_get (ref_count); }
+  inline bool is_invalid (void) { return get () == HB_REFERENCE_COUNT_INVALID_VALUE; }
 
 } hb_reference_count_t;
 
-#define HB_REFERENCE_COUNT_INVALID_VALUE ((hb_atomic_int_t) -1)
-#define HB_REFERENCE_COUNT_INVALID {HB_REFERENCE_COUNT_INVALID_VALUE}
-
-#define HB_REFERENCE_COUNT_IS_INVALID(RC) ((RC).get () == HB_REFERENCE_COUNT_INVALID_VALUE)
 
 
 /* Debug */
@@ -77,7 +90,7 @@ _hb_trace_object (const void *obj,
 /* Object allocation and lifecycle manamgement macros */
 
 #define HB_OBJECT_IS_INERT(obj) \
-    (unlikely (HB_REFERENCE_COUNT_IS_INVALID ((obj)->ref_count)))
+    (unlikely ((obj)->ref_count.is_invalid ()))
 
 #define HB_OBJECT_DO_INIT_EXPR(obj) \
     obj->ref_count.init (1)
diff --git a/src/hb-private.hh b/src/hb-private.hh
index d08a4d0..ae52889 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -212,7 +212,7 @@ typedef int (*hb_compare_func_t) (const void *, const void *);
 
 #include <glib.h>
 
-typedef int hb_atomic_int_t;
+typedef volatile int hb_atomic_int_t;
 #define hb_atomic_int_fetch_and_add(AI, V)	g_atomic_int_exchange_and_add (&(AI), V)
 #define hb_atomic_int_get(AI)			g_atomic_int_get (&(AI))
 #define hb_atomic_int_set(AI, V)		g_atomic_int_set (&(AI), V)
@@ -235,12 +235,12 @@ typedef GStaticMutex hb_mutex_t;
 #warning "Could not find any system to define platform macros, library will NOT be thread-safe"
 #endif
 
-typedef int hb_atomic_int_t;
+typedef volatile int hb_atomic_int_t;
 #define hb_atomic_int_fetch_and_add(AI, V)	((AI) += (V), (AI) - (V))
 #define hb_atomic_int_get(AI)			(AI)
 #define hb_atomic_int_set(AI, V)		HB_STMT_START { (AI) = (V); } HB_STMT_END
 
-typedef int hb_mutex_t;
+typedef volatile int hb_mutex_t;
 #define HB_MUTEX_INIT				0
 #define hb_mutex_init(M)			HB_STMT_START { (M) = 0; } HB_STMT_END
 #define hb_mutex_lock(M)			HB_STMT_START { (M) = 1; } HB_STMT_END
commit 783a7d69696bf0b1502ec9c1495e482e491c78e0
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Apr 21 16:03:59 2011 -0400

    [TODO] Remove finished items

diff --git a/TODO b/TODO
index b5eb3be..c90857e 100644
--- a/TODO
+++ b/TODO
@@ -9,8 +9,6 @@ General fixes:
 
 - Remove fixed-size feature/lookup arrays in hb-ot-map
 
-- Use size_t in sanitize
-
 - Use templates instead of macros for objects?
 
 API issues to fix before 1.0:
@@ -18,8 +16,6 @@ API issues to fix before 1.0:
 
 - Figure out how many .so objects, how to link, etc
 
-- Shall y axis progress downward instead of upward?
-
 - User-data support ala cairo
 
 - Real subclassing support for vfunc vectors
@@ -28,7 +24,7 @@ API issues to fix before 1.0:
 
 - Fix blob, remove mutex, etc.
 
-- Add sanitize API (since may affect blob API)
+- Add sanitize API
 
 - Add glib GBoxedType stuff
 
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index f583ce5..f792ba0 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -178,7 +178,6 @@ get_table  (hb_tag_t tag, void *user_data)
   if (error)
     return NULL;
 
-  /* TODO Use FT_Memory? */
   buffer = (FT_Byte *) malloc (length);
   if (buffer == NULL)
     return NULL;
commit da975419884a535281745f30f4b32fee0bc8a7a1
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Apr 21 15:08:01 2011 -0400

    [API] Allow negative font x_scale/y_scale
    
    I was reconsidering whether y should grow down, since all three/four
    times I've used this API I was tricked and got that wrong in my use.
    So I was very inclined to make y grow down instead of up.  However,
    considering that the font space has y up and it would be very confusing
    for callbacks to work against that, I decided that what I really want
    is for the user to be able to set y_scale to a negative number to imply
    that user-space y grows down.
    
    Changing x_scale/y_scale from unsigned int to int allows that, and I've
    made pango to use that instead of negating glyph y_offset later.  hb-ft
    however still has y group up.  I *guess* that's how FreeType works?
    I'm not sure, FreeType docs don't make this clear...
    
    I'm happy with the resolution :-).

diff --git a/src/hb-font.cc b/src/hb-font.cc
index a84dde4..733eb3b 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -523,8 +523,8 @@ hb_font_unset_funcs (hb_font_t          *font,
 
 void
 hb_font_set_scale (hb_font_t *font,
-		   unsigned int x_scale,
-		   unsigned int y_scale)
+		   int x_scale,
+		   int y_scale)
 {
   if (HB_OBJECT_IS_INERT (font))
     return;
@@ -535,8 +535,8 @@ hb_font_set_scale (hb_font_t *font,
 
 void
 hb_font_get_scale (hb_font_t *font,
-		   unsigned int *x_scale,
-		   unsigned int *y_scale)
+		   int *x_scale,
+		   int *y_scale)
 {
   if (x_scale) *x_scale = font->x_scale;
   if (y_scale) *y_scale = font->y_scale;
diff --git a/src/hb-font.h b/src/hb-font.h
index 397b586..d9d6090 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -231,13 +231,13 @@ hb_font_unset_funcs (hb_font_t          *font,
  */
 void
 hb_font_set_scale (hb_font_t *font,
-		   unsigned int x_scale,
-		   unsigned int y_scale);
+		   int x_scale,
+		   int y_scale);
 
 void
 hb_font_get_scale (hb_font_t *font,
-		   unsigned int *x_scale,
-		   unsigned int *y_scale);
+		   int *x_scale,
+		   int *y_scale);
 
 /*
  * A zero value means "no hinting in that direction"
diff --git a/src/hb-ot-layout-common-private.hh b/src/hb-ot-layout-common-private.hh
index 9ff5ca9..00a1432 100644
--- a/src/hb-ot-layout-common-private.hh
+++ b/src/hb-ot-layout-common-private.hh
@@ -532,7 +532,7 @@ struct Device
   inline hb_position_t get_y_delta (hb_ot_layout_context_t *c) const
   { return get_delta (c->font->y_ppem, c->font->y_scale); }
 
-  inline int get_delta (unsigned int ppem, unsigned int scale) const
+  inline int get_delta (unsigned int ppem, int scale) const
   {
     if (!ppem) return 0;
 
@@ -540,10 +540,6 @@ struct Device
 
     if (!pixels) return 0;
 
-    /* pixels is at most in the -8..7 range.  So 64-bit arithmetic is
-     * not really necessary here.  A simple cast to int may just work
-     * as well.  But since this code is not reached that often and
-     * for the sake of correctness, we do a 64bit operation. */
     return pixels * (int64_t) scale / ppem;
   }
 
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index a802c63..b032a7a 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -77,7 +77,7 @@ struct hb_ot_layout_context_t
   inline hb_position_t scale_y (int16_t v) { return scale (v, this->font->y_scale); }
 
   private:
-  inline hb_position_t scale (int16_t v, unsigned int scale) { return v * (int64_t) scale / this->face->head_table->get_upem (); }
+  inline hb_position_t scale (int16_t v, int scale) { return v * (int64_t) scale / this->face->head_table->get_upem (); }
 };
 
 
commit 4d559cddbb3b3a5c12c5167eba69598618a9f283
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Thu Apr 21 14:58:23 2011 -0400

    [icu] Remove big script switch(), rely on reverse-lookup

diff --git a/src/hb-icu.cc b/src/hb-icu.cc
index 2f31a07..5ff3d0f 100644
--- a/src/hb-icu.cc
+++ b/src/hb-icu.cc
@@ -40,140 +40,21 @@ HB_BEGIN_DECLS
 hb_script_t
 hb_icu_script_to_script (UScriptCode script)
 {
+  if (unlikely (script == USCRIPT_INVALID_CODE))
+    return HB_SCRIPT_INVALID;
+
   return hb_script_from_string (uscript_getShortName (script));
 }
 
 UScriptCode
 hb_icu_script_from_script (hb_script_t script)
 {
-  switch ((int) script)
-  {
-#define CHECK_ICU_VERSION(major, minor) \
-	U_ICU_VERSION_MAJOR_NUM > (major) || (U_ICU_VERSION_MAJOR_NUM == (major) && U_ICU_VERSION_MINOR_NUM >= (minor))
-#define MATCH_SCRIPT(C) case HB_SCRIPT_##C: return USCRIPT_##C
-#define MATCH_SCRIPT2(C1, C2) case HB_SCRIPT_##C2: return USCRIPT_##C1
-
-  MATCH_SCRIPT2(INVALID_CODE, INVALID);
-
-  MATCH_SCRIPT (COMMON);
-  MATCH_SCRIPT (INHERITED);
-  MATCH_SCRIPT (ARABIC);
-  MATCH_SCRIPT (ARMENIAN);
-  MATCH_SCRIPT (BENGALI);
-  MATCH_SCRIPT (BOPOMOFO);
-  MATCH_SCRIPT (CHEROKEE);
-  MATCH_SCRIPT (COPTIC);
-  MATCH_SCRIPT (CYRILLIC);
-  MATCH_SCRIPT (DESERET);
-  MATCH_SCRIPT (DEVANAGARI);
-  MATCH_SCRIPT (ETHIOPIC);
-  MATCH_SCRIPT (GEORGIAN);
-  MATCH_SCRIPT (GOTHIC);
-  MATCH_SCRIPT (GREEK);
-  MATCH_SCRIPT (GUJARATI);
-  MATCH_SCRIPT (GURMUKHI);
-  MATCH_SCRIPT (HAN);
-  MATCH_SCRIPT (HANGUL);
-  MATCH_SCRIPT (HEBREW);
-  MATCH_SCRIPT (HIRAGANA);
-  MATCH_SCRIPT (KANNADA);
-  MATCH_SCRIPT (KATAKANA);
-  MATCH_SCRIPT (KHMER);
-  MATCH_SCRIPT (LAO);
-  MATCH_SCRIPT (LATIN);
-  MATCH_SCRIPT (MALAYALAM);
-  MATCH_SCRIPT (MONGOLIAN);
-  MATCH_SCRIPT (MYANMAR);
-  MATCH_SCRIPT (OGHAM);
-  MATCH_SCRIPT (OLD_ITALIC);
-  MATCH_SCRIPT (ORIYA);
-  MATCH_SCRIPT (RUNIC);
-  MATCH_SCRIPT (SINHALA);
-  MATCH_SCRIPT (SYRIAC);
-  MATCH_SCRIPT (TAMIL);
-  MATCH_SCRIPT (TELUGU);
-  MATCH_SCRIPT (THAANA);
-  MATCH_SCRIPT (THAI);
-  MATCH_SCRIPT (TIBETAN);
-  MATCH_SCRIPT (CANADIAN_ABORIGINAL);
-  MATCH_SCRIPT (YI);
-  MATCH_SCRIPT (TAGALOG);
-  MATCH_SCRIPT (HANUNOO);
-  MATCH_SCRIPT (BUHID);
-  MATCH_SCRIPT (TAGBANWA);
-
-  /* Unicode-4.0 additions */
-  MATCH_SCRIPT (BRAILLE);
-  MATCH_SCRIPT (CYPRIOT);
-  MATCH_SCRIPT (LIMBU);
-  MATCH_SCRIPT (OSMANYA);
-  MATCH_SCRIPT (SHAVIAN);
-  MATCH_SCRIPT (LINEAR_B);
-  MATCH_SCRIPT (TAI_LE);
-  MATCH_SCRIPT (UGARITIC);
-
-  /* Unicode-4.1 additions */
-  MATCH_SCRIPT (NEW_TAI_LUE);
-  MATCH_SCRIPT (BUGINESE);
-  MATCH_SCRIPT (GLAGOLITIC);
-  MATCH_SCRIPT (TIFINAGH);
-  MATCH_SCRIPT (SYLOTI_NAGRI);
-  MATCH_SCRIPT (OLD_PERSIAN);
-  MATCH_SCRIPT (KHAROSHTHI);
-
-  /* Unicode-5.0 additions */
-  MATCH_SCRIPT (UNKNOWN);
-  MATCH_SCRIPT (BALINESE);
-  MATCH_SCRIPT (CUNEIFORM);
-  MATCH_SCRIPT (PHOENICIAN);
-  MATCH_SCRIPT (PHAGS_PA);
-  MATCH_SCRIPT (NKO);
-
-  /* Unicode-5.1 additions */
-  MATCH_SCRIPT (KAYAH_LI);
-  MATCH_SCRIPT (LEPCHA);
-  MATCH_SCRIPT (REJANG);
-  MATCH_SCRIPT (SUNDANESE);
-  MATCH_SCRIPT (SAURASHTRA);
-  MATCH_SCRIPT (CHAM);
-  MATCH_SCRIPT (OL_CHIKI);
-  MATCH_SCRIPT (VAI);
-  MATCH_SCRIPT (CARIAN);
-  MATCH_SCRIPT (LYCIAN);
-  MATCH_SCRIPT (LYDIAN);
-
-  /* Unicode-5.2 additions */
-  MATCH_SCRIPT (AVESTAN);
-#if CHECK_ICU_VERSION (4, 4)
-  MATCH_SCRIPT (BAMUM);
-#endif
-  MATCH_SCRIPT (EGYPTIAN_HIEROGLYPHS);
-  MATCH_SCRIPT (IMPERIAL_ARAMAIC);
-  MATCH_SCRIPT (INSCRIPTIONAL_PAHLAVI);
-  MATCH_SCRIPT (INSCRIPTIONAL_PARTHIAN);
-  MATCH_SCRIPT (JAVANESE);
-  MATCH_SCRIPT (KAITHI);
-  MATCH_SCRIPT2(LANNA, TAI_THAM);
-#if CHECK_ICU_VERSION (4, 4)
-  MATCH_SCRIPT (LISU);
-#endif
-  MATCH_SCRIPT2(MEITEI_MAYEK, MEETEI_MAYEK);
-#if CHECK_ICU_VERSION (4, 4)
-  MATCH_SCRIPT (OLD_SOUTH_ARABIAN);
-#endif
-  MATCH_SCRIPT2(ORKHON, OLD_TURKIC);
-  MATCH_SCRIPT (SAMARITAN);
-  MATCH_SCRIPT (TAI_VIET);
-
-  /* Unicode-6.0 additions */
-  MATCH_SCRIPT (BATAK);
-  MATCH_SCRIPT (BRAHMI);
-  MATCH_SCRIPT2(MANDAEAN, MANDAIC);
-
-#undef CHECK_ICU_VERSION
-#undef MATCH_SCRIPT
-#undef MATCH_SCRIPT2
-  }
+  if (unlikely (script == HB_SCRIPT_INVALID))
+    return USCRIPT_INVALID_CODE;
+
+  for (unsigned int i = 0; i < USCRIPT_CODE_LIMIT; i++)
+    if (unlikely (hb_icu_script_to_script ((UScriptCode) i) == script))
+      return (UScriptCode) i;
 
   return USCRIPT_UNKNOWN;
 }
commit d18431b4cd8c1b14523733cd60a62b862f5b471f
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Apr 20 18:59:10 2011 -0400

    Move hb_reference_count_t from macros to inline methods

diff --git a/src/hb-object-private.hh b/src/hb-object-private.hh
index 8f0d5ae..930f616 100644
--- a/src/hb-object-private.hh
+++ b/src/hb-object-private.hh
@@ -35,24 +35,21 @@
 HB_BEGIN_DECLS
 
 
-/* Encapsulate operations on the object's reference count */
 typedef struct {
   hb_atomic_int_t ref_count;
-} hb_reference_count_t;
-
-#define hb_reference_count_inc(RC) hb_atomic_int_fetch_and_add ((RC).ref_count, 1)
-#define hb_reference_count_dec(RC) hb_atomic_int_fetch_and_add ((RC).ref_count, -1)
 
-#define HB_REFERENCE_COUNT_INIT(RC, VALUE) ((RC).ref_count = (VALUE))
+  inline void init (int v) { ref_count = v; /* non-atomic is fine */ }
+  inline int inc (void) { return hb_atomic_int_fetch_and_add (ref_count,  1); }
+  inline int dec (void) { return hb_atomic_int_fetch_and_add (ref_count, -1); }
+  inline void set (int v) { return hb_atomic_int_set (ref_count, v); }
+  inline int get (void) { return hb_atomic_int_get (ref_count); }
 
-#define HB_REFERENCE_COUNT_GET_VALUE(RC) hb_atomic_int_get ((RC).ref_count)
-#define HB_REFERENCE_COUNT_SET_VALUE(RC, VALUE) hb_atomic_int_set ((RC).ref_count, (VALUE))
+} hb_reference_count_t;
 
 #define HB_REFERENCE_COUNT_INVALID_VALUE ((hb_atomic_int_t) -1)
 #define HB_REFERENCE_COUNT_INVALID {HB_REFERENCE_COUNT_INVALID_VALUE}
 
-#define HB_REFERENCE_COUNT_IS_INVALID(RC) (HB_REFERENCE_COUNT_GET_VALUE (RC) == HB_REFERENCE_COUNT_INVALID_VALUE)
-
+#define HB_REFERENCE_COUNT_IS_INVALID(RC) ((RC).get () == HB_REFERENCE_COUNT_INVALID_VALUE)
 
 
 /* Debug */
@@ -69,7 +66,7 @@ _hb_trace_object (const void *obj,
   (void) (HB_DEBUG_OBJECT &&
 	  fprintf (stderr, "OBJECT(%p) refcount=%d %s\n",
 		   obj,
-		   HB_REFERENCE_COUNT_GET_VALUE (*ref_count),
+		   ref_count->get (),
 		   function));
 }
 
@@ -83,7 +80,7 @@ _hb_trace_object (const void *obj,
     (unlikely (HB_REFERENCE_COUNT_IS_INVALID ((obj)->ref_count)))
 
 #define HB_OBJECT_DO_INIT_EXPR(obj) \
-    HB_REFERENCE_COUNT_INIT (obj->ref_count, 1)
+    obj->ref_count.init (1)
 
 #define HB_OBJECT_DO_INIT(obj) \
   HB_STMT_START { \
@@ -109,7 +106,7 @@ _hb_trace_object (const void *obj,
     if (unlikely (!(obj) || HB_OBJECT_IS_INERT (obj))) \
       return obj; \
     TRACE_OBJECT (obj); \
-    old_count = hb_reference_count_inc (obj->ref_count); \
+    old_count = obj->ref_count.inc (); \
     assert (old_count > 0); \
     return obj; \
   } HB_STMT_END
@@ -120,7 +117,7 @@ _hb_trace_object (const void *obj,
     if (unlikely (!(obj) || HB_OBJECT_IS_INERT (obj))) \
       return; \
     TRACE_OBJECT (obj); \
-    old_count = hb_reference_count_dec (obj->ref_count); \
+    old_count = obj->ref_count.dec (); \
     assert (old_count > 0); \
     if (old_count != 1) \
       return; \
commit c57d454accff66e5f2c58006e8fb40bc020b6182
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Apr 20 18:50:27 2011 -0400

    Rename all private sources and headers to C++ files
    
    So we can liberally use the simple features of C++ that parts of the
    codebase is already using.

diff --git a/src/Makefile.am b/src/Makefile.am
index 845c24a..1069307 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -12,17 +12,17 @@ HBCFLAGS =
 HBLIBS =
 HBSOURCES =  \
 	hb-blob.cc \
-	hb-blob-private.h \
+	hb-blob-private.hh \
 	hb-buffer.cc \
 	hb-buffer-private.hh \
-	hb-common.c \
+	hb-common.cc \
 	hb-font.cc \
-	hb-font-private.h \
-	hb-object-private.h \
+	hb-font-private.hh \
+	hb-object-private.hh \
 	hb-open-file-private.hh \
 	hb-open-type-private.hh \
 	hb-ot-head-private.hh \
-	hb-private.h \
+	hb-private.hh \
 	hb-shape.cc \
 	hb-unicode.cc \
 	hb-unicode-private.hh \
@@ -49,10 +49,10 @@ HBSOURCES += \
 	hb-ot-map-private.hh \
 	hb-ot-shape.cc \
 	hb-ot-shape-complex-arabic.cc \
-	hb-ot-shape-complex-arabic-table.h \
+	hb-ot-shape-complex-arabic-table.hh \
 	hb-ot-shape-complex-private.hh \
 	hb-ot-shape-private.hh \
-	hb-ot-tag.c \
+	hb-ot-tag.cc \
 	$(NULL)
 HBHEADERS += \
 	hb-ot.h \
@@ -87,7 +87,7 @@ if HAVE_FREETYPE
 HBCFLAGS += $(FREETYPE_CFLAGS)
 HBLIBS   += $(FREETYPE_LIBS)
 HBSOURCES += \
-	hb-ft.c \
+	hb-ft.cc \
 	$(NULL)
 HBHEADERS += \
 	hb-ft.h \
@@ -142,5 +142,6 @@ else
 dist_check_SCRIPTS += check-libstdc++.sh
 endif
 
+TESTS = $(dist_check_SCRIPTS)
 
 -include $(top_srcdir)/git.mk
diff --git a/src/hb-blob-private.h b/src/hb-blob-private.h
deleted file mode 100644
index cdc9b2f..0000000
--- a/src/hb-blob-private.h
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
- * Copyright (C) 2010  Red Hat, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Red Hat Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_BLOB_PRIVATE_H
-#define HB_BLOB_PRIVATE_H
-
-#include "hb-private.h"
-
-#include "hb-blob.h"
-
-HB_BEGIN_DECLS
-
-
-struct _hb_blob_t {
-  hb_reference_count_t ref_count;
-
-  unsigned int length;
-
-  hb_mutex_t lock;
-  /* the rest are protected by lock */
-
-  unsigned int lock_count;
-  hb_memory_mode_t mode;
-
-  const char *data;
-
-  void *user_data;
-  hb_destroy_func_t destroy;
-};
-
-extern HB_INTERNAL hb_blob_t _hb_blob_nil;
-
-
-HB_END_DECLS
-
-#endif /* HB_BLOB_PRIVATE_H */
diff --git a/src/hb-blob-private.hh b/src/hb-blob-private.hh
new file mode 100644
index 0000000..4b421da
--- /dev/null
+++ b/src/hb-blob-private.hh
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010  Red Hat, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_BLOB_PRIVATE_HH
+#define HB_BLOB_PRIVATE_HH
+
+#include "hb-private.hh"
+
+#include "hb-blob.h"
+
+HB_BEGIN_DECLS
+
+
+struct _hb_blob_t {
+  hb_reference_count_t ref_count;
+
+  unsigned int length;
+
+  hb_mutex_t lock;
+  /* the rest are protected by lock */
+
+  unsigned int lock_count;
+  hb_memory_mode_t mode;
+
+  const char *data;
+
+  void *user_data;
+  hb_destroy_func_t destroy;
+};
+
+extern HB_INTERNAL hb_blob_t _hb_blob_nil;
+
+
+HB_END_DECLS
+
+#endif /* HB_BLOB_PRIVATE_HH */
diff --git a/src/hb-blob.cc b/src/hb-blob.cc
index 511455b..e2af8b7 100644
--- a/src/hb-blob.cc
+++ b/src/hb-blob.cc
@@ -24,9 +24,9 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.h"
+#include "hb-private.hh"
 
-#include "hb-blob-private.h"
+#include "hb-blob-private.hh"
 
 #ifdef HAVE_SYS_MMAN_H
 #ifdef HAVE_UNISTD_H
diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh
index dfcc45d..4c2a3e6 100644
--- a/src/hb-buffer-private.hh
+++ b/src/hb-buffer-private.hh
@@ -28,7 +28,7 @@
 #ifndef HB_BUFFER_PRIVATE_HH
 #define HB_BUFFER_PRIVATE_HH
 
-#include "hb-private.h"
+#include "hb-private.hh"
 #include "hb-buffer.h"
 #include "hb-unicode-private.hh"
 
diff --git a/src/hb-common.c b/src/hb-common.c
deleted file mode 100644
index c1c4c54..0000000
--- a/src/hb-common.c
+++ /dev/null
@@ -1,222 +0,0 @@
-/*
- * Copyright (C) 2009,2010  Red Hat, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Red Hat Author(s): Behdad Esfahbod
- */
-
-#include "hb-private.h"
-
-HB_BEGIN_DECLS
-
-
-/* hb_tag_t */
-
-hb_tag_t
-hb_tag_from_string (const char *s)
-{
-  char tag[4];
-  unsigned int i;
-
-  if (!s || !*s)
-    return HB_TAG_NONE;
-
-  for (i = 0; i < 4 && s[i]; i++)
-    tag[i] = s[i];
-  for (; i < 4; i++)
-    tag[i] = ' ';
-
-  return HB_TAG_CHAR4 (tag);
-}
-
-
-/* hb_language_t */
-
-struct _hb_language_t {
-  const char s[1];
-};
-
-static const char canon_map[256] = {
-   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
-   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
-   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,  '-',  0,   0,
-  '0', '1', '2', '3', '4', '5', '6', '7',  '8', '9',  0,   0,   0,   0,   0,   0,
-  '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
-  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,  '-',
-   0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
-  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,   0
-};
-
-static hb_bool_t
-lang_equal (const void *v1,
-	    const void *v2)
-{
-  const unsigned char *p1 = v1;
-  const unsigned char *p2 = v2;
-
-  while (canon_map[*p1] && canon_map[*p1] == canon_map[*p2])
-    {
-      p1++, p2++;
-    }
-
-  return (canon_map[*p1] == canon_map[*p2]);
-}
-
-#if 0
-static unsigned int
-lang_hash (const void *key)
-{
-  const unsigned char *p = key;
-  unsigned int h = 0;
-  while (canon_map[*p])
-    {
-      h = (h << 5) - h + canon_map[*p];
-      p++;
-    }
-
-  return h;
-}
-#endif
-
-
-hb_language_t
-hb_language_from_string (const char *str)
-{
-  static unsigned int num_langs;
-  static unsigned int num_alloced;
-  static hb_language_t *langs;
-  unsigned int i;
-  unsigned char *p;
-
-  /* TODO Use a hash table or something */
-
-  if (!str || !*str)
-    return NULL;
-
-  for (i = 0; i < num_langs; i++)
-    if (lang_equal (str, langs[i]->s))
-      return langs[i];
-
-  if (unlikely (num_langs == num_alloced)) {
-    unsigned int new_alloced = 2 * (8 + num_alloced);
-    hb_language_t *new_langs = realloc (langs, new_alloced * sizeof (langs[0]));
-    if (!new_langs)
-      return NULL;
-    num_alloced = new_alloced;
-    langs = new_langs;
-  }
-
-  langs[i] = (hb_language_t) strdup (str);
-  for (p = (unsigned char *) langs[i]->s; *p; p++)
-    *p = canon_map[*p];
-
-  num_langs++;
-
-  return langs[i];
-}
-
-const char *
-hb_language_to_string (hb_language_t language)
-{
-  return language->s;
-}
-
-
-/* hb_script_t */
-
-hb_script_t
-hb_script_from_iso15924_tag (hb_tag_t tag)
-{
-  if (unlikely (tag == HB_TAG_NONE))
-    return HB_SCRIPT_INVALID;
-
-  /* Be lenient, adjust case (one capital letter followed by three small letters) */
-  tag = (tag & 0xDFDFDFDF) | 0x00202020;
-
-  switch (tag) {
-    case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
-    case HB_TAG('G','e','o','a'): return HB_SCRIPT_GEORGIAN;
-    case HB_TAG('G','e','o','n'): return HB_SCRIPT_GEORGIAN;
-    case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
-    case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
-    case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
-    case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
-    case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
-  }
-
-  /* If it looks right, just use the tag as a script */
-  if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
-    return (hb_script_t) tag;
-
-  /* Otherwise, return unknown */
-  return HB_SCRIPT_UNKNOWN;
-}
-
-hb_script_t
-hb_script_from_string (const char *s)
-{
-  return hb_script_from_iso15924_tag (hb_tag_from_string (s));
-}
-
-hb_tag_t
-hb_script_to_iso15924_tag (hb_script_t script)
-{
-  return (hb_tag_t) script;
-}
-
-hb_direction_t
-hb_script_get_horizontal_direction (hb_script_t script)
-{
-  switch ((hb_tag_t) script)
-  {
-    case HB_SCRIPT_ARABIC:
-    case HB_SCRIPT_HEBREW:
-    case HB_SCRIPT_SYRIAC:
-    case HB_SCRIPT_THAANA:
-
-    /* Unicode-4.0 additions */
-    case HB_SCRIPT_CYPRIOT:
-
-    /* Unicode-5.0 additions */
-    case HB_SCRIPT_PHOENICIAN:
-    case HB_SCRIPT_NKO:
-
-    /* Unicode-5.2 additions */
-    case HB_SCRIPT_AVESTAN:
-    case HB_SCRIPT_IMPERIAL_ARAMAIC:
-    case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
-    case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
-    case HB_SCRIPT_OLD_SOUTH_ARABIAN:
-    case HB_SCRIPT_OLD_TURKIC:
-    case HB_SCRIPT_SAMARITAN:
-
-    /* Unicode-6.0 additions */
-    case HB_SCRIPT_MANDAIC:
-
-      return HB_DIRECTION_RTL;
-  }
-
-  return HB_DIRECTION_LTR;
-}
-
-
-HB_END_DECLS
diff --git a/src/hb-common.cc b/src/hb-common.cc
new file mode 100644
index 0000000..ece0980
--- /dev/null
+++ b/src/hb-common.cc
@@ -0,0 +1,222 @@
+/*
+ * Copyright (C) 2009,2010  Red Hat, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.hh"
+
+HB_BEGIN_DECLS
+
+
+/* hb_tag_t */
+
+hb_tag_t
+hb_tag_from_string (const char *s)
+{
+  char tag[4];
+  unsigned int i;
+
+  if (!s || !*s)
+    return HB_TAG_NONE;
+
+  for (i = 0; i < 4 && s[i]; i++)
+    tag[i] = s[i];
+  for (; i < 4; i++)
+    tag[i] = ' ';
+
+  return HB_TAG_CHAR4 (tag);
+}
+
+
+/* hb_language_t */
+
+struct _hb_language_t {
+  const char s[1];
+};
+
+static const char canon_map[256] = {
+   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,   0,   0,   0,
+   0,   0,   0,   0,   0,   0,   0,   0,    0,   0,   0,   0,   0,  '-',  0,   0,
+  '0', '1', '2', '3', '4', '5', '6', '7',  '8', '9',  0,   0,   0,   0,   0,   0,
+  '-', 'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,  '-',
+   0,  'a', 'b', 'c', 'd', 'e', 'f', 'g',  'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',
+  'p', 'q', 'r', 's', 't', 'u', 'v', 'w',  'x', 'y', 'z',  0,   0,   0,   0,   0
+};
+
+static hb_bool_t
+lang_equal (const void *v1,
+	    const void *v2)
+{
+  const unsigned char *p1 = (const unsigned char *) v1;
+  const unsigned char *p2 = (const unsigned char *) v2;
+
+  while (canon_map[*p1] && canon_map[*p1] == canon_map[*p2])
+    {
+      p1++, p2++;
+    }
+
+  return (canon_map[*p1] == canon_map[*p2]);
+}
+
+#if 0
+static unsigned int
+lang_hash (const void *key)
+{
+  const unsigned char *p = key;
+  unsigned int h = 0;
+  while (canon_map[*p])
+    {
+      h = (h << 5) - h + canon_map[*p];
+      p++;
+    }
+
+  return h;
+}
+#endif
+
+
+hb_language_t
+hb_language_from_string (const char *str)
+{
+  static unsigned int num_langs;
+  static unsigned int num_alloced;
+  static hb_language_t *langs;
+  unsigned int i;
+  unsigned char *p;
+
+  /* TODO Use a hash table or something */
+
+  if (!str || !*str)
+    return NULL;
+
+  for (i = 0; i < num_langs; i++)
+    if (lang_equal (str, langs[i]->s))
+      return langs[i];
+
+  if (unlikely (num_langs == num_alloced)) {
+    unsigned int new_alloced = 2 * (8 + num_alloced);
+    hb_language_t *new_langs = (hb_language_t *) realloc (langs, new_alloced * sizeof (langs[0]));
+    if (!new_langs)
+      return NULL;
+    num_alloced = new_alloced;
+    langs = new_langs;
+  }
+
+  langs[i] = (hb_language_t) strdup (str);
+  for (p = (unsigned char *) langs[i]->s; *p; p++)
+    *p = canon_map[*p];
+
+  num_langs++;
+
+  return langs[i];
+}
+
+const char *
+hb_language_to_string (hb_language_t language)
+{
+  return language->s;
+}
+
+
+/* hb_script_t */
+
+hb_script_t
+hb_script_from_iso15924_tag (hb_tag_t tag)
+{
+  if (unlikely (tag == HB_TAG_NONE))
+    return HB_SCRIPT_INVALID;
+
+  /* Be lenient, adjust case (one capital letter followed by three small letters) */
+  tag = (tag & 0xDFDFDFDF) | 0x00202020;
+
+  switch (tag) {
+    case HB_TAG('C','y','r','s'): return HB_SCRIPT_CYRILLIC;
+    case HB_TAG('G','e','o','a'): return HB_SCRIPT_GEORGIAN;
+    case HB_TAG('G','e','o','n'): return HB_SCRIPT_GEORGIAN;
+    case HB_TAG('L','a','t','f'): return HB_SCRIPT_LATIN;
+    case HB_TAG('L','a','t','g'): return HB_SCRIPT_LATIN;
+    case HB_TAG('S','y','r','e'): return HB_SCRIPT_SYRIAC;
+    case HB_TAG('S','y','r','j'): return HB_SCRIPT_SYRIAC;
+    case HB_TAG('S','y','r','n'): return HB_SCRIPT_SYRIAC;
+  }
+
+  /* If it looks right, just use the tag as a script */
+  if (((uint32_t) tag & 0xE0E0E0E0) == 0x40606060)
+    return (hb_script_t) tag;
+
+  /* Otherwise, return unknown */
+  return HB_SCRIPT_UNKNOWN;
+}
+
+hb_script_t
+hb_script_from_string (const char *s)
+{
+  return hb_script_from_iso15924_tag (hb_tag_from_string (s));
+}
+
+hb_tag_t
+hb_script_to_iso15924_tag (hb_script_t script)
+{
+  return (hb_tag_t) script;
+}
+
+hb_direction_t
+hb_script_get_horizontal_direction (hb_script_t script)
+{
+  switch ((hb_tag_t) script)
+  {
+    case HB_SCRIPT_ARABIC:
+    case HB_SCRIPT_HEBREW:
+    case HB_SCRIPT_SYRIAC:
+    case HB_SCRIPT_THAANA:
+
+    /* Unicode-4.0 additions */
+    case HB_SCRIPT_CYPRIOT:
+
+    /* Unicode-5.0 additions */
+    case HB_SCRIPT_PHOENICIAN:
+    case HB_SCRIPT_NKO:
+
+    /* Unicode-5.2 additions */
+    case HB_SCRIPT_AVESTAN:
+    case HB_SCRIPT_IMPERIAL_ARAMAIC:
+    case HB_SCRIPT_INSCRIPTIONAL_PAHLAVI:
+    case HB_SCRIPT_INSCRIPTIONAL_PARTHIAN:
+    case HB_SCRIPT_OLD_SOUTH_ARABIAN:
+    case HB_SCRIPT_OLD_TURKIC:
+    case HB_SCRIPT_SAMARITAN:
+
+    /* Unicode-6.0 additions */
+    case HB_SCRIPT_MANDAIC:
+
+      return HB_DIRECTION_RTL;
+  }
+
+  return HB_DIRECTION_LTR;
+}
+
+
+HB_END_DECLS
diff --git a/src/hb-font-private.h b/src/hb-font-private.h
deleted file mode 100644
index 46686b7..0000000
--- a/src/hb-font-private.h
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * Copyright (C) 2009  Red Hat, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Red Hat Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_FONT_PRIVATE_H
-#define HB_FONT_PRIVATE_H
-
-#include "hb-private.h"
-
-#include "hb-font.h"
-
-HB_BEGIN_DECLS
-
-
-/*
- * hb_font_funcs_t
- */
-
-struct _hb_font_funcs_t {
-  hb_reference_count_t ref_count;
-
-  hb_bool_t immutable;
-
-  struct {
-    hb_font_get_glyph_func_t		get_glyph;
-    hb_font_get_glyph_advance_func_t	get_glyph_advance;
-    hb_font_get_glyph_extents_func_t	get_glyph_extents;
-    hb_font_get_contour_point_func_t	get_contour_point;
-    hb_font_get_kerning_func_t		get_kerning;
-  } v;
-};
-
-extern HB_INTERNAL hb_font_funcs_t _hb_font_funcs_nil;
-
-
-/*
- * hb_face_t
- */
-
-struct _hb_face_t {
-  hb_reference_count_t ref_count;
-
-  hb_get_table_func_t  get_table;
-  void                *user_data;
-  hb_destroy_func_t    destroy;
-
-  hb_blob_t *head_blob;
-  const struct head *head_table;
-
-  struct hb_ot_layout_t *ot_layout;
-};
-
-
-/*
- * hb_font_t
- */
-
-struct _hb_font_t {
-  hb_reference_count_t ref_count;
-
-  unsigned int x_scale;
-  unsigned int y_scale;
-
-  unsigned int x_ppem;
-  unsigned int y_ppem;
-
-  hb_font_funcs_t   *klass;
-  void              *user_data;
-  hb_destroy_func_t  destroy;
-};
-
-
-HB_END_DECLS
-
-#endif /* HB_FONT_PRIVATE_H */
diff --git a/src/hb-font-private.hh b/src/hb-font-private.hh
new file mode 100644
index 0000000..da7255a
--- /dev/null
+++ b/src/hb-font-private.hh
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2009  Red Hat, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_FONT_PRIVATE_HH
+#define HB_FONT_PRIVATE_HH
+
+#include "hb-private.hh"
+
+#include "hb-font.h"
+
+HB_BEGIN_DECLS
+
+
+/*
+ * hb_font_funcs_t
+ */
+
+struct _hb_font_funcs_t {
+  hb_reference_count_t ref_count;
+
+  hb_bool_t immutable;
+
+  struct {
+    hb_font_get_glyph_func_t		get_glyph;
+    hb_font_get_glyph_advance_func_t	get_glyph_advance;
+    hb_font_get_glyph_extents_func_t	get_glyph_extents;
+    hb_font_get_contour_point_func_t	get_contour_point;
+    hb_font_get_kerning_func_t		get_kerning;
+  } v;
+};
+
+extern HB_INTERNAL hb_font_funcs_t _hb_font_funcs_nil;
+
+
+/*
+ * hb_face_t
+ */
+
+struct _hb_face_t {
+  hb_reference_count_t ref_count;
+
+  hb_get_table_func_t  get_table;
+  void                *user_data;
+  hb_destroy_func_t    destroy;
+
+  hb_blob_t *head_blob;
+  const struct head *head_table;
+
+  struct hb_ot_layout_t *ot_layout;
+};
+
+
+/*
+ * hb_font_t
+ */
+
+struct _hb_font_t {
+  hb_reference_count_t ref_count;
+
+  unsigned int x_scale;
+  unsigned int y_scale;
+
+  unsigned int x_ppem;
+  unsigned int y_ppem;
+
+  hb_font_funcs_t   *klass;
+  void              *user_data;
+  hb_destroy_func_t  destroy;
+};
+
+
+HB_END_DECLS
+
+#endif /* HB_FONT_PRIVATE_HH */
diff --git a/src/hb-font.cc b/src/hb-font.cc
index 96ff4f5..a84dde4 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -24,10 +24,10 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.h"
+#include "hb-private.hh"
 
-#include "hb-font-private.h"
-#include "hb-blob-private.h"
+#include "hb-font-private.hh"
+#include "hb-blob-private.hh"
 #include "hb-open-file-private.hh"
 
 #include "hb-ot-layout-private.hh"
diff --git a/src/hb-ft.c b/src/hb-ft.c
deleted file mode 100644
index 96c035a..0000000
--- a/src/hb-ft.c
+++ /dev/null
@@ -1,262 +0,0 @@
-/*
- * Copyright (C) 2009  Red Hat, Inc.
- * Copyright (C) 2009  Keith Stribley
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Red Hat Author(s): Behdad Esfahbod
- */
-
-#include "hb-private.h"
-
-#include "hb-ft.h"
-
-#include "hb-font-private.h"
-
-#include FT_TRUETYPE_TABLES_H
-
-HB_BEGIN_DECLS
-
-
-static hb_codepoint_t
-hb_ft_get_glyph (hb_font_t *font HB_UNUSED,
-		 hb_face_t *face HB_UNUSED,
-		 const void *user_data,
-		 hb_codepoint_t unicode,
-		 hb_codepoint_t variation_selector)
-{
-  FT_Face ft_face = (FT_Face) user_data;
-
-#ifdef HAVE_FT_FACE_GETCHARVARIANTINDEX
-  if (unlikely (variation_selector)) {
-    hb_codepoint_t glyph = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector);
-    if (glyph)
-      return glyph;
-  }
-#endif
-
-  return FT_Get_Char_Index (ft_face, unicode);
-}
-
-static void
-hb_ft_get_glyph_advance (hb_font_t *font HB_UNUSED,
-			 hb_face_t *face HB_UNUSED,
-			 const void *user_data,
-			 hb_codepoint_t glyph,
-			 hb_position_t *x_advance,
-			 hb_position_t *y_advance)
-{
-  FT_Face ft_face = (FT_Face) user_data;
-  int load_flags = FT_LOAD_DEFAULT;
-
-  /* TODO: load_flags, embolden, etc */
-
-  if (likely (!FT_Load_Glyph (ft_face, glyph, load_flags)))
-  {
-    *x_advance = ft_face->glyph->advance.x;
-    *y_advance = ft_face->glyph->advance.y;
-  }
-}
-
-static void
-hb_ft_get_glyph_extents (hb_font_t *font HB_UNUSED,
-			 hb_face_t *face HB_UNUSED,
-			 const void *user_data,
-			 hb_codepoint_t glyph,
-			 hb_glyph_extents_t *extents)
-{
-  FT_Face ft_face = (FT_Face) user_data;
-  int load_flags = FT_LOAD_DEFAULT;
-
-  /* TODO: load_flags, embolden, etc */
-
-  if (likely (!FT_Load_Glyph (ft_face, glyph, load_flags)))
-  {
-    /* XXX: A few negations should be in order here, not sure. */
-    extents->x_bearing = ft_face->glyph->metrics.horiBearingX;
-    extents->y_bearing = ft_face->glyph->metrics.horiBearingY;
-    extents->width = ft_face->glyph->metrics.width;
-    extents->height = ft_face->glyph->metrics.height;
-  }
-}
-
-static hb_bool_t
-hb_ft_get_contour_point (hb_font_t *font HB_UNUSED,
-			 hb_face_t *face HB_UNUSED,
-			 const void *user_data,
-			 unsigned int point_index,
-			 hb_codepoint_t glyph,
-			 hb_position_t *x,
-			 hb_position_t *y)
-{
-  FT_Face ft_face = (FT_Face) user_data;
-  int load_flags = FT_LOAD_DEFAULT;
-
-  /* TODO: load_flags, embolden, etc */
-
-  if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
-      return FALSE;
-
-  if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE))
-      return FALSE;
-
-  if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points))
-      return FALSE;
-
-  *x = ft_face->glyph->outline.points[point_index].x;
-  *y = ft_face->glyph->outline.points[point_index].y;
-
-  return TRUE;
-}
-
-static hb_position_t
-hb_ft_get_kerning (hb_font_t *font HB_UNUSED,
-		   hb_face_t *face HB_UNUSED,
-		   const void *user_data,
-		   hb_codepoint_t first_glyph,
-		   hb_codepoint_t second_glyph)
-{
-  FT_Face ft_face = (FT_Face) user_data;
-  FT_Vector kerning;
-
-  /* TODO: Kern type? */
-  if (FT_Get_Kerning (ft_face, first_glyph, second_glyph, FT_KERNING_DEFAULT, &kerning))
-      return 0;
-
-  return kerning.x;
-}
-
-static hb_font_funcs_t ft_ffuncs = {
-  HB_REFERENCE_COUNT_INVALID, /* ref_count */
-  TRUE, /* immutable */
-  {
-    hb_ft_get_glyph,
-    hb_ft_get_glyph_advance,
-    hb_ft_get_glyph_extents,
-    hb_ft_get_contour_point,
-    hb_ft_get_kerning
-  }
-};
-
-hb_font_funcs_t *
-hb_ft_get_font_funcs (void)
-{
-  return &ft_ffuncs;
-}
-
-
-static hb_blob_t *
-get_table  (hb_tag_t tag, void *user_data)
-{
-  FT_Face ft_face = (FT_Face) user_data;
-  FT_Byte *buffer;
-  FT_ULong  length = 0;
-  FT_Error error;
-
-  if (unlikely (tag == HB_TAG_NONE))
-    return NULL;
-
-  error = FT_Load_Sfnt_Table (ft_face, tag, 0, NULL, &length);
-  if (error)
-    return NULL;
-
-  /* TODO Use FT_Memory? */
-  buffer = (FT_Byte *) malloc (length);
-  if (buffer == NULL)
-    return NULL;
-
-  error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length);
-  if (error)
-    return NULL;
-
-  return hb_blob_create ((const char *) buffer, length,
-			 HB_MEMORY_MODE_WRITABLE,
-			 buffer, free);
-}
-
-
-hb_face_t *
-hb_ft_face_create (FT_Face           ft_face,
-		   hb_destroy_func_t destroy)
-{
-  hb_face_t *face;
-
-  if (ft_face->stream->read == NULL) {
-    hb_blob_t *blob;
-
-    blob = hb_blob_create ((const char *) ft_face->stream->base,
-			   (unsigned int) ft_face->stream->size,
-			   /* TODO: Check FT_FACE_FLAG_EXTERNAL_STREAM? */
-			   HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE,
-			   ft_face, destroy);
-    face = hb_face_create_for_data (blob, ft_face->face_index);
-    hb_blob_destroy (blob);
-  } else {
-    face = hb_face_create_for_tables (get_table, ft_face, destroy);
-  }
-
-  return face;
-}
-
-static void
-hb_ft_face_finalize (FT_Face ft_face)
-{
-  hb_face_destroy ((hb_face_t *) ft_face->generic.data);
-}
-
-hb_face_t *
-hb_ft_face_create_cached (FT_Face ft_face)
-{
-  if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize))
-  {
-    if (ft_face->generic.finalizer)
-      ft_face->generic.finalizer (ft_face);
-
-    ft_face->generic.data = hb_ft_face_create (ft_face, NULL);
-    ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize;
-  }
-
-  return hb_face_reference ((hb_face_t *) ft_face->generic.data);
-}
-
-
-hb_font_t *
-hb_ft_font_create (FT_Face           ft_face,
-		   hb_destroy_func_t destroy)
-{
-  hb_font_t *font;
-
-  font = hb_font_create ();
-  hb_font_set_funcs (font,
-		     hb_ft_get_font_funcs (),
-		     ft_face, destroy);
-  hb_font_set_scale (font,
-		     ((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM) >> 16,
-		     ((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM) >> 16);
-  hb_font_set_ppem (font,
-		    ft_face->size->metrics.x_ppem,
-		    ft_face->size->metrics.y_ppem);
-
-  return font;
-}
-
-
-HB_END_DECLS
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
new file mode 100644
index 0000000..f583ce5
--- /dev/null
+++ b/src/hb-ft.cc
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2009  Red Hat, Inc.
+ * Copyright (C) 2009  Keith Stribley
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.hh"
+
+#include "hb-ft.h"
+
+#include "hb-font-private.hh"
+
+#include FT_TRUETYPE_TABLES_H
+
+HB_BEGIN_DECLS
+
+
+static hb_codepoint_t
+hb_ft_get_glyph (hb_font_t *font HB_UNUSED,
+		 hb_face_t *face HB_UNUSED,
+		 const void *user_data,
+		 hb_codepoint_t unicode,
+		 hb_codepoint_t variation_selector)
+{
+  FT_Face ft_face = (FT_Face) user_data;
+
+#ifdef HAVE_FT_FACE_GETCHARVARIANTINDEX
+  if (unlikely (variation_selector)) {
+    hb_codepoint_t glyph = FT_Face_GetCharVariantIndex (ft_face, unicode, variation_selector);
+    if (glyph)
+      return glyph;
+  }
+#endif
+
+  return FT_Get_Char_Index (ft_face, unicode);
+}
+
+static void
+hb_ft_get_glyph_advance (hb_font_t *font HB_UNUSED,
+			 hb_face_t *face HB_UNUSED,
+			 const void *user_data,
+			 hb_codepoint_t glyph,
+			 hb_position_t *x_advance,
+			 hb_position_t *y_advance)
+{
+  FT_Face ft_face = (FT_Face) user_data;
+  int load_flags = FT_LOAD_DEFAULT;
+
+  /* TODO: load_flags, embolden, etc */
+
+  if (likely (!FT_Load_Glyph (ft_face, glyph, load_flags)))
+  {
+    *x_advance = ft_face->glyph->advance.x;
+    *y_advance = ft_face->glyph->advance.y;
+  }
+}
+
+static void
+hb_ft_get_glyph_extents (hb_font_t *font HB_UNUSED,
+			 hb_face_t *face HB_UNUSED,
+			 const void *user_data,
+			 hb_codepoint_t glyph,
+			 hb_glyph_extents_t *extents)
+{
+  FT_Face ft_face = (FT_Face) user_data;
+  int load_flags = FT_LOAD_DEFAULT;
+
+  /* TODO: load_flags, embolden, etc */
+
+  if (likely (!FT_Load_Glyph (ft_face, glyph, load_flags)))
+  {
+    /* XXX: A few negations should be in order here, not sure. */
+    extents->x_bearing = ft_face->glyph->metrics.horiBearingX;
+    extents->y_bearing = ft_face->glyph->metrics.horiBearingY;
+    extents->width = ft_face->glyph->metrics.width;
+    extents->height = ft_face->glyph->metrics.height;
+  }
+}
+
+static hb_bool_t
+hb_ft_get_contour_point (hb_font_t *font HB_UNUSED,
+			 hb_face_t *face HB_UNUSED,
+			 const void *user_data,
+			 unsigned int point_index,
+			 hb_codepoint_t glyph,
+			 hb_position_t *x,
+			 hb_position_t *y)
+{
+  FT_Face ft_face = (FT_Face) user_data;
+  int load_flags = FT_LOAD_DEFAULT;
+
+  /* TODO: load_flags, embolden, etc */
+
+  if (unlikely (FT_Load_Glyph (ft_face, glyph, load_flags)))
+      return FALSE;
+
+  if (unlikely (ft_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE))
+      return FALSE;
+
+  if (unlikely (point_index >= (unsigned int) ft_face->glyph->outline.n_points))
+      return FALSE;
+
+  *x = ft_face->glyph->outline.points[point_index].x;
+  *y = ft_face->glyph->outline.points[point_index].y;
+
+  return TRUE;
+}
+
+static hb_position_t
+hb_ft_get_kerning (hb_font_t *font HB_UNUSED,
+		   hb_face_t *face HB_UNUSED,
+		   const void *user_data,
+		   hb_codepoint_t first_glyph,
+		   hb_codepoint_t second_glyph)
+{
+  FT_Face ft_face = (FT_Face) user_data;
+  FT_Vector kerning;
+
+  /* TODO: Kern type? */
+  if (FT_Get_Kerning (ft_face, first_glyph, second_glyph, FT_KERNING_DEFAULT, &kerning))
+      return 0;
+
+  return kerning.x;
+}
+
+static hb_font_funcs_t ft_ffuncs = {
+  HB_REFERENCE_COUNT_INVALID, /* ref_count */
+  TRUE, /* immutable */
+  {
+    hb_ft_get_glyph,
+    hb_ft_get_glyph_advance,
+    hb_ft_get_glyph_extents,
+    hb_ft_get_contour_point,
+    hb_ft_get_kerning
+  }
+};
+
+hb_font_funcs_t *
+hb_ft_get_font_funcs (void)
+{
+  return &ft_ffuncs;
+}
+
+
+static hb_blob_t *
+get_table  (hb_tag_t tag, void *user_data)
+{
+  FT_Face ft_face = (FT_Face) user_data;
+  FT_Byte *buffer;
+  FT_ULong  length = 0;
+  FT_Error error;
+
+  if (unlikely (tag == HB_TAG_NONE))
+    return NULL;
+
+  error = FT_Load_Sfnt_Table (ft_face, tag, 0, NULL, &length);
+  if (error)
+    return NULL;
+
+  /* TODO Use FT_Memory? */
+  buffer = (FT_Byte *) malloc (length);
+  if (buffer == NULL)
+    return NULL;
+
+  error = FT_Load_Sfnt_Table (ft_face, tag, 0, buffer, &length);
+  if (error)
+    return NULL;
+
+  return hb_blob_create ((const char *) buffer, length,
+			 HB_MEMORY_MODE_WRITABLE,
+			 buffer, free);
+}
+
+
+hb_face_t *
+hb_ft_face_create (FT_Face           ft_face,
+		   hb_destroy_func_t destroy)
+{
+  hb_face_t *face;
+
+  if (ft_face->stream->read == NULL) {
+    hb_blob_t *blob;
+
+    blob = hb_blob_create ((const char *) ft_face->stream->base,
+			   (unsigned int) ft_face->stream->size,
+			   /* TODO: Check FT_FACE_FLAG_EXTERNAL_STREAM? */
+			   HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE,
+			   ft_face, destroy);
+    face = hb_face_create_for_data (blob, ft_face->face_index);
+    hb_blob_destroy (blob);
+  } else {
+    face = hb_face_create_for_tables (get_table, ft_face, destroy);
+  }
+
+  return face;
+}
+
+static void
+hb_ft_face_finalize (FT_Face ft_face)
+{
+  hb_face_destroy ((hb_face_t *) ft_face->generic.data);
+}
+
+hb_face_t *
+hb_ft_face_create_cached (FT_Face ft_face)
+{
+  if (unlikely (!ft_face->generic.data || ft_face->generic.finalizer != (FT_Generic_Finalizer) hb_ft_face_finalize))
+  {
+    if (ft_face->generic.finalizer)
+      ft_face->generic.finalizer (ft_face);
+
+    ft_face->generic.data = hb_ft_face_create (ft_face, NULL);
+    ft_face->generic.finalizer = (FT_Generic_Finalizer) hb_ft_face_finalize;
+  }
+
+  return hb_face_reference ((hb_face_t *) ft_face->generic.data);
+}
+
+
+hb_font_t *
+hb_ft_font_create (FT_Face           ft_face,
+		   hb_destroy_func_t destroy)
+{
+  hb_font_t *font;
+
+  font = hb_font_create ();
+  hb_font_set_funcs (font,
+		     hb_ft_get_font_funcs (),
+		     ft_face, destroy);
+  hb_font_set_scale (font,
+		     ((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM) >> 16,
+		     ((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM) >> 16);
+  hb_font_set_ppem (font,
+		    ft_face->size->metrics.x_ppem,
+		    ft_face->size->metrics.y_ppem);
+
+  return font;
+}
+
+
+HB_END_DECLS
diff --git a/src/hb-glib.cc b/src/hb-glib.cc
index 158a210..0f94f52 100644
--- a/src/hb-glib.cc
+++ b/src/hb-glib.cc
@@ -24,7 +24,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.h"
+#include "hb-private.hh"
 
 #include "hb-glib.h"
 
diff --git a/src/hb-icu.cc b/src/hb-icu.cc
index 8fc8c81..2f31a07 100644
--- a/src/hb-icu.cc
+++ b/src/hb-icu.cc
@@ -25,7 +25,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.h"
+#include "hb-private.hh"
 
 #include "hb-icu.h"
 
diff --git a/src/hb-object-private.h b/src/hb-object-private.h
deleted file mode 100644
index c1787c5..0000000
--- a/src/hb-object-private.h
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2007  Chris Wilson
- * Copyright (C) 2009,2010  Red Hat, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Contributor(s):
- *	Chris Wilson <chris at chris-wilson.co.uk>
- * Red Hat Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OBJECT_PRIVATE_H
-#define HB_OBJECT_PRIVATE_H
-
-#include "hb-private.h"
-
-HB_BEGIN_DECLS
-
-
-/* Encapsulate operations on the object's reference count */
-typedef struct {
-  hb_atomic_int_t ref_count;
-} hb_reference_count_t;
-
-#define hb_reference_count_inc(RC) hb_atomic_int_fetch_and_add ((RC).ref_count, 1)
-#define hb_reference_count_dec(RC) hb_atomic_int_fetch_and_add ((RC).ref_count, -1)
-
-#define HB_REFERENCE_COUNT_INIT(RC, VALUE) ((RC).ref_count = (VALUE))
-
-#define HB_REFERENCE_COUNT_GET_VALUE(RC) hb_atomic_int_get ((RC).ref_count)
-#define HB_REFERENCE_COUNT_SET_VALUE(RC, VALUE) hb_atomic_int_set ((RC).ref_count, (VALUE))
-
-#define HB_REFERENCE_COUNT_INVALID_VALUE ((hb_atomic_int_t) -1)
-#define HB_REFERENCE_COUNT_INVALID {HB_REFERENCE_COUNT_INVALID_VALUE}
-
-#define HB_REFERENCE_COUNT_IS_INVALID(RC) (HB_REFERENCE_COUNT_GET_VALUE (RC) == HB_REFERENCE_COUNT_INVALID_VALUE)
-
-#define HB_REFERENCE_COUNT_HAS_REFERENCE(RC) (HB_REFERENCE_COUNT_GET_VALUE (RC) > 0)
-
-
-
-/* Debug */
-
-#ifndef HB_DEBUG_OBJECT
-#define HB_DEBUG_OBJECT (HB_DEBUG+0)
-#endif
-
-static inline void
-_hb_trace_object (const void *obj,
-		  hb_reference_count_t *ref_count,
-		  const char *function)
-{
-  (void) (HB_DEBUG_OBJECT &&
-	  fprintf (stderr, "OBJECT(%p) refcount=%d %s\n",
-		   obj,
-		   HB_REFERENCE_COUNT_GET_VALUE (*ref_count),
-		   function));
-}
-
-#define TRACE_OBJECT(obj) _hb_trace_object (obj, &obj->ref_count, __FUNCTION__)
-
-
-
-/* Object allocation and lifecycle manamgement macros */
-
-#define HB_OBJECT_IS_INERT(obj) \
-    (unlikely (HB_REFERENCE_COUNT_IS_INVALID ((obj)->ref_count)))
-
-#define HB_OBJECT_DO_INIT_EXPR(obj) \
-    HB_REFERENCE_COUNT_INIT (obj->ref_count, 1)
-
-#define HB_OBJECT_DO_INIT(obj) \
-  HB_STMT_START { \
-    HB_OBJECT_DO_INIT_EXPR (obj); \
-  } HB_STMT_END
-
-#define HB_OBJECT_DO_CREATE(Type, obj) \
-  likely (( \
-	       (void) ( \
-		 ((obj) = (Type *) calloc (1, sizeof (Type))) && \
-		 ( \
-		  HB_OBJECT_DO_INIT_EXPR (obj), \
-		  TRACE_OBJECT (obj), \
-		  TRUE \
-		 ) \
-	       ), \
-	       (obj) \
-	     ))
-
-#define HB_OBJECT_DO_REFERENCE(obj) \
-  HB_STMT_START { \
-    int old_count; \
-    if (unlikely (!(obj) || HB_OBJECT_IS_INERT (obj))) \
-      return obj; \
-    TRACE_OBJECT (obj); \
-    old_count = hb_reference_count_inc (obj->ref_count); \
-    assert (old_count > 0); \
-    return obj; \
-  } HB_STMT_END
-
-#define HB_OBJECT_DO_DESTROY(obj) \
-  HB_STMT_START { \
-    int old_count; \
-    if (unlikely (!(obj) || HB_OBJECT_IS_INERT (obj))) \
-      return; \
-    TRACE_OBJECT (obj); \
-    old_count = hb_reference_count_dec (obj->ref_count); \
-    assert (old_count > 0); \
-    if (old_count != 1) \
-      return; \
-  } HB_STMT_END
-
-
-HB_END_DECLS
-
-#endif /* HB_OBJECT_PRIVATE_H */
diff --git a/src/hb-object-private.hh b/src/hb-object-private.hh
new file mode 100644
index 0000000..8f0d5ae
--- /dev/null
+++ b/src/hb-object-private.hh
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2007  Chris Wilson
+ * Copyright (C) 2009,2010  Red Hat, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Contributor(s):
+ *	Chris Wilson <chris at chris-wilson.co.uk>
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OBJECT_PRIVATE_HH
+#define HB_OBJECT_PRIVATE_HH
+
+#include "hb-private.hh"
+
+HB_BEGIN_DECLS
+
+
+/* Encapsulate operations on the object's reference count */
+typedef struct {
+  hb_atomic_int_t ref_count;
+} hb_reference_count_t;
+
+#define hb_reference_count_inc(RC) hb_atomic_int_fetch_and_add ((RC).ref_count, 1)
+#define hb_reference_count_dec(RC) hb_atomic_int_fetch_and_add ((RC).ref_count, -1)
+
+#define HB_REFERENCE_COUNT_INIT(RC, VALUE) ((RC).ref_count = (VALUE))
+
+#define HB_REFERENCE_COUNT_GET_VALUE(RC) hb_atomic_int_get ((RC).ref_count)
+#define HB_REFERENCE_COUNT_SET_VALUE(RC, VALUE) hb_atomic_int_set ((RC).ref_count, (VALUE))
+
+#define HB_REFERENCE_COUNT_INVALID_VALUE ((hb_atomic_int_t) -1)
+#define HB_REFERENCE_COUNT_INVALID {HB_REFERENCE_COUNT_INVALID_VALUE}
+
+#define HB_REFERENCE_COUNT_IS_INVALID(RC) (HB_REFERENCE_COUNT_GET_VALUE (RC) == HB_REFERENCE_COUNT_INVALID_VALUE)
+
+
+
+/* Debug */
+
+#ifndef HB_DEBUG_OBJECT
+#define HB_DEBUG_OBJECT (HB_DEBUG+0)
+#endif
+
+static inline void
+_hb_trace_object (const void *obj,
+		  hb_reference_count_t *ref_count,
+		  const char *function)
+{
+  (void) (HB_DEBUG_OBJECT &&
+	  fprintf (stderr, "OBJECT(%p) refcount=%d %s\n",
+		   obj,
+		   HB_REFERENCE_COUNT_GET_VALUE (*ref_count),
+		   function));
+}
+
+#define TRACE_OBJECT(obj) _hb_trace_object (obj, &obj->ref_count, __FUNCTION__)
+
+
+
+/* Object allocation and lifecycle manamgement macros */
+
+#define HB_OBJECT_IS_INERT(obj) \
+    (unlikely (HB_REFERENCE_COUNT_IS_INVALID ((obj)->ref_count)))
+
+#define HB_OBJECT_DO_INIT_EXPR(obj) \
+    HB_REFERENCE_COUNT_INIT (obj->ref_count, 1)
+
+#define HB_OBJECT_DO_INIT(obj) \
+  HB_STMT_START { \
+    HB_OBJECT_DO_INIT_EXPR (obj); \
+  } HB_STMT_END
+
+#define HB_OBJECT_DO_CREATE(Type, obj) \
+  likely (( \
+	       (void) ( \
+		 ((obj) = (Type *) calloc (1, sizeof (Type))) && \
+		 ( \
+		  HB_OBJECT_DO_INIT_EXPR (obj), \
+		  TRACE_OBJECT (obj), \
+		  TRUE \
+		 ) \
+	       ), \
+	       (obj) \
+	     ))
+
+#define HB_OBJECT_DO_REFERENCE(obj) \
+  HB_STMT_START { \
+    int old_count; \
+    if (unlikely (!(obj) || HB_OBJECT_IS_INERT (obj))) \
+      return obj; \
+    TRACE_OBJECT (obj); \
+    old_count = hb_reference_count_inc (obj->ref_count); \
+    assert (old_count > 0); \
+    return obj; \
+  } HB_STMT_END
+
+#define HB_OBJECT_DO_DESTROY(obj) \
+  HB_STMT_START { \
+    int old_count; \
+    if (unlikely (!(obj) || HB_OBJECT_IS_INERT (obj))) \
+      return; \
+    TRACE_OBJECT (obj); \
+    old_count = hb_reference_count_dec (obj->ref_count); \
+    assert (old_count > 0); \
+    if (old_count != 1) \
+      return; \
+  } HB_STMT_END
+
+
+HB_END_DECLS
+
+#endif /* HB_OBJECT_PRIVATE_HH */
diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index 241c5a0..490ea3a 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -27,7 +27,7 @@
 #ifndef HB_OPEN_TYPE_PRIVATE_HH
 #define HB_OPEN_TYPE_PRIVATE_HH
 
-#include "hb-private.h"
+#include "hb-private.hh"
 
 #include "hb-blob.h"
 
diff --git a/src/hb-ot-layout-gdef-private.hh b/src/hb-ot-layout-gdef-private.hh
index 4172a7c..242b33a 100644
--- a/src/hb-ot-layout-gdef-private.hh
+++ b/src/hb-ot-layout-gdef-private.hh
@@ -31,7 +31,7 @@
 
 #include "hb-ot-layout-common-private.hh"
 
-#include "hb-font-private.h"
+#include "hb-font-private.hh"
 
 HB_BEGIN_DECLS
 
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index b0088fb..a802c63 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -27,12 +27,12 @@
 #ifndef HB_OT_LAYOUT_PRIVATE_HH
 #define HB_OT_LAYOUT_PRIVATE_HH
 
-#include "hb-private.h"
+#include "hb-private.hh"
 
 #include "hb-ot-layout.h"
 #include "hb-ot-head-private.hh"
 
-#include "hb-font-private.h"
+#include "hb-font-private.hh"
 #include "hb-buffer-private.hh"
 
 HB_BEGIN_DECLS
diff --git a/src/hb-ot-shape-complex-arabic-table.h b/src/hb-ot-shape-complex-arabic-table.h
deleted file mode 100644
index 523fc84..0000000
--- a/src/hb-ot-shape-complex-arabic-table.h
+++ /dev/null
@@ -1,674 +0,0 @@
-/*
- * Copyright (C) 2010  Google, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Google Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_H
-#define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_H
-
-#include "hb-private.h"
-
-HB_BEGIN_DECLS
-
-/* == Start of generated table == */
-/*
- * The following table is generated by running:
- *
- *   ./gen-arabic-joining-table.py < ArabicShaping.txt
- *
- * on the ArabicShaping.txt file with the header:
- *
- * # ArabicShaping-6.1.0.txt
- * # Date: 2010-11-09, 12:10:00 PST [KW]
- */
-static const uint8_t joining_table[] =
-{
-
-  /* Arabic characters */
-
-  JOINING_TYPE_U, /* 0600; ARABIC NUMBER SIGN; U; No_Joining_Group */
-  JOINING_TYPE_U, /* 0601; ARABIC SIGN SANAH; U; No_Joining_Group */
-  JOINING_TYPE_U, /* 0602; ARABIC FOOTNOTE MARKER; U; No_Joining_Group */
-  JOINING_TYPE_U, /* 0603; ARABIC SIGN SAFHA; U; No_Joining_Group */
-  JOINING_TYPE_X, /* 0604 */
-  JOINING_TYPE_X, /* 0605 */
-  JOINING_TYPE_X, /* 0606 */
-  JOINING_TYPE_X, /* 0607 */
-  JOINING_TYPE_U, /* 0608; ARABIC RAY; U; No_Joining_Group */
-  JOINING_TYPE_X, /* 0609 */
-  JOINING_TYPE_X, /* 060A */
-  JOINING_TYPE_U, /* 060B; AFGHANI SIGN; U; No_Joining_Group */
-  JOINING_TYPE_X, /* 060C */
-  JOINING_TYPE_X, /* 060D */
-  JOINING_TYPE_X, /* 060E */
-  JOINING_TYPE_X, /* 060F */
-  JOINING_TYPE_X, /* 0610 */
-  JOINING_TYPE_X, /* 0611 */
-  JOINING_TYPE_X, /* 0612 */
-  JOINING_TYPE_X, /* 0613 */
-  JOINING_TYPE_X, /* 0614 */
-  JOINING_TYPE_X, /* 0615 */
-  JOINING_TYPE_X, /* 0616 */
-  JOINING_TYPE_X, /* 0617 */
-  JOINING_TYPE_X, /* 0618 */
-  JOINING_TYPE_X, /* 0619 */
-  JOINING_TYPE_X, /* 061A */
-  JOINING_TYPE_X, /* 061B */
-  JOINING_TYPE_X, /* 061C */
-  JOINING_TYPE_X, /* 061D */
-  JOINING_TYPE_X, /* 061E */
-  JOINING_TYPE_X, /* 061F */
-  JOINING_TYPE_D, /* 0620; YEH WITH RING; D; YEH */
-  JOINING_TYPE_U, /* 0621; HAMZA; U; No_Joining_Group */
-  JOINING_TYPE_R, /* 0622; MADDA ON ALEF; R; ALEF */
-  JOINING_TYPE_R, /* 0623; HAMZA ON ALEF; R; ALEF */
-  JOINING_TYPE_R, /* 0624; HAMZA ON WAW; R; WAW */
-  JOINING_TYPE_R, /* 0625; HAMZA UNDER ALEF; R; ALEF */
-  JOINING_TYPE_D, /* 0626; HAMZA ON YEH; D; YEH */
-  JOINING_TYPE_R, /* 0627; ALEF; R; ALEF */
-  JOINING_TYPE_D, /* 0628; BEH; D; BEH */
-  JOINING_TYPE_R, /* 0629; TEH MARBUTA; R; TEH MARBUTA */
-  JOINING_TYPE_D, /* 062A; TEH; D; BEH */
-  JOINING_TYPE_D, /* 062B; THEH; D; BEH */
-  JOINING_TYPE_D, /* 062C; JEEM; D; HAH */
-  JOINING_TYPE_D, /* 062D; HAH; D; HAH */
-  JOINING_TYPE_D, /* 062E; KHAH; D; HAH */
-  JOINING_TYPE_R, /* 062F; DAL; R; DAL */
-  JOINING_TYPE_R, /* 0630; THAL; R; DAL */
-  JOINING_TYPE_R, /* 0631; REH; R; REH */
-  JOINING_TYPE_R, /* 0632; ZAIN; R; REH */
-  JOINING_TYPE_D, /* 0633; SEEN; D; SEEN */
-  JOINING_TYPE_D, /* 0634; SHEEN; D; SEEN */
-  JOINING_TYPE_D, /* 0635; SAD; D; SAD */
-  JOINING_TYPE_D, /* 0636; DAD; D; SAD */
-  JOINING_TYPE_D, /* 0637; TAH; D; TAH */
-  JOINING_TYPE_D, /* 0638; ZAH; D; TAH */
-  JOINING_TYPE_D, /* 0639; AIN; D; AIN */
-  JOINING_TYPE_D, /* 063A; GHAIN; D; AIN */
-  JOINING_TYPE_D, /* 063B; KEHEH WITH 2 DOTS ABOVE; D; GAF */
-  JOINING_TYPE_D, /* 063C; KEHEH WITH 3 DOTS BELOW; D; GAF */
-  JOINING_TYPE_D, /* 063D; FARSI YEH WITH INVERTED V; D; FARSI YEH */
-  JOINING_TYPE_D, /* 063E; FARSI YEH WITH 2 DOTS ABOVE; D; FARSI YEH */
-  JOINING_TYPE_D, /* 063F; FARSI YEH WITH 3 DOTS ABOVE; D; FARSI YEH */
-  JOINING_TYPE_C, /* 0640; TATWEEL; C; No_Joining_Group */
-  JOINING_TYPE_D, /* 0641; FEH; D; FEH */
-  JOINING_TYPE_D, /* 0642; QAF; D; QAF */
-  JOINING_TYPE_D, /* 0643; KAF; D; KAF */
-  JOINING_TYPE_D, /* 0644; LAM; D; LAM */
-  JOINING_TYPE_D, /* 0645; MEEM; D; MEEM */
-  JOINING_TYPE_D, /* 0646; NOON; D; NOON */
-  JOINING_TYPE_D, /* 0647; HEH; D; HEH */
-  JOINING_TYPE_R, /* 0648; WAW; R; WAW */
-  JOINING_TYPE_D, /* 0649; ALEF MAKSURA; D; YEH */
-  JOINING_TYPE_D, /* 064A; YEH; D; YEH */
-  JOINING_TYPE_X, /* 064B */
-  JOINING_TYPE_X, /* 064C */
-  JOINING_TYPE_X, /* 064D */
-  JOINING_TYPE_X, /* 064E */
-  JOINING_TYPE_X, /* 064F */
-  JOINING_TYPE_X, /* 0650 */
-  JOINING_TYPE_X, /* 0651 */
-  JOINING_TYPE_X, /* 0652 */
-  JOINING_TYPE_X, /* 0653 */
-  JOINING_TYPE_X, /* 0654 */
-  JOINING_TYPE_X, /* 0655 */
-  JOINING_TYPE_X, /* 0656 */
-  JOINING_TYPE_X, /* 0657 */
-  JOINING_TYPE_X, /* 0658 */
-  JOINING_TYPE_X, /* 0659 */
-  JOINING_TYPE_X, /* 065A */
-  JOINING_TYPE_X, /* 065B */
-  JOINING_TYPE_X, /* 065C */
-  JOINING_TYPE_X, /* 065D */
-  JOINING_TYPE_X, /* 065E */
-  JOINING_TYPE_X, /* 065F */
-  JOINING_TYPE_X, /* 0660 */
-  JOINING_TYPE_X, /* 0661 */
-  JOINING_TYPE_X, /* 0662 */
-  JOINING_TYPE_X, /* 0663 */
-  JOINING_TYPE_X, /* 0664 */
-  JOINING_TYPE_X, /* 0665 */
-  JOINING_TYPE_X, /* 0666 */
-  JOINING_TYPE_X, /* 0667 */
-  JOINING_TYPE_X, /* 0668 */
-  JOINING_TYPE_X, /* 0669 */
-  JOINING_TYPE_X, /* 066A */
-  JOINING_TYPE_X, /* 066B */
-  JOINING_TYPE_X, /* 066C */
-  JOINING_TYPE_X, /* 066D */
-  JOINING_TYPE_D, /* 066E; DOTLESS BEH; D; BEH */
-  JOINING_TYPE_D, /* 066F; DOTLESS QAF; D; QAF */
-  JOINING_TYPE_X, /* 0670 */
-  JOINING_TYPE_R, /* 0671; HAMZAT WASL ON ALEF; R; ALEF */
-  JOINING_TYPE_R, /* 0672; WAVY HAMZA ON ALEF; R; ALEF */
-  JOINING_TYPE_R, /* 0673; WAVY HAMZA UNDER ALEF; R; ALEF */
-  JOINING_TYPE_U, /* 0674; HIGH HAMZA; U; No_Joining_Group */
-  JOINING_TYPE_R, /* 0675; HIGH HAMZA ALEF; R; ALEF */
-  JOINING_TYPE_R, /* 0676; HIGH HAMZA WAW; R; WAW */
-  JOINING_TYPE_R, /* 0677; HIGH HAMZA WAW WITH DAMMA; R; WAW */
-  JOINING_TYPE_D, /* 0678; HIGH HAMZA YEH; D; YEH */
-  JOINING_TYPE_D, /* 0679; TEH WITH SMALL TAH; D; BEH */
-  JOINING_TYPE_D, /* 067A; TEH WITH 2 DOTS VERTICAL ABOVE; D; BEH */
-  JOINING_TYPE_D, /* 067B; BEH WITH 2 DOTS VERTICAL BELOW; D; BEH */
-  JOINING_TYPE_D, /* 067C; TEH WITH RING; D; BEH */
-  JOINING_TYPE_D, /* 067D; TEH WITH 3 DOTS ABOVE DOWNWARD; D; BEH */
-  JOINING_TYPE_D, /* 067E; TEH WITH 3 DOTS BELOW; D; BEH */
-  JOINING_TYPE_D, /* 067F; TEH WITH 4 DOTS ABOVE; D; BEH */
-  JOINING_TYPE_D, /* 0680; BEH WITH 4 DOTS BELOW; D; BEH */
-  JOINING_TYPE_D, /* 0681; HAMZA ON HAH; D; HAH */
-  JOINING_TYPE_D, /* 0682; HAH WITH 2 DOTS VERTICAL ABOVE; D; HAH */
-  JOINING_TYPE_D, /* 0683; HAH WITH MIDDLE 2 DOTS; D; HAH */
-  JOINING_TYPE_D, /* 0684; HAH WITH MIDDLE 2 DOTS VERTICAL; D; HAH */
-  JOINING_TYPE_D, /* 0685; HAH WITH 3 DOTS ABOVE; D; HAH */
-  JOINING_TYPE_D, /* 0686; HAH WITH MIDDLE 3 DOTS DOWNWARD; D; HAH */
-  JOINING_TYPE_D, /* 0687; HAH WITH MIDDLE 4 DOTS; D; HAH */
-  JOINING_TYPE_R, /* 0688; DAL WITH SMALL TAH; R; DAL */
-  JOINING_TYPE_R, /* 0689; DAL WITH RING; R; DAL */
-  JOINING_TYPE_R, /* 068A; DAL WITH DOT BELOW; R; DAL */
-  JOINING_TYPE_R, /* 068B; DAL WITH DOT BELOW AND SMALL TAH; R; DAL */
-  JOINING_TYPE_R, /* 068C; DAL WITH 2 DOTS ABOVE; R; DAL */
-  JOINING_TYPE_R, /* 068D; DAL WITH 2 DOTS BELOW; R; DAL */
-  JOINING_TYPE_R, /* 068E; DAL WITH 3 DOTS ABOVE; R; DAL */
-  JOINING_TYPE_R, /* 068F; DAL WITH 3 DOTS ABOVE DOWNWARD; R; DAL */
-  JOINING_TYPE_R, /* 0690; DAL WITH 4 DOTS ABOVE; R; DAL */
-  JOINING_TYPE_R, /* 0691; REH WITH SMALL TAH; R; REH */
-  JOINING_TYPE_R, /* 0692; REH WITH SMALL V; R; REH */
-  JOINING_TYPE_R, /* 0693; REH WITH RING; R; REH */
-  JOINING_TYPE_R, /* 0694; REH WITH DOT BELOW; R; REH */
-  JOINING_TYPE_R, /* 0695; REH WITH SMALL V BELOW; R; REH */
-  JOINING_TYPE_R, /* 0696; REH WITH DOT BELOW AND DOT ABOVE; R; REH */
-  JOINING_TYPE_R, /* 0697; REH WITH 2 DOTS ABOVE; R; REH */
-  JOINING_TYPE_R, /* 0698; REH WITH 3 DOTS ABOVE; R; REH */
-  JOINING_TYPE_R, /* 0699; REH WITH 4 DOTS ABOVE; R; REH */
-  JOINING_TYPE_D, /* 069A; SEEN WITH DOT BELOW AND DOT ABOVE; D; SEEN */
-  JOINING_TYPE_D, /* 069B; SEEN WITH 3 DOTS BELOW; D; SEEN */
-  JOINING_TYPE_D, /* 069C; SEEN WITH 3 DOTS BELOW AND 3 DOTS ABOVE; D; SEEN */
-  JOINING_TYPE_D, /* 069D; SAD WITH 2 DOTS BELOW; D; SAD */
-  JOINING_TYPE_D, /* 069E; SAD WITH 3 DOTS ABOVE; D; SAD */
-  JOINING_TYPE_D, /* 069F; TAH WITH 3 DOTS ABOVE; D; TAH */
-  JOINING_TYPE_D, /* 06A0; AIN WITH 3 DOTS ABOVE; D; AIN */
-  JOINING_TYPE_D, /* 06A1; DOTLESS FEH; D; FEH */
-  JOINING_TYPE_D, /* 06A2; FEH WITH DOT MOVED BELOW; D; FEH */
-  JOINING_TYPE_D, /* 06A3; FEH WITH DOT BELOW; D; FEH */
-  JOINING_TYPE_D, /* 06A4; FEH WITH 3 DOTS ABOVE; D; FEH */
-  JOINING_TYPE_D, /* 06A5; FEH WITH 3 DOTS BELOW; D; FEH */
-  JOINING_TYPE_D, /* 06A6; FEH WITH 4 DOTS ABOVE; D; FEH */
-  JOINING_TYPE_D, /* 06A7; QAF WITH DOT ABOVE; D; QAF */
-  JOINING_TYPE_D, /* 06A8; QAF WITH 3 DOTS ABOVE; D; QAF */
-  JOINING_TYPE_D, /* 06A9; KEHEH; D; GAF */
-  JOINING_TYPE_D, /* 06AA; SWASH KAF; D; SWASH KAF */
-  JOINING_TYPE_D, /* 06AB; KAF WITH RING; D; GAF */
-  JOINING_TYPE_D, /* 06AC; KAF WITH DOT ABOVE; D; KAF */
-  JOINING_TYPE_D, /* 06AD; KAF WITH 3 DOTS ABOVE; D; KAF */
-  JOINING_TYPE_D, /* 06AE; KAF WITH 3 DOTS BELOW; D; KAF */
-  JOINING_TYPE_D, /* 06AF; GAF; D; GAF */
-  JOINING_TYPE_D, /* 06B0; GAF WITH RING; D; GAF */
-  JOINING_TYPE_D, /* 06B1; GAF WITH 2 DOTS ABOVE; D; GAF */
-  JOINING_TYPE_D, /* 06B2; GAF WITH 2 DOTS BELOW; D; GAF */
-  JOINING_TYPE_D, /* 06B3; GAF WITH 2 DOTS VERTICAL BELOW; D; GAF */
-  JOINING_TYPE_D, /* 06B4; GAF WITH 3 DOTS ABOVE; D; GAF */
-  JOINING_TYPE_D, /* 06B5; LAM WITH SMALL V; D; LAM */
-  JOINING_TYPE_D, /* 06B6; LAM WITH DOT ABOVE; D; LAM */
-  JOINING_TYPE_D, /* 06B7; LAM WITH 3 DOTS ABOVE; D; LAM */
-  JOINING_TYPE_D, /* 06B8; LAM WITH 3 DOTS BELOW; D; LAM */
-  JOINING_TYPE_D, /* 06B9; NOON WITH DOT BELOW; D; NOON */
-  JOINING_TYPE_D, /* 06BA; DOTLESS NOON; D; NOON */
-  JOINING_TYPE_D, /* 06BB; DOTLESS NOON WITH SMALL TAH; D; NOON */
-  JOINING_TYPE_D, /* 06BC; NOON WITH RING; D; NOON */
-  JOINING_TYPE_D, /* 06BD; NYA; D; NYA */
-  JOINING_TYPE_D, /* 06BE; KNOTTED HEH; D; KNOTTED HEH */
-  JOINING_TYPE_D, /* 06BF; HAH WITH MIDDLE 3 DOTS DOWNWARD AND DOT ABOVE; D; HAH */
-  JOINING_TYPE_R, /* 06C0; HAMZA ON HEH; R; TEH MARBUTA */
-  JOINING_TYPE_D, /* 06C1; HEH GOAL; D; HEH GOAL */
-  JOINING_TYPE_D, /* 06C2; HAMZA ON HEH GOAL; D; HEH GOAL */
-  JOINING_TYPE_R, /* 06C3; TEH MARBUTA GOAL; R; TEH MARBUTA GOAL */
-  JOINING_TYPE_R, /* 06C4; WAW WITH RING; R; WAW */
-  JOINING_TYPE_R, /* 06C5; WAW WITH BAR; R; WAW */
-  JOINING_TYPE_R, /* 06C6; WAW WITH SMALL V; R; WAW */
-  JOINING_TYPE_R, /* 06C7; WAW WITH DAMMA; R; WAW */
-  JOINING_TYPE_R, /* 06C8; WAW WITH ALEF ABOVE; R; WAW */
-  JOINING_TYPE_R, /* 06C9; WAW WITH INVERTED SMALL V; R; WAW */
-  JOINING_TYPE_R, /* 06CA; WAW WITH 2 DOTS ABOVE; R; WAW */
-  JOINING_TYPE_R, /* 06CB; WAW WITH 3 DOTS ABOVE; R; WAW */
-  JOINING_TYPE_D, /* 06CC; FARSI YEH; D; FARSI YEH */
-  JOINING_TYPE_R, /* 06CD; YEH WITH TAIL; R; YEH WITH TAIL */
-  JOINING_TYPE_D, /* 06CE; FARSI YEH WITH SMALL V; D; FARSI YEH */
-  JOINING_TYPE_R, /* 06CF; WAW WITH DOT ABOVE; R; WAW */
-  JOINING_TYPE_D, /* 06D0; YEH WITH 2 DOTS VERTICAL BELOW; D; YEH */
-  JOINING_TYPE_D, /* 06D1; YEH WITH 3 DOTS BELOW; D; YEH */
-  JOINING_TYPE_R, /* 06D2; YEH BARREE; R; YEH BARREE */
-  JOINING_TYPE_R, /* 06D3; HAMZA ON YEH BARREE; R; YEH BARREE */
-  JOINING_TYPE_X, /* 06D4 */
-  JOINING_TYPE_R, /* 06D5; AE; R; TEH MARBUTA */
-  JOINING_TYPE_X, /* 06D6 */
-  JOINING_TYPE_X, /* 06D7 */
-  JOINING_TYPE_X, /* 06D8 */
-  JOINING_TYPE_X, /* 06D9 */
-  JOINING_TYPE_X, /* 06DA */
-  JOINING_TYPE_X, /* 06DB */
-  JOINING_TYPE_X, /* 06DC */
-  JOINING_TYPE_U, /* 06DD; ARABIC END OF AYAH; U; No_Joining_Group */
-  JOINING_TYPE_X, /* 06DE */
-  JOINING_TYPE_X, /* 06DF */
-  JOINING_TYPE_X, /* 06E0 */
-  JOINING_TYPE_X, /* 06E1 */
-  JOINING_TYPE_X, /* 06E2 */
-  JOINING_TYPE_X, /* 06E3 */
-  JOINING_TYPE_X, /* 06E4 */
-  JOINING_TYPE_X, /* 06E5 */
-  JOINING_TYPE_X, /* 06E6 */
-  JOINING_TYPE_X, /* 06E7 */
-  JOINING_TYPE_X, /* 06E8 */
-  JOINING_TYPE_X, /* 06E9 */
-  JOINING_TYPE_X, /* 06EA */
-  JOINING_TYPE_X, /* 06EB */
-  JOINING_TYPE_X, /* 06EC */
-  JOINING_TYPE_X, /* 06ED */
-  JOINING_TYPE_R, /* 06EE; DAL WITH INVERTED V; R; DAL */
-  JOINING_TYPE_R, /* 06EF; REH WITH INVERTED V; R; REH */
-  JOINING_TYPE_X, /* 06F0 */
-  JOINING_TYPE_X, /* 06F1 */
-  JOINING_TYPE_X, /* 06F2 */
-  JOINING_TYPE_X, /* 06F3 */
-  JOINING_TYPE_X, /* 06F4 */
-  JOINING_TYPE_X, /* 06F5 */
-  JOINING_TYPE_X, /* 06F6 */
-  JOINING_TYPE_X, /* 06F7 */
-  JOINING_TYPE_X, /* 06F8 */
-  JOINING_TYPE_X, /* 06F9 */
-  JOINING_TYPE_D, /* 06FA; SEEN WITH DOT BELOW AND 3 DOTS ABOVE; D; SEEN */
-  JOINING_TYPE_D, /* 06FB; DAD WITH DOT BELOW; D; SAD */
-  JOINING_TYPE_D, /* 06FC; GHAIN WITH DOT BELOW; D; AIN */
-  JOINING_TYPE_X, /* 06FD */
-  JOINING_TYPE_X, /* 06FE */
-  JOINING_TYPE_D, /* 06FF; HEH WITH INVERTED V; D; KNOTTED HEH */
-
-  /* Syriac characters */
-
-  JOINING_TYPE_X, /* 0700 */
-  JOINING_TYPE_X, /* 0701 */
-  JOINING_TYPE_X, /* 0702 */
-  JOINING_TYPE_X, /* 0703 */
-  JOINING_TYPE_X, /* 0704 */
-  JOINING_TYPE_X, /* 0705 */
-  JOINING_TYPE_X, /* 0706 */
-  JOINING_TYPE_X, /* 0707 */
-  JOINING_TYPE_X, /* 0708 */
-  JOINING_TYPE_X, /* 0709 */
-  JOINING_TYPE_X, /* 070A */
-  JOINING_TYPE_X, /* 070B */
-  JOINING_TYPE_X, /* 070C */
-  JOINING_TYPE_X, /* 070D */
-  JOINING_TYPE_X, /* 070E */
-  JOINING_TYPE_X, /* 070F */
-  JOINING_GROUP_ALAPH, /* 0710; ALAPH; R; ALAPH */
-  JOINING_TYPE_X, /* 0711 */
-  JOINING_TYPE_D, /* 0712; BETH; D; BETH */
-  JOINING_TYPE_D, /* 0713; GAMAL; D; GAMAL */
-  JOINING_TYPE_D, /* 0714; GAMAL GARSHUNI; D; GAMAL */
-  JOINING_GROUP_DALATH_RISH, /* 0715; DALATH; R; DALATH RISH */
-  JOINING_GROUP_DALATH_RISH, /* 0716; DOTLESS DALATH RISH; R; DALATH RISH */
-  JOINING_TYPE_R, /* 0717; HE; R; HE */
-  JOINING_TYPE_R, /* 0718; WAW; R; SYRIAC WAW */
-  JOINING_TYPE_R, /* 0719; ZAIN; R; ZAIN */
-  JOINING_TYPE_D, /* 071A; HETH; D; HETH */
-  JOINING_TYPE_D, /* 071B; TETH; D; TETH */
-  JOINING_TYPE_D, /* 071C; TETH GARSHUNI; D; TETH */
-  JOINING_TYPE_D, /* 071D; YUDH; D; YUDH */
-  JOINING_TYPE_R, /* 071E; YUDH HE; R; YUDH HE */
-  JOINING_TYPE_D, /* 071F; KAPH; D; KAPH */
-  JOINING_TYPE_D, /* 0720; LAMADH; D; LAMADH */
-  JOINING_TYPE_D, /* 0721; MIM; D; MIM */
-  JOINING_TYPE_D, /* 0722; NUN; D; NUN */
-  JOINING_TYPE_D, /* 0723; SEMKATH; D; SEMKATH */
-  JOINING_TYPE_D, /* 0724; FINAL SEMKATH; D; FINAL SEMKATH */
-  JOINING_TYPE_D, /* 0725; E; D; E */
-  JOINING_TYPE_D, /* 0726; PE; D; PE */
-  JOINING_TYPE_D, /* 0727; REVERSED PE; D; REVERSED PE */
-  JOINING_TYPE_R, /* 0728; SADHE; R; SADHE */
-  JOINING_TYPE_D, /* 0729; QAPH; D; QAPH */
-  JOINING_GROUP_DALATH_RISH, /* 072A; RISH; R; DALATH RISH */
-  JOINING_TYPE_D, /* 072B; SHIN; D; SHIN */
-  JOINING_TYPE_R, /* 072C; TAW; R; TAW */
-  JOINING_TYPE_D, /* 072D; PERSIAN BHETH; D; BETH */
-  JOINING_TYPE_D, /* 072E; PERSIAN GHAMAL; D; GAMAL */
-  JOINING_GROUP_DALATH_RISH, /* 072F; PERSIAN DHALATH; R; DALATH RISH */
-  JOINING_TYPE_X, /* 0730 */
-  JOINING_TYPE_X, /* 0731 */
-  JOINING_TYPE_X, /* 0732 */
-  JOINING_TYPE_X, /* 0733 */
-  JOINING_TYPE_X, /* 0734 */
-  JOINING_TYPE_X, /* 0735 */
-  JOINING_TYPE_X, /* 0736 */
-  JOINING_TYPE_X, /* 0737 */
-  JOINING_TYPE_X, /* 0738 */
-  JOINING_TYPE_X, /* 0739 */
-  JOINING_TYPE_X, /* 073A */
-  JOINING_TYPE_X, /* 073B */
-  JOINING_TYPE_X, /* 073C */
-  JOINING_TYPE_X, /* 073D */
-  JOINING_TYPE_X, /* 073E */
-  JOINING_TYPE_X, /* 073F */
-  JOINING_TYPE_X, /* 0740 */
-  JOINING_TYPE_X, /* 0741 */
-  JOINING_TYPE_X, /* 0742 */
-  JOINING_TYPE_X, /* 0743 */
-  JOINING_TYPE_X, /* 0744 */
-  JOINING_TYPE_X, /* 0745 */
-  JOINING_TYPE_X, /* 0746 */
-  JOINING_TYPE_X, /* 0747 */
-  JOINING_TYPE_X, /* 0748 */
-  JOINING_TYPE_X, /* 0749 */
-  JOINING_TYPE_X, /* 074A */
-  JOINING_TYPE_X, /* 074B */
-  JOINING_TYPE_X, /* 074C */
-  JOINING_TYPE_R, /* 074D; SOGDIAN ZHAIN; R; ZHAIN */
-  JOINING_TYPE_D, /* 074E; SOGDIAN KHAPH; D; KHAPH */
-  JOINING_TYPE_D, /* 074F; SOGDIAN FE; D; FE */
-
-  /* Arabic supplement characters */
-
-  JOINING_TYPE_D, /* 0750; BEH WITH 3 DOTS HORIZONTALLY BELOW; D; BEH */
-  JOINING_TYPE_D, /* 0751; BEH WITH DOT BELOW AND 3 DOTS ABOVE; D; BEH */
-  JOINING_TYPE_D, /* 0752; BEH WITH 3 DOTS POINTING UPWARDS BELOW; D; BEH */
-  JOINING_TYPE_D, /* 0753; BEH WITH 3 DOTS POINTING UPWARDS BELOW AND 2 DOTS ABOVE; D; BEH */
-  JOINING_TYPE_D, /* 0754; BEH WITH 2 DOTS BELOW AND DOT ABOVE; D; BEH */
-  JOINING_TYPE_D, /* 0755; BEH WITH INVERTED SMALL V BELOW; D; BEH */
-  JOINING_TYPE_D, /* 0756; BEH WITH SMALL V; D; BEH */
-  JOINING_TYPE_D, /* 0757; HAH WITH 2 DOTS ABOVE; D; HAH */
-  JOINING_TYPE_D, /* 0758; HAH WITH 3 DOTS POINTING UPWARDS BELOW; D; HAH */
-  JOINING_TYPE_R, /* 0759; DAL WITH 2 DOTS VERTICALLY BELOW AND SMALL TAH; R; DAL */
-  JOINING_TYPE_R, /* 075A; DAL WITH INVERTED SMALL V BELOW; R; DAL */
-  JOINING_TYPE_R, /* 075B; REH WITH STROKE; R; REH */
-  JOINING_TYPE_D, /* 075C; SEEN WITH 4 DOTS ABOVE; D; SEEN */
-  JOINING_TYPE_D, /* 075D; AIN WITH 2 DOTS ABOVE; D; AIN */
-  JOINING_TYPE_D, /* 075E; AIN WITH 3 DOTS POINTING DOWNWARDS ABOVE; D; AIN */
-  JOINING_TYPE_D, /* 075F; AIN WITH 2 DOTS VERTICALLY ABOVE; D; AIN */
-  JOINING_TYPE_D, /* 0760; FEH WITH 2 DOTS BELOW; D; FEH */
-  JOINING_TYPE_D, /* 0761; FEH WITH 3 DOTS POINTING UPWARDS BELOW; D; FEH */
-  JOINING_TYPE_D, /* 0762; KEHEH WITH DOT ABOVE; D; GAF */
-  JOINING_TYPE_D, /* 0763; KEHEH WITH 3 DOTS ABOVE; D; GAF */
-  JOINING_TYPE_D, /* 0764; KEHEH WITH 3 DOTS POINTING UPWARDS BELOW; D; GAF */
-  JOINING_TYPE_D, /* 0765; MEEM WITH DOT ABOVE; D; MEEM */
-  JOINING_TYPE_D, /* 0766; MEEM WITH DOT BELOW; D; MEEM */
-  JOINING_TYPE_D, /* 0767; NOON WITH 2 DOTS BELOW; D; NOON */
-  JOINING_TYPE_D, /* 0768; NOON WITH SMALL TAH; D; NOON */
-  JOINING_TYPE_D, /* 0769; NOON WITH SMALL V; D; NOON */
-  JOINING_TYPE_D, /* 076A; LAM WITH BAR; D; LAM */
-  JOINING_TYPE_R, /* 076B; REH WITH 2 DOTS VERTICALLY ABOVE; R; REH */
-  JOINING_TYPE_R, /* 076C; REH WITH HAMZA ABOVE; R; REH */
-  JOINING_TYPE_D, /* 076D; SEEN WITH 2 DOTS VERTICALLY ABOVE; D; SEEN */
-  JOINING_TYPE_D, /* 076E; HAH WITH SMALL TAH BELOW; D; HAH */
-  JOINING_TYPE_D, /* 076F; HAH WITH SMALL TAH AND 2 DOTS; D; HAH */
-  JOINING_TYPE_D, /* 0770; SEEN WITH SMALL TAH AND 2 DOTS; D; SEEN */
-  JOINING_TYPE_R, /* 0771; REH WITH SMALL TAH AND 2 DOTS; R; REH */
-  JOINING_TYPE_D, /* 0772; HAH WITH SMALL TAH ABOVE; D; HAH */
-  JOINING_TYPE_R, /* 0773; ALEF WITH DIGIT TWO ABOVE; R; ALEF */
-  JOINING_TYPE_R, /* 0774; ALEF WITH DIGIT THREE ABOVE; R; ALEF */
-  JOINING_TYPE_D, /* 0775; FARSI YEH WITH DIGIT TWO ABOVE; D; FARSI YEH */
-  JOINING_TYPE_D, /* 0776; FARSI YEH WITH DIGIT THREE ABOVE; D; FARSI YEH */
-  JOINING_TYPE_D, /* 0777; YEH WITH DIGIT FOUR BELOW; D; YEH */
-  JOINING_TYPE_R, /* 0778; WAW WITH DIGIT TWO ABOVE; R; WAW */
-  JOINING_TYPE_R, /* 0779; WAW WITH DIGIT THREE ABOVE; R; WAW */
-  JOINING_TYPE_D, /* 077A; YEH BARREE WITH DIGIT TWO ABOVE; D; BURUSHASKI YEH BARREE */
-  JOINING_TYPE_D, /* 077B; YEH BARREE WITH DIGIT THREE ABOVE; D; BURUSHASKI YEH BARREE */
-  JOINING_TYPE_D, /* 077C; HAH WITH DIGIT FOUR BELOW; D; HAH */
-  JOINING_TYPE_D, /* 077D; SEEN WITH DIGIT FOUR ABOVE; D; SEEN */
-  JOINING_TYPE_D, /* 077E; SEEN WITH INVERTED V; D; SEEN */
-  JOINING_TYPE_D, /* 077F; KAF WITH 2 DOTS ABOVE; D; KAF */
-
-  /* N'Ko Characters */
-
-  JOINING_TYPE_X, /* 0780 */
-  JOINING_TYPE_X, /* 0781 */
-  JOINING_TYPE_X, /* 0782 */
-  JOINING_TYPE_X, /* 0783 */
-  JOINING_TYPE_X, /* 0784 */
-  JOINING_TYPE_X, /* 0785 */
-  JOINING_TYPE_X, /* 0786 */
-  JOINING_TYPE_X, /* 0787 */
-  JOINING_TYPE_X, /* 0788 */
-  JOINING_TYPE_X, /* 0789 */
-  JOINING_TYPE_X, /* 078A */
-  JOINING_TYPE_X, /* 078B */
-  JOINING_TYPE_X, /* 078C */
-  JOINING_TYPE_X, /* 078D */
-  JOINING_TYPE_X, /* 078E */
-  JOINING_TYPE_X, /* 078F */
-  JOINING_TYPE_X, /* 0790 */
-  JOINING_TYPE_X, /* 0791 */
-  JOINING_TYPE_X, /* 0792 */
-  JOINING_TYPE_X, /* 0793 */
-  JOINING_TYPE_X, /* 0794 */
-  JOINING_TYPE_X, /* 0795 */
-  JOINING_TYPE_X, /* 0796 */
-  JOINING_TYPE_X, /* 0797 */
-  JOINING_TYPE_X, /* 0798 */
-  JOINING_TYPE_X, /* 0799 */
-  JOINING_TYPE_X, /* 079A */
-  JOINING_TYPE_X, /* 079B */
-  JOINING_TYPE_X, /* 079C */
-  JOINING_TYPE_X, /* 079D */
-  JOINING_TYPE_X, /* 079E */
-  JOINING_TYPE_X, /* 079F */
-  JOINING_TYPE_X, /* 07A0 */
-  JOINING_TYPE_X, /* 07A1 */
-  JOINING_TYPE_X, /* 07A2 */
-  JOINING_TYPE_X, /* 07A3 */
-  JOINING_TYPE_X, /* 07A4 */
-  JOINING_TYPE_X, /* 07A5 */
-  JOINING_TYPE_X, /* 07A6 */
-  JOINING_TYPE_X, /* 07A7 */
-  JOINING_TYPE_X, /* 07A8 */
-  JOINING_TYPE_X, /* 07A9 */
-  JOINING_TYPE_X, /* 07AA */
-  JOINING_TYPE_X, /* 07AB */
-  JOINING_TYPE_X, /* 07AC */
-  JOINING_TYPE_X, /* 07AD */
-  JOINING_TYPE_X, /* 07AE */
-  JOINING_TYPE_X, /* 07AF */
-  JOINING_TYPE_X, /* 07B0 */
-  JOINING_TYPE_X, /* 07B1 */
-  JOINING_TYPE_X, /* 07B2 */
-  JOINING_TYPE_X, /* 07B3 */
-  JOINING_TYPE_X, /* 07B4 */
-  JOINING_TYPE_X, /* 07B5 */
-  JOINING_TYPE_X, /* 07B6 */
-  JOINING_TYPE_X, /* 07B7 */
-  JOINING_TYPE_X, /* 07B8 */
-  JOINING_TYPE_X, /* 07B9 */
-  JOINING_TYPE_X, /* 07BA */
-  JOINING_TYPE_X, /* 07BB */
-  JOINING_TYPE_X, /* 07BC */
-  JOINING_TYPE_X, /* 07BD */
-  JOINING_TYPE_X, /* 07BE */
-  JOINING_TYPE_X, /* 07BF */
-  JOINING_TYPE_X, /* 07C0 */
-  JOINING_TYPE_X, /* 07C1 */
-  JOINING_TYPE_X, /* 07C2 */
-  JOINING_TYPE_X, /* 07C3 */
-  JOINING_TYPE_X, /* 07C4 */
-  JOINING_TYPE_X, /* 07C5 */
-  JOINING_TYPE_X, /* 07C6 */
-  JOINING_TYPE_X, /* 07C7 */
-  JOINING_TYPE_X, /* 07C8 */
-  JOINING_TYPE_X, /* 07C9 */
-  JOINING_TYPE_D, /* 07CA; NKO A; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07CB; NKO EE; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07CC; NKO I; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07CD; NKO E; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07CE; NKO U; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07CF; NKO OO; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07D0; NKO O; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07D1; NKO DAGBASINNA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07D2; NKO N; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07D3; NKO BA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07D4; NKO PA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07D5; NKO TA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07D6; NKO JA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07D7; NKO CHA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07D8; NKO DA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07D9; NKO RA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07DA; NKO RRA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07DB; NKO SA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07DC; NKO GBA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07DD; NKO FA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07DE; NKO KA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07DF; NKO LA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07E0; NKO NA WOLOSO; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07E1; NKO MA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07E2; NKO NYA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07E3; NKO NA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07E4; NKO HA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07E5; NKO WA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07E6; NKO YA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07E7; NKO NYA WOLOSO; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07E8; NKO JONA JA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07E9; NKO JONA CHA; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 07EA; NKO JONA RA; D; No_Joining_Group */
-  JOINING_TYPE_X, /* 07EB */
-  JOINING_TYPE_X, /* 07EC */
-  JOINING_TYPE_X, /* 07ED */
-  JOINING_TYPE_X, /* 07EE */
-  JOINING_TYPE_X, /* 07EF */
-  JOINING_TYPE_X, /* 07F0 */
-  JOINING_TYPE_X, /* 07F1 */
-  JOINING_TYPE_X, /* 07F2 */
-  JOINING_TYPE_X, /* 07F3 */
-  JOINING_TYPE_X, /* 07F4 */
-  JOINING_TYPE_X, /* 07F5 */
-  JOINING_TYPE_X, /* 07F6 */
-  JOINING_TYPE_X, /* 07F7 */
-  JOINING_TYPE_X, /* 07F8 */
-  JOINING_TYPE_X, /* 07F9 */
-  JOINING_TYPE_C, /* 07FA; NKO LAJANYALAN; C; No_Joining_Group */
-
-  /* Mandaic Characters */
-
-  JOINING_TYPE_X, /* 07FB */
-  JOINING_TYPE_X, /* 07FC */
-  JOINING_TYPE_X, /* 07FD */
-  JOINING_TYPE_X, /* 07FE */
-  JOINING_TYPE_X, /* 07FF */
-  JOINING_TYPE_X, /* 0800 */
-  JOINING_TYPE_X, /* 0801 */
-  JOINING_TYPE_X, /* 0802 */
-  JOINING_TYPE_X, /* 0803 */
-  JOINING_TYPE_X, /* 0804 */
-  JOINING_TYPE_X, /* 0805 */
-  JOINING_TYPE_X, /* 0806 */
-  JOINING_TYPE_X, /* 0807 */
-  JOINING_TYPE_X, /* 0808 */
-  JOINING_TYPE_X, /* 0809 */
-  JOINING_TYPE_X, /* 080A */
-  JOINING_TYPE_X, /* 080B */
-  JOINING_TYPE_X, /* 080C */
-  JOINING_TYPE_X, /* 080D */
-  JOINING_TYPE_X, /* 080E */
-  JOINING_TYPE_X, /* 080F */
-  JOINING_TYPE_X, /* 0810 */
-  JOINING_TYPE_X, /* 0811 */
-  JOINING_TYPE_X, /* 0812 */
-  JOINING_TYPE_X, /* 0813 */
-  JOINING_TYPE_X, /* 0814 */
-  JOINING_TYPE_X, /* 0815 */
-  JOINING_TYPE_X, /* 0816 */
-  JOINING_TYPE_X, /* 0817 */
-  JOINING_TYPE_X, /* 0818 */
-  JOINING_TYPE_X, /* 0819 */
-  JOINING_TYPE_X, /* 081A */
-  JOINING_TYPE_X, /* 081B */
-  JOINING_TYPE_X, /* 081C */
-  JOINING_TYPE_X, /* 081D */
-  JOINING_TYPE_X, /* 081E */
-  JOINING_TYPE_X, /* 081F */
-  JOINING_TYPE_X, /* 0820 */
-  JOINING_TYPE_X, /* 0821 */
-  JOINING_TYPE_X, /* 0822 */
-  JOINING_TYPE_X, /* 0823 */
-  JOINING_TYPE_X, /* 0824 */
-  JOINING_TYPE_X, /* 0825 */
-  JOINING_TYPE_X, /* 0826 */
-  JOINING_TYPE_X, /* 0827 */
-  JOINING_TYPE_X, /* 0828 */
-  JOINING_TYPE_X, /* 0829 */
-  JOINING_TYPE_X, /* 082A */
-  JOINING_TYPE_X, /* 082B */
-  JOINING_TYPE_X, /* 082C */
-  JOINING_TYPE_X, /* 082D */
-  JOINING_TYPE_X, /* 082E */
-  JOINING_TYPE_X, /* 082F */
-  JOINING_TYPE_X, /* 0830 */
-  JOINING_TYPE_X, /* 0831 */
-  JOINING_TYPE_X, /* 0832 */
-  JOINING_TYPE_X, /* 0833 */
-  JOINING_TYPE_X, /* 0834 */
-  JOINING_TYPE_X, /* 0835 */
-  JOINING_TYPE_X, /* 0836 */
-  JOINING_TYPE_X, /* 0837 */
-  JOINING_TYPE_X, /* 0838 */
-  JOINING_TYPE_X, /* 0839 */
-  JOINING_TYPE_X, /* 083A */
-  JOINING_TYPE_X, /* 083B */
-  JOINING_TYPE_X, /* 083C */
-  JOINING_TYPE_X, /* 083D */
-  JOINING_TYPE_X, /* 083E */
-  JOINING_TYPE_X, /* 083F */
-  JOINING_TYPE_R, /* 0840; MANDAIC HALQA; R; No_Joining_Group */
-  JOINING_TYPE_D, /* 0841; MANDAIC AB; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 0842; MANDAIC AG; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 0843; MANDAIC AD; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 0844; MANDAIC AH; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 0845; MANDAIC USHENNA; D; No_Joining_Group */
-  JOINING_TYPE_R, /* 0846; MANDAIC AZ; R; No_Joining_Group */
-  JOINING_TYPE_D, /* 0847; MANDAIC IT; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 0848; MANDAIC ATT; D; No_Joining_Group */
-  JOINING_TYPE_R, /* 0849; MANDAIC AKSA; R; No_Joining_Group */
-  JOINING_TYPE_D, /* 084A; MANDAIC AK; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 084B; MANDAIC AL; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 084C; MANDAIC AM; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 084D; MANDAIC AN; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 084E; MANDAIC AS; D; No_Joining_Group */
-  JOINING_TYPE_R, /* 084F; MANDAIC IN; R; No_Joining_Group */
-  JOINING_TYPE_D, /* 0850; MANDAIC AP; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 0851; MANDAIC ASZ; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 0852; MANDAIC AQ; D; No_Joining_Group */
-  JOINING_TYPE_D, /* 0853; MANDAIC AR; D; No_Joining_Group */
-  JOINING_TYPE_R, /* 0854; MANDAIC ASH; R; No_Joining_Group */
-  JOINING_TYPE_D, /* 0855; MANDAIC AT; D; No_Joining_Group */
-  JOINING_TYPE_U, /* 0856; MANDAIC DUSHENNA; U; No_Joining_Group */
-  JOINING_TYPE_U, /* 0857; MANDAIC KAD; U; No_Joining_Group */
-  JOINING_TYPE_U, /* 0858; MANDAIC AIN; U; No_Joining_Group */
-
-  JOINING_TYPE_X  /* dummy */
-};
-
-#define JOINING_TABLE_FIRST	0x0600
-#define JOINING_TABLE_LAST	0x0858
-
-/* == End of generated table == */
-
-HB_END_DECLS
-
-#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_H */
diff --git a/src/hb-ot-shape-complex-arabic-table.hh b/src/hb-ot-shape-complex-arabic-table.hh
new file mode 100644
index 0000000..27c6696
--- /dev/null
+++ b/src/hb-ot-shape-complex-arabic-table.hh
@@ -0,0 +1,674 @@
+/*
+ * Copyright (C) 2010  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH
+#define HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH
+
+#include "hb-private.hh"
+
+HB_BEGIN_DECLS
+
+/* == Start of generated table == */
+/*
+ * The following table is generated by running:
+ *
+ *   ./gen-arabic-joining-table.py < ArabicShaping.txt
+ *
+ * on the ArabicShaping.txt file with the header:
+ *
+ * # ArabicShaping-6.1.0.txt
+ * # Date: 2010-11-09, 12:10:00 PST [KW]
+ */
+static const uint8_t joining_table[] =
+{
+
+  /* Arabic characters */
+
+  JOINING_TYPE_U, /* 0600; ARABIC NUMBER SIGN; U; No_Joining_Group */
+  JOINING_TYPE_U, /* 0601; ARABIC SIGN SANAH; U; No_Joining_Group */
+  JOINING_TYPE_U, /* 0602; ARABIC FOOTNOTE MARKER; U; No_Joining_Group */
+  JOINING_TYPE_U, /* 0603; ARABIC SIGN SAFHA; U; No_Joining_Group */
+  JOINING_TYPE_X, /* 0604 */
+  JOINING_TYPE_X, /* 0605 */
+  JOINING_TYPE_X, /* 0606 */
+  JOINING_TYPE_X, /* 0607 */
+  JOINING_TYPE_U, /* 0608; ARABIC RAY; U; No_Joining_Group */
+  JOINING_TYPE_X, /* 0609 */
+  JOINING_TYPE_X, /* 060A */
+  JOINING_TYPE_U, /* 060B; AFGHANI SIGN; U; No_Joining_Group */
+  JOINING_TYPE_X, /* 060C */
+  JOINING_TYPE_X, /* 060D */
+  JOINING_TYPE_X, /* 060E */
+  JOINING_TYPE_X, /* 060F */
+  JOINING_TYPE_X, /* 0610 */
+  JOINING_TYPE_X, /* 0611 */
+  JOINING_TYPE_X, /* 0612 */
+  JOINING_TYPE_X, /* 0613 */
+  JOINING_TYPE_X, /* 0614 */
+  JOINING_TYPE_X, /* 0615 */
+  JOINING_TYPE_X, /* 0616 */
+  JOINING_TYPE_X, /* 0617 */
+  JOINING_TYPE_X, /* 0618 */
+  JOINING_TYPE_X, /* 0619 */
+  JOINING_TYPE_X, /* 061A */
+  JOINING_TYPE_X, /* 061B */
+  JOINING_TYPE_X, /* 061C */
+  JOINING_TYPE_X, /* 061D */
+  JOINING_TYPE_X, /* 061E */
+  JOINING_TYPE_X, /* 061F */
+  JOINING_TYPE_D, /* 0620; YEH WITH RING; D; YEH */
+  JOINING_TYPE_U, /* 0621; HAMZA; U; No_Joining_Group */
+  JOINING_TYPE_R, /* 0622; MADDA ON ALEF; R; ALEF */
+  JOINING_TYPE_R, /* 0623; HAMZA ON ALEF; R; ALEF */
+  JOINING_TYPE_R, /* 0624; HAMZA ON WAW; R; WAW */
+  JOINING_TYPE_R, /* 0625; HAMZA UNDER ALEF; R; ALEF */
+  JOINING_TYPE_D, /* 0626; HAMZA ON YEH; D; YEH */
+  JOINING_TYPE_R, /* 0627; ALEF; R; ALEF */
+  JOINING_TYPE_D, /* 0628; BEH; D; BEH */
+  JOINING_TYPE_R, /* 0629; TEH MARBUTA; R; TEH MARBUTA */
+  JOINING_TYPE_D, /* 062A; TEH; D; BEH */
+  JOINING_TYPE_D, /* 062B; THEH; D; BEH */
+  JOINING_TYPE_D, /* 062C; JEEM; D; HAH */
+  JOINING_TYPE_D, /* 062D; HAH; D; HAH */
+  JOINING_TYPE_D, /* 062E; KHAH; D; HAH */
+  JOINING_TYPE_R, /* 062F; DAL; R; DAL */
+  JOINING_TYPE_R, /* 0630; THAL; R; DAL */
+  JOINING_TYPE_R, /* 0631; REH; R; REH */
+  JOINING_TYPE_R, /* 0632; ZAIN; R; REH */
+  JOINING_TYPE_D, /* 0633; SEEN; D; SEEN */
+  JOINING_TYPE_D, /* 0634; SHEEN; D; SEEN */
+  JOINING_TYPE_D, /* 0635; SAD; D; SAD */
+  JOINING_TYPE_D, /* 0636; DAD; D; SAD */
+  JOINING_TYPE_D, /* 0637; TAH; D; TAH */
+  JOINING_TYPE_D, /* 0638; ZAH; D; TAH */
+  JOINING_TYPE_D, /* 0639; AIN; D; AIN */
+  JOINING_TYPE_D, /* 063A; GHAIN; D; AIN */
+  JOINING_TYPE_D, /* 063B; KEHEH WITH 2 DOTS ABOVE; D; GAF */
+  JOINING_TYPE_D, /* 063C; KEHEH WITH 3 DOTS BELOW; D; GAF */
+  JOINING_TYPE_D, /* 063D; FARSI YEH WITH INVERTED V; D; FARSI YEH */
+  JOINING_TYPE_D, /* 063E; FARSI YEH WITH 2 DOTS ABOVE; D; FARSI YEH */
+  JOINING_TYPE_D, /* 063F; FARSI YEH WITH 3 DOTS ABOVE; D; FARSI YEH */
+  JOINING_TYPE_C, /* 0640; TATWEEL; C; No_Joining_Group */
+  JOINING_TYPE_D, /* 0641; FEH; D; FEH */
+  JOINING_TYPE_D, /* 0642; QAF; D; QAF */
+  JOINING_TYPE_D, /* 0643; KAF; D; KAF */
+  JOINING_TYPE_D, /* 0644; LAM; D; LAM */
+  JOINING_TYPE_D, /* 0645; MEEM; D; MEEM */
+  JOINING_TYPE_D, /* 0646; NOON; D; NOON */
+  JOINING_TYPE_D, /* 0647; HEH; D; HEH */
+  JOINING_TYPE_R, /* 0648; WAW; R; WAW */
+  JOINING_TYPE_D, /* 0649; ALEF MAKSURA; D; YEH */
+  JOINING_TYPE_D, /* 064A; YEH; D; YEH */
+  JOINING_TYPE_X, /* 064B */
+  JOINING_TYPE_X, /* 064C */
+  JOINING_TYPE_X, /* 064D */
+  JOINING_TYPE_X, /* 064E */
+  JOINING_TYPE_X, /* 064F */
+  JOINING_TYPE_X, /* 0650 */
+  JOINING_TYPE_X, /* 0651 */
+  JOINING_TYPE_X, /* 0652 */
+  JOINING_TYPE_X, /* 0653 */
+  JOINING_TYPE_X, /* 0654 */
+  JOINING_TYPE_X, /* 0655 */
+  JOINING_TYPE_X, /* 0656 */
+  JOINING_TYPE_X, /* 0657 */
+  JOINING_TYPE_X, /* 0658 */
+  JOINING_TYPE_X, /* 0659 */
+  JOINING_TYPE_X, /* 065A */
+  JOINING_TYPE_X, /* 065B */
+  JOINING_TYPE_X, /* 065C */
+  JOINING_TYPE_X, /* 065D */
+  JOINING_TYPE_X, /* 065E */
+  JOINING_TYPE_X, /* 065F */
+  JOINING_TYPE_X, /* 0660 */
+  JOINING_TYPE_X, /* 0661 */
+  JOINING_TYPE_X, /* 0662 */
+  JOINING_TYPE_X, /* 0663 */
+  JOINING_TYPE_X, /* 0664 */
+  JOINING_TYPE_X, /* 0665 */
+  JOINING_TYPE_X, /* 0666 */
+  JOINING_TYPE_X, /* 0667 */
+  JOINING_TYPE_X, /* 0668 */
+  JOINING_TYPE_X, /* 0669 */
+  JOINING_TYPE_X, /* 066A */
+  JOINING_TYPE_X, /* 066B */
+  JOINING_TYPE_X, /* 066C */
+  JOINING_TYPE_X, /* 066D */
+  JOINING_TYPE_D, /* 066E; DOTLESS BEH; D; BEH */
+  JOINING_TYPE_D, /* 066F; DOTLESS QAF; D; QAF */
+  JOINING_TYPE_X, /* 0670 */
+  JOINING_TYPE_R, /* 0671; HAMZAT WASL ON ALEF; R; ALEF */
+  JOINING_TYPE_R, /* 0672; WAVY HAMZA ON ALEF; R; ALEF */
+  JOINING_TYPE_R, /* 0673; WAVY HAMZA UNDER ALEF; R; ALEF */
+  JOINING_TYPE_U, /* 0674; HIGH HAMZA; U; No_Joining_Group */
+  JOINING_TYPE_R, /* 0675; HIGH HAMZA ALEF; R; ALEF */
+  JOINING_TYPE_R, /* 0676; HIGH HAMZA WAW; R; WAW */
+  JOINING_TYPE_R, /* 0677; HIGH HAMZA WAW WITH DAMMA; R; WAW */
+  JOINING_TYPE_D, /* 0678; HIGH HAMZA YEH; D; YEH */
+  JOINING_TYPE_D, /* 0679; TEH WITH SMALL TAH; D; BEH */
+  JOINING_TYPE_D, /* 067A; TEH WITH 2 DOTS VERTICAL ABOVE; D; BEH */
+  JOINING_TYPE_D, /* 067B; BEH WITH 2 DOTS VERTICAL BELOW; D; BEH */
+  JOINING_TYPE_D, /* 067C; TEH WITH RING; D; BEH */
+  JOINING_TYPE_D, /* 067D; TEH WITH 3 DOTS ABOVE DOWNWARD; D; BEH */
+  JOINING_TYPE_D, /* 067E; TEH WITH 3 DOTS BELOW; D; BEH */
+  JOINING_TYPE_D, /* 067F; TEH WITH 4 DOTS ABOVE; D; BEH */
+  JOINING_TYPE_D, /* 0680; BEH WITH 4 DOTS BELOW; D; BEH */
+  JOINING_TYPE_D, /* 0681; HAMZA ON HAH; D; HAH */
+  JOINING_TYPE_D, /* 0682; HAH WITH 2 DOTS VERTICAL ABOVE; D; HAH */
+  JOINING_TYPE_D, /* 0683; HAH WITH MIDDLE 2 DOTS; D; HAH */
+  JOINING_TYPE_D, /* 0684; HAH WITH MIDDLE 2 DOTS VERTICAL; D; HAH */
+  JOINING_TYPE_D, /* 0685; HAH WITH 3 DOTS ABOVE; D; HAH */
+  JOINING_TYPE_D, /* 0686; HAH WITH MIDDLE 3 DOTS DOWNWARD; D; HAH */
+  JOINING_TYPE_D, /* 0687; HAH WITH MIDDLE 4 DOTS; D; HAH */
+  JOINING_TYPE_R, /* 0688; DAL WITH SMALL TAH; R; DAL */
+  JOINING_TYPE_R, /* 0689; DAL WITH RING; R; DAL */
+  JOINING_TYPE_R, /* 068A; DAL WITH DOT BELOW; R; DAL */
+  JOINING_TYPE_R, /* 068B; DAL WITH DOT BELOW AND SMALL TAH; R; DAL */
+  JOINING_TYPE_R, /* 068C; DAL WITH 2 DOTS ABOVE; R; DAL */
+  JOINING_TYPE_R, /* 068D; DAL WITH 2 DOTS BELOW; R; DAL */
+  JOINING_TYPE_R, /* 068E; DAL WITH 3 DOTS ABOVE; R; DAL */
+  JOINING_TYPE_R, /* 068F; DAL WITH 3 DOTS ABOVE DOWNWARD; R; DAL */
+  JOINING_TYPE_R, /* 0690; DAL WITH 4 DOTS ABOVE; R; DAL */
+  JOINING_TYPE_R, /* 0691; REH WITH SMALL TAH; R; REH */
+  JOINING_TYPE_R, /* 0692; REH WITH SMALL V; R; REH */
+  JOINING_TYPE_R, /* 0693; REH WITH RING; R; REH */
+  JOINING_TYPE_R, /* 0694; REH WITH DOT BELOW; R; REH */
+  JOINING_TYPE_R, /* 0695; REH WITH SMALL V BELOW; R; REH */
+  JOINING_TYPE_R, /* 0696; REH WITH DOT BELOW AND DOT ABOVE; R; REH */
+  JOINING_TYPE_R, /* 0697; REH WITH 2 DOTS ABOVE; R; REH */
+  JOINING_TYPE_R, /* 0698; REH WITH 3 DOTS ABOVE; R; REH */
+  JOINING_TYPE_R, /* 0699; REH WITH 4 DOTS ABOVE; R; REH */
+  JOINING_TYPE_D, /* 069A; SEEN WITH DOT BELOW AND DOT ABOVE; D; SEEN */
+  JOINING_TYPE_D, /* 069B; SEEN WITH 3 DOTS BELOW; D; SEEN */
+  JOINING_TYPE_D, /* 069C; SEEN WITH 3 DOTS BELOW AND 3 DOTS ABOVE; D; SEEN */
+  JOINING_TYPE_D, /* 069D; SAD WITH 2 DOTS BELOW; D; SAD */
+  JOINING_TYPE_D, /* 069E; SAD WITH 3 DOTS ABOVE; D; SAD */
+  JOINING_TYPE_D, /* 069F; TAH WITH 3 DOTS ABOVE; D; TAH */
+  JOINING_TYPE_D, /* 06A0; AIN WITH 3 DOTS ABOVE; D; AIN */
+  JOINING_TYPE_D, /* 06A1; DOTLESS FEH; D; FEH */
+  JOINING_TYPE_D, /* 06A2; FEH WITH DOT MOVED BELOW; D; FEH */
+  JOINING_TYPE_D, /* 06A3; FEH WITH DOT BELOW; D; FEH */
+  JOINING_TYPE_D, /* 06A4; FEH WITH 3 DOTS ABOVE; D; FEH */
+  JOINING_TYPE_D, /* 06A5; FEH WITH 3 DOTS BELOW; D; FEH */
+  JOINING_TYPE_D, /* 06A6; FEH WITH 4 DOTS ABOVE; D; FEH */
+  JOINING_TYPE_D, /* 06A7; QAF WITH DOT ABOVE; D; QAF */
+  JOINING_TYPE_D, /* 06A8; QAF WITH 3 DOTS ABOVE; D; QAF */
+  JOINING_TYPE_D, /* 06A9; KEHEH; D; GAF */
+  JOINING_TYPE_D, /* 06AA; SWASH KAF; D; SWASH KAF */
+  JOINING_TYPE_D, /* 06AB; KAF WITH RING; D; GAF */
+  JOINING_TYPE_D, /* 06AC; KAF WITH DOT ABOVE; D; KAF */
+  JOINING_TYPE_D, /* 06AD; KAF WITH 3 DOTS ABOVE; D; KAF */
+  JOINING_TYPE_D, /* 06AE; KAF WITH 3 DOTS BELOW; D; KAF */
+  JOINING_TYPE_D, /* 06AF; GAF; D; GAF */
+  JOINING_TYPE_D, /* 06B0; GAF WITH RING; D; GAF */
+  JOINING_TYPE_D, /* 06B1; GAF WITH 2 DOTS ABOVE; D; GAF */
+  JOINING_TYPE_D, /* 06B2; GAF WITH 2 DOTS BELOW; D; GAF */
+  JOINING_TYPE_D, /* 06B3; GAF WITH 2 DOTS VERTICAL BELOW; D; GAF */
+  JOINING_TYPE_D, /* 06B4; GAF WITH 3 DOTS ABOVE; D; GAF */
+  JOINING_TYPE_D, /* 06B5; LAM WITH SMALL V; D; LAM */
+  JOINING_TYPE_D, /* 06B6; LAM WITH DOT ABOVE; D; LAM */
+  JOINING_TYPE_D, /* 06B7; LAM WITH 3 DOTS ABOVE; D; LAM */
+  JOINING_TYPE_D, /* 06B8; LAM WITH 3 DOTS BELOW; D; LAM */
+  JOINING_TYPE_D, /* 06B9; NOON WITH DOT BELOW; D; NOON */
+  JOINING_TYPE_D, /* 06BA; DOTLESS NOON; D; NOON */
+  JOINING_TYPE_D, /* 06BB; DOTLESS NOON WITH SMALL TAH; D; NOON */
+  JOINING_TYPE_D, /* 06BC; NOON WITH RING; D; NOON */
+  JOINING_TYPE_D, /* 06BD; NYA; D; NYA */
+  JOINING_TYPE_D, /* 06BE; KNOTTED HEH; D; KNOTTED HEH */
+  JOINING_TYPE_D, /* 06BF; HAH WITH MIDDLE 3 DOTS DOWNWARD AND DOT ABOVE; D; HAH */
+  JOINING_TYPE_R, /* 06C0; HAMZA ON HEH; R; TEH MARBUTA */
+  JOINING_TYPE_D, /* 06C1; HEH GOAL; D; HEH GOAL */
+  JOINING_TYPE_D, /* 06C2; HAMZA ON HEH GOAL; D; HEH GOAL */
+  JOINING_TYPE_R, /* 06C3; TEH MARBUTA GOAL; R; TEH MARBUTA GOAL */
+  JOINING_TYPE_R, /* 06C4; WAW WITH RING; R; WAW */
+  JOINING_TYPE_R, /* 06C5; WAW WITH BAR; R; WAW */
+  JOINING_TYPE_R, /* 06C6; WAW WITH SMALL V; R; WAW */
+  JOINING_TYPE_R, /* 06C7; WAW WITH DAMMA; R; WAW */
+  JOINING_TYPE_R, /* 06C8; WAW WITH ALEF ABOVE; R; WAW */
+  JOINING_TYPE_R, /* 06C9; WAW WITH INVERTED SMALL V; R; WAW */
+  JOINING_TYPE_R, /* 06CA; WAW WITH 2 DOTS ABOVE; R; WAW */
+  JOINING_TYPE_R, /* 06CB; WAW WITH 3 DOTS ABOVE; R; WAW */
+  JOINING_TYPE_D, /* 06CC; FARSI YEH; D; FARSI YEH */
+  JOINING_TYPE_R, /* 06CD; YEH WITH TAIL; R; YEH WITH TAIL */
+  JOINING_TYPE_D, /* 06CE; FARSI YEH WITH SMALL V; D; FARSI YEH */
+  JOINING_TYPE_R, /* 06CF; WAW WITH DOT ABOVE; R; WAW */
+  JOINING_TYPE_D, /* 06D0; YEH WITH 2 DOTS VERTICAL BELOW; D; YEH */
+  JOINING_TYPE_D, /* 06D1; YEH WITH 3 DOTS BELOW; D; YEH */
+  JOINING_TYPE_R, /* 06D2; YEH BARREE; R; YEH BARREE */
+  JOINING_TYPE_R, /* 06D3; HAMZA ON YEH BARREE; R; YEH BARREE */
+  JOINING_TYPE_X, /* 06D4 */
+  JOINING_TYPE_R, /* 06D5; AE; R; TEH MARBUTA */
+  JOINING_TYPE_X, /* 06D6 */
+  JOINING_TYPE_X, /* 06D7 */
+  JOINING_TYPE_X, /* 06D8 */
+  JOINING_TYPE_X, /* 06D9 */
+  JOINING_TYPE_X, /* 06DA */
+  JOINING_TYPE_X, /* 06DB */
+  JOINING_TYPE_X, /* 06DC */
+  JOINING_TYPE_U, /* 06DD; ARABIC END OF AYAH; U; No_Joining_Group */
+  JOINING_TYPE_X, /* 06DE */
+  JOINING_TYPE_X, /* 06DF */
+  JOINING_TYPE_X, /* 06E0 */
+  JOINING_TYPE_X, /* 06E1 */
+  JOINING_TYPE_X, /* 06E2 */
+  JOINING_TYPE_X, /* 06E3 */
+  JOINING_TYPE_X, /* 06E4 */
+  JOINING_TYPE_X, /* 06E5 */
+  JOINING_TYPE_X, /* 06E6 */
+  JOINING_TYPE_X, /* 06E7 */
+  JOINING_TYPE_X, /* 06E8 */
+  JOINING_TYPE_X, /* 06E9 */
+  JOINING_TYPE_X, /* 06EA */
+  JOINING_TYPE_X, /* 06EB */
+  JOINING_TYPE_X, /* 06EC */
+  JOINING_TYPE_X, /* 06ED */
+  JOINING_TYPE_R, /* 06EE; DAL WITH INVERTED V; R; DAL */
+  JOINING_TYPE_R, /* 06EF; REH WITH INVERTED V; R; REH */
+  JOINING_TYPE_X, /* 06F0 */
+  JOINING_TYPE_X, /* 06F1 */
+  JOINING_TYPE_X, /* 06F2 */
+  JOINING_TYPE_X, /* 06F3 */
+  JOINING_TYPE_X, /* 06F4 */
+  JOINING_TYPE_X, /* 06F5 */
+  JOINING_TYPE_X, /* 06F6 */
+  JOINING_TYPE_X, /* 06F7 */
+  JOINING_TYPE_X, /* 06F8 */
+  JOINING_TYPE_X, /* 06F9 */
+  JOINING_TYPE_D, /* 06FA; SEEN WITH DOT BELOW AND 3 DOTS ABOVE; D; SEEN */
+  JOINING_TYPE_D, /* 06FB; DAD WITH DOT BELOW; D; SAD */
+  JOINING_TYPE_D, /* 06FC; GHAIN WITH DOT BELOW; D; AIN */
+  JOINING_TYPE_X, /* 06FD */
+  JOINING_TYPE_X, /* 06FE */
+  JOINING_TYPE_D, /* 06FF; HEH WITH INVERTED V; D; KNOTTED HEH */
+
+  /* Syriac characters */
+
+  JOINING_TYPE_X, /* 0700 */
+  JOINING_TYPE_X, /* 0701 */
+  JOINING_TYPE_X, /* 0702 */
+  JOINING_TYPE_X, /* 0703 */
+  JOINING_TYPE_X, /* 0704 */
+  JOINING_TYPE_X, /* 0705 */
+  JOINING_TYPE_X, /* 0706 */
+  JOINING_TYPE_X, /* 0707 */
+  JOINING_TYPE_X, /* 0708 */
+  JOINING_TYPE_X, /* 0709 */
+  JOINING_TYPE_X, /* 070A */
+  JOINING_TYPE_X, /* 070B */
+  JOINING_TYPE_X, /* 070C */
+  JOINING_TYPE_X, /* 070D */
+  JOINING_TYPE_X, /* 070E */
+  JOINING_TYPE_X, /* 070F */
+  JOINING_GROUP_ALAPH, /* 0710; ALAPH; R; ALAPH */
+  JOINING_TYPE_X, /* 0711 */
+  JOINING_TYPE_D, /* 0712; BETH; D; BETH */
+  JOINING_TYPE_D, /* 0713; GAMAL; D; GAMAL */
+  JOINING_TYPE_D, /* 0714; GAMAL GARSHUNI; D; GAMAL */
+  JOINING_GROUP_DALATH_RISH, /* 0715; DALATH; R; DALATH RISH */
+  JOINING_GROUP_DALATH_RISH, /* 0716; DOTLESS DALATH RISH; R; DALATH RISH */
+  JOINING_TYPE_R, /* 0717; HE; R; HE */
+  JOINING_TYPE_R, /* 0718; WAW; R; SYRIAC WAW */
+  JOINING_TYPE_R, /* 0719; ZAIN; R; ZAIN */
+  JOINING_TYPE_D, /* 071A; HETH; D; HETH */
+  JOINING_TYPE_D, /* 071B; TETH; D; TETH */
+  JOINING_TYPE_D, /* 071C; TETH GARSHUNI; D; TETH */
+  JOINING_TYPE_D, /* 071D; YUDH; D; YUDH */
+  JOINING_TYPE_R, /* 071E; YUDH HE; R; YUDH HE */
+  JOINING_TYPE_D, /* 071F; KAPH; D; KAPH */
+  JOINING_TYPE_D, /* 0720; LAMADH; D; LAMADH */
+  JOINING_TYPE_D, /* 0721; MIM; D; MIM */
+  JOINING_TYPE_D, /* 0722; NUN; D; NUN */
+  JOINING_TYPE_D, /* 0723; SEMKATH; D; SEMKATH */
+  JOINING_TYPE_D, /* 0724; FINAL SEMKATH; D; FINAL SEMKATH */
+  JOINING_TYPE_D, /* 0725; E; D; E */
+  JOINING_TYPE_D, /* 0726; PE; D; PE */
+  JOINING_TYPE_D, /* 0727; REVERSED PE; D; REVERSED PE */
+  JOINING_TYPE_R, /* 0728; SADHE; R; SADHE */
+  JOINING_TYPE_D, /* 0729; QAPH; D; QAPH */
+  JOINING_GROUP_DALATH_RISH, /* 072A; RISH; R; DALATH RISH */
+  JOINING_TYPE_D, /* 072B; SHIN; D; SHIN */
+  JOINING_TYPE_R, /* 072C; TAW; R; TAW */
+  JOINING_TYPE_D, /* 072D; PERSIAN BHETH; D; BETH */
+  JOINING_TYPE_D, /* 072E; PERSIAN GHAMAL; D; GAMAL */
+  JOINING_GROUP_DALATH_RISH, /* 072F; PERSIAN DHALATH; R; DALATH RISH */
+  JOINING_TYPE_X, /* 0730 */
+  JOINING_TYPE_X, /* 0731 */
+  JOINING_TYPE_X, /* 0732 */
+  JOINING_TYPE_X, /* 0733 */
+  JOINING_TYPE_X, /* 0734 */
+  JOINING_TYPE_X, /* 0735 */
+  JOINING_TYPE_X, /* 0736 */
+  JOINING_TYPE_X, /* 0737 */
+  JOINING_TYPE_X, /* 0738 */
+  JOINING_TYPE_X, /* 0739 */
+  JOINING_TYPE_X, /* 073A */
+  JOINING_TYPE_X, /* 073B */
+  JOINING_TYPE_X, /* 073C */
+  JOINING_TYPE_X, /* 073D */
+  JOINING_TYPE_X, /* 073E */
+  JOINING_TYPE_X, /* 073F */
+  JOINING_TYPE_X, /* 0740 */
+  JOINING_TYPE_X, /* 0741 */
+  JOINING_TYPE_X, /* 0742 */
+  JOINING_TYPE_X, /* 0743 */
+  JOINING_TYPE_X, /* 0744 */
+  JOINING_TYPE_X, /* 0745 */
+  JOINING_TYPE_X, /* 0746 */
+  JOINING_TYPE_X, /* 0747 */
+  JOINING_TYPE_X, /* 0748 */
+  JOINING_TYPE_X, /* 0749 */
+  JOINING_TYPE_X, /* 074A */
+  JOINING_TYPE_X, /* 074B */
+  JOINING_TYPE_X, /* 074C */
+  JOINING_TYPE_R, /* 074D; SOGDIAN ZHAIN; R; ZHAIN */
+  JOINING_TYPE_D, /* 074E; SOGDIAN KHAPH; D; KHAPH */
+  JOINING_TYPE_D, /* 074F; SOGDIAN FE; D; FE */
+
+  /* Arabic supplement characters */
+
+  JOINING_TYPE_D, /* 0750; BEH WITH 3 DOTS HORIZONTALLY BELOW; D; BEH */
+  JOINING_TYPE_D, /* 0751; BEH WITH DOT BELOW AND 3 DOTS ABOVE; D; BEH */
+  JOINING_TYPE_D, /* 0752; BEH WITH 3 DOTS POINTING UPWARDS BELOW; D; BEH */
+  JOINING_TYPE_D, /* 0753; BEH WITH 3 DOTS POINTING UPWARDS BELOW AND 2 DOTS ABOVE; D; BEH */
+  JOINING_TYPE_D, /* 0754; BEH WITH 2 DOTS BELOW AND DOT ABOVE; D; BEH */
+  JOINING_TYPE_D, /* 0755; BEH WITH INVERTED SMALL V BELOW; D; BEH */
+  JOINING_TYPE_D, /* 0756; BEH WITH SMALL V; D; BEH */
+  JOINING_TYPE_D, /* 0757; HAH WITH 2 DOTS ABOVE; D; HAH */
+  JOINING_TYPE_D, /* 0758; HAH WITH 3 DOTS POINTING UPWARDS BELOW; D; HAH */
+  JOINING_TYPE_R, /* 0759; DAL WITH 2 DOTS VERTICALLY BELOW AND SMALL TAH; R; DAL */
+  JOINING_TYPE_R, /* 075A; DAL WITH INVERTED SMALL V BELOW; R; DAL */
+  JOINING_TYPE_R, /* 075B; REH WITH STROKE; R; REH */
+  JOINING_TYPE_D, /* 075C; SEEN WITH 4 DOTS ABOVE; D; SEEN */
+  JOINING_TYPE_D, /* 075D; AIN WITH 2 DOTS ABOVE; D; AIN */
+  JOINING_TYPE_D, /* 075E; AIN WITH 3 DOTS POINTING DOWNWARDS ABOVE; D; AIN */
+  JOINING_TYPE_D, /* 075F; AIN WITH 2 DOTS VERTICALLY ABOVE; D; AIN */
+  JOINING_TYPE_D, /* 0760; FEH WITH 2 DOTS BELOW; D; FEH */
+  JOINING_TYPE_D, /* 0761; FEH WITH 3 DOTS POINTING UPWARDS BELOW; D; FEH */
+  JOINING_TYPE_D, /* 0762; KEHEH WITH DOT ABOVE; D; GAF */
+  JOINING_TYPE_D, /* 0763; KEHEH WITH 3 DOTS ABOVE; D; GAF */
+  JOINING_TYPE_D, /* 0764; KEHEH WITH 3 DOTS POINTING UPWARDS BELOW; D; GAF */
+  JOINING_TYPE_D, /* 0765; MEEM WITH DOT ABOVE; D; MEEM */
+  JOINING_TYPE_D, /* 0766; MEEM WITH DOT BELOW; D; MEEM */
+  JOINING_TYPE_D, /* 0767; NOON WITH 2 DOTS BELOW; D; NOON */
+  JOINING_TYPE_D, /* 0768; NOON WITH SMALL TAH; D; NOON */
+  JOINING_TYPE_D, /* 0769; NOON WITH SMALL V; D; NOON */
+  JOINING_TYPE_D, /* 076A; LAM WITH BAR; D; LAM */
+  JOINING_TYPE_R, /* 076B; REH WITH 2 DOTS VERTICALLY ABOVE; R; REH */
+  JOINING_TYPE_R, /* 076C; REH WITH HAMZA ABOVE; R; REH */
+  JOINING_TYPE_D, /* 076D; SEEN WITH 2 DOTS VERTICALLY ABOVE; D; SEEN */
+  JOINING_TYPE_D, /* 076E; HAH WITH SMALL TAH BELOW; D; HAH */
+  JOINING_TYPE_D, /* 076F; HAH WITH SMALL TAH AND 2 DOTS; D; HAH */
+  JOINING_TYPE_D, /* 0770; SEEN WITH SMALL TAH AND 2 DOTS; D; SEEN */
+  JOINING_TYPE_R, /* 0771; REH WITH SMALL TAH AND 2 DOTS; R; REH */
+  JOINING_TYPE_D, /* 0772; HAH WITH SMALL TAH ABOVE; D; HAH */
+  JOINING_TYPE_R, /* 0773; ALEF WITH DIGIT TWO ABOVE; R; ALEF */
+  JOINING_TYPE_R, /* 0774; ALEF WITH DIGIT THREE ABOVE; R; ALEF */
+  JOINING_TYPE_D, /* 0775; FARSI YEH WITH DIGIT TWO ABOVE; D; FARSI YEH */
+  JOINING_TYPE_D, /* 0776; FARSI YEH WITH DIGIT THREE ABOVE; D; FARSI YEH */
+  JOINING_TYPE_D, /* 0777; YEH WITH DIGIT FOUR BELOW; D; YEH */
+  JOINING_TYPE_R, /* 0778; WAW WITH DIGIT TWO ABOVE; R; WAW */
+  JOINING_TYPE_R, /* 0779; WAW WITH DIGIT THREE ABOVE; R; WAW */
+  JOINING_TYPE_D, /* 077A; YEH BARREE WITH DIGIT TWO ABOVE; D; BURUSHASKI YEH BARREE */
+  JOINING_TYPE_D, /* 077B; YEH BARREE WITH DIGIT THREE ABOVE; D; BURUSHASKI YEH BARREE */
+  JOINING_TYPE_D, /* 077C; HAH WITH DIGIT FOUR BELOW; D; HAH */
+  JOINING_TYPE_D, /* 077D; SEEN WITH DIGIT FOUR ABOVE; D; SEEN */
+  JOINING_TYPE_D, /* 077E; SEEN WITH INVERTED V; D; SEEN */
+  JOINING_TYPE_D, /* 077F; KAF WITH 2 DOTS ABOVE; D; KAF */
+
+  /* N'Ko Characters */
+
+  JOINING_TYPE_X, /* 0780 */
+  JOINING_TYPE_X, /* 0781 */
+  JOINING_TYPE_X, /* 0782 */
+  JOINING_TYPE_X, /* 0783 */
+  JOINING_TYPE_X, /* 0784 */
+  JOINING_TYPE_X, /* 0785 */
+  JOINING_TYPE_X, /* 0786 */
+  JOINING_TYPE_X, /* 0787 */
+  JOINING_TYPE_X, /* 0788 */
+  JOINING_TYPE_X, /* 0789 */
+  JOINING_TYPE_X, /* 078A */
+  JOINING_TYPE_X, /* 078B */
+  JOINING_TYPE_X, /* 078C */
+  JOINING_TYPE_X, /* 078D */
+  JOINING_TYPE_X, /* 078E */
+  JOINING_TYPE_X, /* 078F */
+  JOINING_TYPE_X, /* 0790 */
+  JOINING_TYPE_X, /* 0791 */
+  JOINING_TYPE_X, /* 0792 */
+  JOINING_TYPE_X, /* 0793 */
+  JOINING_TYPE_X, /* 0794 */
+  JOINING_TYPE_X, /* 0795 */
+  JOINING_TYPE_X, /* 0796 */
+  JOINING_TYPE_X, /* 0797 */
+  JOINING_TYPE_X, /* 0798 */
+  JOINING_TYPE_X, /* 0799 */
+  JOINING_TYPE_X, /* 079A */
+  JOINING_TYPE_X, /* 079B */
+  JOINING_TYPE_X, /* 079C */
+  JOINING_TYPE_X, /* 079D */
+  JOINING_TYPE_X, /* 079E */
+  JOINING_TYPE_X, /* 079F */
+  JOINING_TYPE_X, /* 07A0 */
+  JOINING_TYPE_X, /* 07A1 */
+  JOINING_TYPE_X, /* 07A2 */
+  JOINING_TYPE_X, /* 07A3 */
+  JOINING_TYPE_X, /* 07A4 */
+  JOINING_TYPE_X, /* 07A5 */
+  JOINING_TYPE_X, /* 07A6 */
+  JOINING_TYPE_X, /* 07A7 */
+  JOINING_TYPE_X, /* 07A8 */
+  JOINING_TYPE_X, /* 07A9 */
+  JOINING_TYPE_X, /* 07AA */
+  JOINING_TYPE_X, /* 07AB */
+  JOINING_TYPE_X, /* 07AC */
+  JOINING_TYPE_X, /* 07AD */
+  JOINING_TYPE_X, /* 07AE */
+  JOINING_TYPE_X, /* 07AF */
+  JOINING_TYPE_X, /* 07B0 */
+  JOINING_TYPE_X, /* 07B1 */
+  JOINING_TYPE_X, /* 07B2 */
+  JOINING_TYPE_X, /* 07B3 */
+  JOINING_TYPE_X, /* 07B4 */
+  JOINING_TYPE_X, /* 07B5 */
+  JOINING_TYPE_X, /* 07B6 */
+  JOINING_TYPE_X, /* 07B7 */
+  JOINING_TYPE_X, /* 07B8 */
+  JOINING_TYPE_X, /* 07B9 */
+  JOINING_TYPE_X, /* 07BA */
+  JOINING_TYPE_X, /* 07BB */
+  JOINING_TYPE_X, /* 07BC */
+  JOINING_TYPE_X, /* 07BD */
+  JOINING_TYPE_X, /* 07BE */
+  JOINING_TYPE_X, /* 07BF */
+  JOINING_TYPE_X, /* 07C0 */
+  JOINING_TYPE_X, /* 07C1 */
+  JOINING_TYPE_X, /* 07C2 */
+  JOINING_TYPE_X, /* 07C3 */
+  JOINING_TYPE_X, /* 07C4 */
+  JOINING_TYPE_X, /* 07C5 */
+  JOINING_TYPE_X, /* 07C6 */
+  JOINING_TYPE_X, /* 07C7 */
+  JOINING_TYPE_X, /* 07C8 */
+  JOINING_TYPE_X, /* 07C9 */
+  JOINING_TYPE_D, /* 07CA; NKO A; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07CB; NKO EE; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07CC; NKO I; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07CD; NKO E; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07CE; NKO U; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07CF; NKO OO; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D0; NKO O; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D1; NKO DAGBASINNA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D2; NKO N; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D3; NKO BA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D4; NKO PA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D5; NKO TA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D6; NKO JA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D7; NKO CHA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D8; NKO DA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07D9; NKO RA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07DA; NKO RRA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07DB; NKO SA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07DC; NKO GBA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07DD; NKO FA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07DE; NKO KA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07DF; NKO LA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E0; NKO NA WOLOSO; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E1; NKO MA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E2; NKO NYA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E3; NKO NA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E4; NKO HA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E5; NKO WA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E6; NKO YA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E7; NKO NYA WOLOSO; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E8; NKO JONA JA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07E9; NKO JONA CHA; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 07EA; NKO JONA RA; D; No_Joining_Group */
+  JOINING_TYPE_X, /* 07EB */
+  JOINING_TYPE_X, /* 07EC */
+  JOINING_TYPE_X, /* 07ED */
+  JOINING_TYPE_X, /* 07EE */
+  JOINING_TYPE_X, /* 07EF */
+  JOINING_TYPE_X, /* 07F0 */
+  JOINING_TYPE_X, /* 07F1 */
+  JOINING_TYPE_X, /* 07F2 */
+  JOINING_TYPE_X, /* 07F3 */
+  JOINING_TYPE_X, /* 07F4 */
+  JOINING_TYPE_X, /* 07F5 */
+  JOINING_TYPE_X, /* 07F6 */
+  JOINING_TYPE_X, /* 07F7 */
+  JOINING_TYPE_X, /* 07F8 */
+  JOINING_TYPE_X, /* 07F9 */
+  JOINING_TYPE_C, /* 07FA; NKO LAJANYALAN; C; No_Joining_Group */
+
+  /* Mandaic Characters */
+
+  JOINING_TYPE_X, /* 07FB */
+  JOINING_TYPE_X, /* 07FC */
+  JOINING_TYPE_X, /* 07FD */
+  JOINING_TYPE_X, /* 07FE */
+  JOINING_TYPE_X, /* 07FF */
+  JOINING_TYPE_X, /* 0800 */
+  JOINING_TYPE_X, /* 0801 */
+  JOINING_TYPE_X, /* 0802 */
+  JOINING_TYPE_X, /* 0803 */
+  JOINING_TYPE_X, /* 0804 */
+  JOINING_TYPE_X, /* 0805 */
+  JOINING_TYPE_X, /* 0806 */
+  JOINING_TYPE_X, /* 0807 */
+  JOINING_TYPE_X, /* 0808 */
+  JOINING_TYPE_X, /* 0809 */
+  JOINING_TYPE_X, /* 080A */
+  JOINING_TYPE_X, /* 080B */
+  JOINING_TYPE_X, /* 080C */
+  JOINING_TYPE_X, /* 080D */
+  JOINING_TYPE_X, /* 080E */
+  JOINING_TYPE_X, /* 080F */
+  JOINING_TYPE_X, /* 0810 */
+  JOINING_TYPE_X, /* 0811 */
+  JOINING_TYPE_X, /* 0812 */
+  JOINING_TYPE_X, /* 0813 */
+  JOINING_TYPE_X, /* 0814 */
+  JOINING_TYPE_X, /* 0815 */
+  JOINING_TYPE_X, /* 0816 */
+  JOINING_TYPE_X, /* 0817 */
+  JOINING_TYPE_X, /* 0818 */
+  JOINING_TYPE_X, /* 0819 */
+  JOINING_TYPE_X, /* 081A */
+  JOINING_TYPE_X, /* 081B */
+  JOINING_TYPE_X, /* 081C */
+  JOINING_TYPE_X, /* 081D */
+  JOINING_TYPE_X, /* 081E */
+  JOINING_TYPE_X, /* 081F */
+  JOINING_TYPE_X, /* 0820 */
+  JOINING_TYPE_X, /* 0821 */
+  JOINING_TYPE_X, /* 0822 */
+  JOINING_TYPE_X, /* 0823 */
+  JOINING_TYPE_X, /* 0824 */
+  JOINING_TYPE_X, /* 0825 */
+  JOINING_TYPE_X, /* 0826 */
+  JOINING_TYPE_X, /* 0827 */
+  JOINING_TYPE_X, /* 0828 */
+  JOINING_TYPE_X, /* 0829 */
+  JOINING_TYPE_X, /* 082A */
+  JOINING_TYPE_X, /* 082B */
+  JOINING_TYPE_X, /* 082C */
+  JOINING_TYPE_X, /* 082D */
+  JOINING_TYPE_X, /* 082E */
+  JOINING_TYPE_X, /* 082F */
+  JOINING_TYPE_X, /* 0830 */
+  JOINING_TYPE_X, /* 0831 */
+  JOINING_TYPE_X, /* 0832 */
+  JOINING_TYPE_X, /* 0833 */
+  JOINING_TYPE_X, /* 0834 */
+  JOINING_TYPE_X, /* 0835 */
+  JOINING_TYPE_X, /* 0836 */
+  JOINING_TYPE_X, /* 0837 */
+  JOINING_TYPE_X, /* 0838 */
+  JOINING_TYPE_X, /* 0839 */
+  JOINING_TYPE_X, /* 083A */
+  JOINING_TYPE_X, /* 083B */
+  JOINING_TYPE_X, /* 083C */
+  JOINING_TYPE_X, /* 083D */
+  JOINING_TYPE_X, /* 083E */
+  JOINING_TYPE_X, /* 083F */
+  JOINING_TYPE_R, /* 0840; MANDAIC HALQA; R; No_Joining_Group */
+  JOINING_TYPE_D, /* 0841; MANDAIC AB; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 0842; MANDAIC AG; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 0843; MANDAIC AD; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 0844; MANDAIC AH; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 0845; MANDAIC USHENNA; D; No_Joining_Group */
+  JOINING_TYPE_R, /* 0846; MANDAIC AZ; R; No_Joining_Group */
+  JOINING_TYPE_D, /* 0847; MANDAIC IT; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 0848; MANDAIC ATT; D; No_Joining_Group */
+  JOINING_TYPE_R, /* 0849; MANDAIC AKSA; R; No_Joining_Group */
+  JOINING_TYPE_D, /* 084A; MANDAIC AK; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 084B; MANDAIC AL; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 084C; MANDAIC AM; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 084D; MANDAIC AN; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 084E; MANDAIC AS; D; No_Joining_Group */
+  JOINING_TYPE_R, /* 084F; MANDAIC IN; R; No_Joining_Group */
+  JOINING_TYPE_D, /* 0850; MANDAIC AP; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 0851; MANDAIC ASZ; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 0852; MANDAIC AQ; D; No_Joining_Group */
+  JOINING_TYPE_D, /* 0853; MANDAIC AR; D; No_Joining_Group */
+  JOINING_TYPE_R, /* 0854; MANDAIC ASH; R; No_Joining_Group */
+  JOINING_TYPE_D, /* 0855; MANDAIC AT; D; No_Joining_Group */
+  JOINING_TYPE_U, /* 0856; MANDAIC DUSHENNA; U; No_Joining_Group */
+  JOINING_TYPE_U, /* 0857; MANDAIC KAD; U; No_Joining_Group */
+  JOINING_TYPE_U, /* 0858; MANDAIC AIN; U; No_Joining_Group */
+
+  JOINING_TYPE_X  /* dummy */
+};
+
+#define JOINING_TABLE_FIRST	0x0600
+#define JOINING_TABLE_LAST	0x0858
+
+/* == End of generated table == */
+
+HB_END_DECLS
+
+#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_TABLE_HH */
diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index cc08fbb..3e26568 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -55,7 +55,7 @@ enum {
  * Joining types:
  */
 
-#include "hb-ot-shape-complex-arabic-table.h"
+#include "hb-ot-shape-complex-arabic-table.hh"
 
 static unsigned int get_joining_type (hb_codepoint_t u, hb_unicode_general_category_t gen_cat)
 {
diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh
index 8147945..8225b46 100644
--- a/src/hb-ot-shape-complex-private.hh
+++ b/src/hb-ot-shape-complex-private.hh
@@ -27,7 +27,7 @@
 #ifndef HB_OT_SHAPE_COMPLEX_PRIVATE_HH
 #define HB_OT_SHAPE_COMPLEX_PRIVATE_HH
 
-#include "hb-private.h"
+#include "hb-private.hh"
 
 #include "hb-ot-shape-private.hh"
 
diff --git a/src/hb-ot-shape-private.hh b/src/hb-ot-shape-private.hh
index 1a5c670..f334cda 100644
--- a/src/hb-ot-shape-private.hh
+++ b/src/hb-ot-shape-private.hh
@@ -27,7 +27,7 @@
 #ifndef HB_OT_SHAPE_PRIVATE_HH
 #define HB_OT_SHAPE_PRIVATE_HH
 
-#include "hb-private.h"
+#include "hb-private.hh"
 
 #include "hb-ot-shape.h"
 
diff --git a/src/hb-ot-tag.c b/src/hb-ot-tag.c
deleted file mode 100644
index f7e8c95..0000000
--- a/src/hb-ot-tag.c
+++ /dev/null
@@ -1,677 +0,0 @@
-/*
- * Copyright (C) 2009  Red Hat, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Red Hat Author(s): Behdad Esfahbod
- */
-
-#include "hb-private.h"
-#include "hb-ot.h"
-
-#include <string.h>
-
-HB_BEGIN_DECLS
-
-
-/* hb_script_t */
-
-static hb_tag_t
-hb_ot_old_tag_from_script (hb_script_t script)
-{
-  switch ((hb_tag_t) script) {
-    case HB_SCRIPT_COPTIC:		return HB_TAG('c','o','p','t');
-    case HB_SCRIPT_HIRAGANA:		return HB_TAG('k','a','n','a');
-    case HB_SCRIPT_LAO:			return HB_TAG('l','a','o',' ');
-    case HB_SCRIPT_YI:			return HB_TAG('y','i',' ',' ');
-    /* Unicode-5.0 additions */
-    case HB_SCRIPT_NKO:			return HB_TAG('n','k','o',' ');
-    /* Unicode-5.1 additions */
-    case HB_SCRIPT_VAI:			return HB_TAG('v','a','i',' ');
-    /* Unicode-5.2 additions */
-    case HB_SCRIPT_MEETEI_MAYEK:	return HB_TAG('m','y','e','i');
-    /* Unicode-6.0 additions */
-  }
-
-  /* Else, just change first char to lowercase and return */
-  return ((hb_tag_t) script) | 0x20000000;
-}
-
-static hb_script_t
-hb_ot_old_tag_to_script (hb_tag_t tag)
-{
-  switch (tag) {
-    case HB_TAG('c','o','p','t'):	return HB_SCRIPT_COPTIC;
-    case HB_TAG('k','a','n','a'):	return HB_SCRIPT_HIRAGANA;
-    case HB_TAG('l','a','o',' '):	return HB_SCRIPT_LAO;
-    case HB_TAG('y','i',' ',' '):	return HB_SCRIPT_YI;
-    /* Unicode-5.0 additions */
-    case HB_TAG('n','k','o',' '):	return HB_SCRIPT_NKO;
-    /* Unicode-5.1 additions */
-    case HB_TAG('v','a','i',' '):	return HB_SCRIPT_VAI;
-    /* Unicode-5.2 additions */
-    case HB_TAG('m','y','e','i'):	return HB_SCRIPT_MEETEI_MAYEK;
-    /* Unicode-6.0 additions */
-  }
-
-  /* Else, just change first char to uppercase and return */
-  return (hb_script_t) (tag & ~0x20000000);
-}
-
-static hb_tag_t
-hb_ot_new_tag_from_script (hb_script_t script)
-{
-  switch ((hb_tag_t) script) {
-    case HB_SCRIPT_BENGALI:		return HB_TAG('b','n','g','2');
-    case HB_SCRIPT_DEVANAGARI:		return HB_TAG('d','e','v','2');
-    case HB_SCRIPT_GUJARATI:		return HB_TAG('g','j','r','2');
-    case HB_SCRIPT_GURMUKHI:		return HB_TAG('g','u','r','2');
-    case HB_SCRIPT_KANNADA:		return HB_TAG('k','n','d','2');
-    case HB_SCRIPT_MALAYALAM:		return HB_TAG('m','l','m','2');
-    case HB_SCRIPT_ORIYA:		return HB_TAG('o','r','y','2');
-    case HB_SCRIPT_TAMIL:		return HB_TAG('t','m','l','2');
-    case HB_SCRIPT_TELUGU:		return HB_TAG('t','e','l','2');
-  }
-
-  return HB_TAG_NONE;
-}
-
-static hb_script_t
-hb_ot_new_tag_to_script (hb_tag_t tag)
-{
-  switch (tag) {
-    case HB_TAG('b','n','g','2'):	return HB_SCRIPT_BENGALI;
-    case HB_TAG('d','e','v','2'):	return HB_SCRIPT_DEVANAGARI;
-    case HB_TAG('g','j','r','2'):	return HB_SCRIPT_GUJARATI;
-    case HB_TAG('g','u','r','2'):	return HB_SCRIPT_GURMUKHI;
-    case HB_TAG('k','n','d','2'):	return HB_SCRIPT_KANNADA;
-    case HB_TAG('m','l','m','2'):	return HB_SCRIPT_MALAYALAM;
-    case HB_TAG('o','r','y','2'):	return HB_SCRIPT_ORIYA;
-    case HB_TAG('t','m','l','2'):	return HB_SCRIPT_TAMIL;
-    case HB_TAG('t','e','l','2'):	return HB_SCRIPT_TELUGU;
-  }
-
-  return HB_SCRIPT_UNKNOWN;
-}
-
-/*
- * Complete list at:
- * http://www.microsoft.com/typography/otspec/scripttags.htm
- *
- * Most of the script tags are the same as the ISO 15924 tag but lowercased.
- * So we just do that, and handle the exceptional cases in a switch.
- */
-
-void
-hb_ot_tags_from_script (hb_script_t  script,
-			hb_tag_t    *script_tag_1,
-			hb_tag_t    *script_tag_2)
-{
-  hb_tag_t new_tag;
-
-  *script_tag_2 = HB_TAG_NONE;
-  *script_tag_1 = hb_ot_old_tag_from_script (script);
-
-  new_tag = hb_ot_new_tag_from_script (script);
-  if (unlikely (new_tag != HB_TAG_NONE)) {
-    *script_tag_2 = *script_tag_1;
-    *script_tag_1 = new_tag;
-  }
-}
-
-hb_script_t
-hb_ot_tag_to_script (hb_tag_t tag)
-{
-  if (unlikely ((tag & 0x000000FF) == '2'))
-    return hb_ot_new_tag_to_script (tag);
-
-  return hb_ot_old_tag_to_script (tag);
-}
-
-
-/* hb_language_t */
-
-typedef struct {
-  char language[6];
-  hb_tag_t tag;
-} LangTag;
-
-/*
- * Complete list at:
- * http://www.microsoft.com/typography/otspec/languagetags.htm
- *
- * Generated by intersecting the OpenType language tag list from
- * Draft OpenType 1.5 spec, with with the ISO 639-3 codes from
- * 2008/08/04, matching on name, and finally adjusted manually.
- *
- * Many items still missing.  Those are commented out at the end.
- * Keep sorted for bsearch.
- */
-static const LangTag ot_languages[] = {
-  {"aa",	HB_TAG('A','F','R',' ')},	/* Afar */
-  {"ab",	HB_TAG('A','B','K',' ')},	/* Abkhazian */
-  {"abq",	HB_TAG('A','B','A',' ')},	/* Abaza */
-  {"ady",	HB_TAG('A','D','Y',' ')},	/* Adyghe */
-  {"af",	HB_TAG('A','F','K',' ')},	/* Afrikaans */
-  {"aiw",	HB_TAG('A','R','I',' ')},	/* Aari */
-  {"am",	HB_TAG('A','M','H',' ')},	/* Amharic */
-  {"ar",	HB_TAG('A','R','A',' ')},	/* Arabic */
-  {"arn",	HB_TAG('M','A','P',' ')},	/* Mapudungun */
-  {"as",	HB_TAG('A','S','M',' ')},	/* Assamese */
-  {"av",	HB_TAG('A','V','R',' ')},	/* Avaric */
-  {"awa",	HB_TAG('A','W','A',' ')},	/* Awadhi */
-  {"ay",	HB_TAG('A','Y','M',' ')},	/* Aymara */
-  {"az",	HB_TAG('A','Z','E',' ')},	/* Azerbaijani */
-  {"ba",	HB_TAG('B','S','H',' ')},	/* Bashkir */
-  {"bal",	HB_TAG('B','L','I',' ')},	/* Baluchi */
-  {"bcq",	HB_TAG('B','C','H',' ')},	/* Bench */
-  {"bem",	HB_TAG('B','E','M',' ')},	/* Bemba (Zambia) */
-  {"bfq",	HB_TAG('B','A','D',' ')},	/* Badaga */
-  {"bft",	HB_TAG('B','L','T',' ')},	/* Balti */
-  {"bg",	HB_TAG('B','G','R',' ')},	/* Bulgarian */
-  {"bhb",	HB_TAG('B','H','I',' ')},	/* Bhili */
-  {"bho",	HB_TAG('B','H','O',' ')},	/* Bhojpuri */
-  {"bik",	HB_TAG('B','I','K',' ')},	/* Bikol */
-  {"bin",	HB_TAG('E','D','O',' ')},	/* Bini */
-  {"bm",	HB_TAG('B','M','B',' ')},	/* Bambara */
-  {"bn",	HB_TAG('B','E','N',' ')},	/* Bengali */
-  {"bo",	HB_TAG('T','I','B',' ')},	/* Tibetan */
-  {"br",	HB_TAG('B','R','E',' ')},	/* Breton */
-  {"brh",	HB_TAG('B','R','H',' ')},	/* Brahui */
-  {"bs",	HB_TAG('B','O','S',' ')},	/* Bosnian */
-  {"btb",	HB_TAG('B','T','I',' ')},	/* Beti (Cameroon) */
-  {"ca",	HB_TAG('C','A','T',' ')},	/* Catalan */
-  {"ce",	HB_TAG('C','H','E',' ')},	/* Chechen */
-  {"ceb",	HB_TAG('C','E','B',' ')},	/* Cebuano */
-  {"chp",	HB_TAG('C','H','P',' ')},	/* Chipewyan */
-  {"chr",	HB_TAG('C','H','R',' ')},	/* Cherokee */
-  {"cop",	HB_TAG('C','O','P',' ')},	/* Coptic */
-  {"cr",	HB_TAG('C','R','E',' ')},	/* Cree */
-  {"crh",	HB_TAG('C','R','T',' ')},	/* Crimean Tatar */
-  {"crm",	HB_TAG('M','C','R',' ')},	/* Moose Cree */
-  {"crx",	HB_TAG('C','R','R',' ')},	/* Carrier */
-  {"cs",	HB_TAG('C','S','Y',' ')},	/* Czech */
-  {"cu",	HB_TAG('C','S','L',' ')},	/* Church Slavic */
-  {"cv",	HB_TAG('C','H','U',' ')},	/* Chuvash */
-  {"cwd",	HB_TAG('D','C','R',' ')},	/* Woods Cree */
-  {"cy",	HB_TAG('W','E','L',' ')},	/* Welsh */
-  {"da",	HB_TAG('D','A','N',' ')},	/* Danish */
-  {"dap",	HB_TAG('N','I','S',' ')},	/* Nisi (India) */
-  {"dar",	HB_TAG('D','A','R',' ')},	/* Dargwa */
-  {"de",	HB_TAG('D','E','U',' ')},	/* German */
-  {"din",	HB_TAG('D','N','K',' ')},	/* Dinka */
-  {"dng",	HB_TAG('D','U','N',' ')},	/* Dungan */
-  {"doi",	HB_TAG('D','G','R',' ')},	/* Dogri */
-  {"dsb",	HB_TAG('L','S','B',' ')},	/* Lower Sorbian */
-  {"dv",	HB_TAG('D','I','V',' ')},	/* Dhivehi */
-  {"dz",	HB_TAG('D','Z','N',' ')},	/* Dzongkha */
-  {"ee",	HB_TAG('E','W','E',' ')},	/* Ewe */
-  {"efi",	HB_TAG('E','F','I',' ')},	/* Efik */
-  {"el",	HB_TAG('E','L','L',' ')},	/* Modern Greek (1453-) */
-  {"en",	HB_TAG('E','N','G',' ')},	/* English */
-  {"eo",	HB_TAG('N','T','O',' ')},	/* Esperanto */
-  {"eot",	HB_TAG('B','T','I',' ')},	/* Beti (Côte d'Ivoire) */
-  {"es",	HB_TAG('E','S','P',' ')},	/* Spanish */
-  {"et",	HB_TAG('E','T','I',' ')},	/* Estonian */
-  {"eu",	HB_TAG('E','U','Q',' ')},	/* Basque */
-  {"eve",	HB_TAG('E','V','N',' ')},	/* Even */
-  {"evn",	HB_TAG('E','V','K',' ')},	/* Evenki */
-  {"fa",	HB_TAG('F','A','R',' ')},	/* Persian */
-  {"ff",	HB_TAG('F','U','L',' ')},	/* Fulah */
-  {"fi",	HB_TAG('F','I','N',' ')},	/* Finnish */
-  {"fil",	HB_TAG('P','I','L',' ')},	/* Filipino */
-  {"fj",	HB_TAG('F','J','I',' ')},	/* Fijian */
-  {"fo",	HB_TAG('F','O','S',' ')},	/* Faroese */
-  {"fon",	HB_TAG('F','O','N',' ')},	/* Fon */
-  {"fr",	HB_TAG('F','R','A',' ')},	/* French */
-  {"fur",	HB_TAG('F','R','L',' ')},	/* Friulian */
-  {"fy",	HB_TAG('F','R','I',' ')},	/* Western Frisian */
-  {"ga",	HB_TAG('I','R','I',' ')},	/* Irish */
-  {"gaa",	HB_TAG('G','A','D',' ')},	/* Ga */
-  {"gag",	HB_TAG('G','A','G',' ')},	/* Gagauz */
-  {"gbm",	HB_TAG('G','A','W',' ')},	/* Garhwali */
-  {"gd",	HB_TAG('G','A','E',' ')},	/* Scottish Gaelic */
-  {"gl",	HB_TAG('G','A','L',' ')},	/* Galician */
-  {"gld",	HB_TAG('N','A','N',' ')},	/* Nanai */
-  {"gn",	HB_TAG('G','U','A',' ')},	/* Guarani */
-  {"gon",	HB_TAG('G','O','N',' ')},	/* Gondi */
-  {"grt",	HB_TAG('G','R','O',' ')},	/* Garo */
-  {"gu",	HB_TAG('G','U','J',' ')},	/* Gujarati */
-  {"guk",	HB_TAG('G','M','Z',' ')},	/* Gumuz */
-  {"gv",	HB_TAG('M','N','X',' ')},	/* Manx Gaelic */
-  {"ha",	HB_TAG('H','A','U',' ')},	/* Hausa */
-  {"har",	HB_TAG('H','R','I',' ')},	/* Harari */
-  {"he",	HB_TAG('I','W','R',' ')},	/* Hebrew */
-  {"hi",	HB_TAG('H','I','N',' ')},	/* Hindi */
-  {"hil",	HB_TAG('H','I','L',' ')},	/* Hiligaynon */
-  {"hoc",	HB_TAG('H','O',' ',' ')},	/* Ho */
-  {"hr",	HB_TAG('H','R','V',' ')},	/* Croatian */
-  {"hsb",	HB_TAG('U','S','B',' ')},	/* Upper Sorbian */
-  {"ht",	HB_TAG('H','A','I',' ')},	/* Haitian */
-  {"hu",	HB_TAG('H','U','N',' ')},	/* Hungarian */
-  {"hy",	HB_TAG('H','Y','E',' ')},	/* Armenian */
-  {"id",	HB_TAG('I','N','D',' ')},	/* Indonesian */
-  {"ig",	HB_TAG('I','B','O',' ')},	/* Igbo */
-  {"igb",	HB_TAG('E','B','I',' ')},	/* Ebira */
-  {"inh",	HB_TAG('I','N','G',' ')},	/* Ingush */
-  {"is",	HB_TAG('I','S','L',' ')},	/* Icelandic */
-  {"it",	HB_TAG('I','T','A',' ')},	/* Italian */
-  {"iu",	HB_TAG('I','N','U',' ')},	/* Inuktitut */
-  {"ja",	HB_TAG('J','A','N',' ')},	/* Japanese */
-  {"jv",	HB_TAG('J','A','V',' ')},	/* Javanese */
-  {"ka",	HB_TAG('K','A','T',' ')},	/* Georgian */
-  {"kam",	HB_TAG('K','M','B',' ')},	/* Kamba (Kenya) */
-  {"kbd",	HB_TAG('K','A','B',' ')},	/* Kabardian */
-  {"kdr",	HB_TAG('K','R','M',' ')},	/* Karaim */
-  {"kdt",	HB_TAG('K','U','Y',' ')},	/* Kuy */
-  {"kfr",	HB_TAG('K','A','C',' ')},	/* Kachchi */
-  {"kfy",	HB_TAG('K','M','N',' ')},	/* Kumaoni */
-  {"kha",	HB_TAG('K','S','I',' ')},	/* Khasi */
-  {"khw",	HB_TAG('K','H','W',' ')},	/* Khowar */
-  {"ki",	HB_TAG('K','I','K',' ')},	/* Kikuyu */
-  {"kk",	HB_TAG('K','A','Z',' ')},	/* Kazakh */
-  {"kl",	HB_TAG('G','R','N',' ')},	/* Kalaallisut */
-  {"kln",	HB_TAG('K','A','L',' ')},	/* Kalenjin */
-  {"km",	HB_TAG('K','H','M',' ')},	/* Central Khmer */
-  {"kmw",	HB_TAG('K','M','O',' ')},	/* Komo (Democratic Republic of Congo) */
-  {"kn",	HB_TAG('K','A','N',' ')},	/* Kannada */
-  {"ko",	HB_TAG('K','O','R',' ')},	/* Korean */
-  {"koi",	HB_TAG('K','O','P',' ')},	/* Komi-Permyak */
-  {"kok",	HB_TAG('K','O','K',' ')},	/* Konkani */
-  {"kpe",	HB_TAG('K','P','L',' ')},	/* Kpelle */
-  {"kpv",	HB_TAG('K','O','Z',' ')},	/* Komi-Zyrian */
-  {"kpy",	HB_TAG('K','Y','K',' ')},	/* Koryak */
-  {"kqy",	HB_TAG('K','R','T',' ')},	/* Koorete */
-  {"kr",	HB_TAG('K','N','R',' ')},	/* Kanuri */
-  {"kri",	HB_TAG('K','R','I',' ')},	/* Krio */
-  {"krl",	HB_TAG('K','R','L',' ')},	/* Karelian */
-  {"kru",	HB_TAG('K','U','U',' ')},	/* Kurukh */
-  {"ks",	HB_TAG('K','S','H',' ')},	/* Kashmiri */
-  {"ku",	HB_TAG('K','U','R',' ')},	/* Kurdish */
-  {"kum",	HB_TAG('K','U','M',' ')},	/* Kumyk */
-  {"kvd",	HB_TAG('K','U','I',' ')},	/* Kui (Indonesia) */
-  {"kxu",	HB_TAG('K','U','I',' ')},	/* Kui (India) */
-  {"ky",	HB_TAG('K','I','R',' ')},	/* Kirghiz */
-  {"la",	HB_TAG('L','A','T',' ')},	/* Latin */
-  {"lad",	HB_TAG('J','U','D',' ')},	/* Ladino */
-  {"lb",	HB_TAG('L','T','Z',' ')},	/* Luxembourgish */
-  {"lbe",	HB_TAG('L','A','K',' ')},	/* Lak */
-  {"lbj",	HB_TAG('L','D','K',' ')},	/* Ladakhi */
-  {"lif",	HB_TAG('L','M','B',' ')},	/* Limbu */
-  {"lld",	HB_TAG('L','A','D',' ')},	/* Ladin */
-  {"ln",	HB_TAG('L','I','N',' ')},	/* Lingala */
-  {"lo",	HB_TAG('L','A','O',' ')},	/* Lao */
-  {"lt",	HB_TAG('L','T','H',' ')},	/* Lithuanian */
-  {"luo",	HB_TAG('L','U','O',' ')},	/* Luo (Kenya and Tanzania) */
-  {"luw",	HB_TAG('L','U','O',' ')},	/* Luo (Cameroon) */
-  {"lv",	HB_TAG('L','V','I',' ')},	/* Latvian */
-  {"lzz",	HB_TAG('L','A','Z',' ')},	/* Laz */
-  {"mai",	HB_TAG('M','T','H',' ')},	/* Maithili */
-  {"mdc",	HB_TAG('M','L','E',' ')},	/* Male (Papua New Guinea) */
-  {"mdf",	HB_TAG('M','O','K',' ')},	/* Moksha */
-  {"mdy",	HB_TAG('M','L','E',' ')},	/* Male (Ethiopia) */
-  {"men",	HB_TAG('M','D','E',' ')},	/* Mende (Sierra Leone) */
-  {"mg",	HB_TAG('M','L','G',' ')},	/* Malagasy */
-  {"mi",	HB_TAG('M','R','I',' ')},	/* Maori */
-  {"mk",	HB_TAG('M','K','D',' ')},	/* Macedonian */
-  {"ml",	HB_TAG('M','L','R',' ')},	/* Malayalam */
-  {"mn",	HB_TAG('M','N','G',' ')},	/* Mongolian */
-  {"mnc",	HB_TAG('M','C','H',' ')},	/* Manchu */
-  {"mni",	HB_TAG('M','N','I',' ')},	/* Manipuri */
-  {"mnk",	HB_TAG('M','N','D',' ')},	/* Mandinka */
-  {"mns",	HB_TAG('M','A','N',' ')},	/* Mansi */
-  {"mnw",	HB_TAG('M','O','N',' ')},	/* Mon */
-  {"mo",	HB_TAG('M','O','L',' ')},	/* Moldavian */
-  {"moh",	HB_TAG('M','O','H',' ')},	/* Mohawk */
-  {"mpe",	HB_TAG('M','A','J',' ')},	/* Majang */
-  {"mr",	HB_TAG('M','A','R',' ')},	/* Marathi */
-  {"ms",	HB_TAG('M','L','Y',' ')},	/* Malay */
-  {"mt",	HB_TAG('M','T','S',' ')},	/* Maltese */
-  {"mwr",	HB_TAG('M','A','W',' ')},	/* Marwari */
-  {"my",	HB_TAG('B','R','M',' ')},	/* Burmese */
-  {"mym",	HB_TAG('M','E','N',' ')},	/* Me'en */
-  {"myv",	HB_TAG('E','R','Z',' ')},	/* Erzya */
-  {"nb",	HB_TAG('N','O','R',' ')},	/* Norwegian Bokmål */
-  {"nco",	HB_TAG('S','I','B',' ')},	/* Sibe */
-  {"ne",	HB_TAG('N','E','P',' ')},	/* Nepali */
-  {"new",	HB_TAG('N','E','W',' ')},	/* Newari */
-  {"ng",	HB_TAG('N','D','G',' ')},	/* Ndonga */
-  {"ngl",	HB_TAG('L','M','W',' ')},	/* Lomwe */
-  {"niu",	HB_TAG('N','I','U',' ')},	/* Niuean */
-  {"niv",	HB_TAG('G','I','L',' ')},	/* Gilyak */
-  {"nl",	HB_TAG('N','L','D',' ')},	/* Dutch */
-  {"nn",	HB_TAG('N','Y','N',' ')},	/* Norwegian Nynorsk */
-  {"no",	HB_TAG('N','O','R',' ')},	/* Norwegian (deprecated) */
-  {"nog",	HB_TAG('N','O','G',' ')},	/* Nogai */
-  {"nqo",	HB_TAG('N','K','O',' ')},	/* N'Ko */
-  {"nsk",	HB_TAG('N','A','S',' ')},	/* Naskapi */
-  {"ny",	HB_TAG('C','H','I',' ')},	/* Nyanja */
-  {"oc",	HB_TAG('O','C','I',' ')},	/* Occitan (post 1500) */
-  {"oj",	HB_TAG('O','J','B',' ')},	/* Ojibwa */
-  {"om",	HB_TAG('O','R','O',' ')},	/* Oromo */
-  {"or",	HB_TAG('O','R','I',' ')},	/* Oriya */
-  {"os",	HB_TAG('O','S','S',' ')},	/* Ossetian */
-  {"pa",	HB_TAG('P','A','N',' ')},	/* Panjabi */
-  {"pi",	HB_TAG('P','A','L',' ')},	/* Pali */
-  {"pl",	HB_TAG('P','L','K',' ')},	/* Polish */
-  {"plp",	HB_TAG('P','A','P',' ')},	/* Palpa */
-  {"prs",	HB_TAG('D','R','I',' ')},	/* Dari */
-  {"ps",	HB_TAG('P','A','S',' ')},	/* Pushto */
-  {"pt",	HB_TAG('P','T','G',' ')},	/* Portuguese */
-  {"raj",	HB_TAG('R','A','J',' ')},	/* Rajasthani */
-  {"ria",	HB_TAG('R','I','A',' ')},	/* Riang (India) */
-  {"ril",	HB_TAG('R','I','A',' ')},	/* Riang (Myanmar) */
-  {"ro",	HB_TAG('R','O','M',' ')},	/* Romanian */
-  {"rom",	HB_TAG('R','O','Y',' ')},	/* Romany */
-  {"ru",	HB_TAG('R','U','S',' ')},	/* Russian */
-  {"rue",	HB_TAG('R','S','Y',' ')},	/* Rusyn */
-  {"sa",	HB_TAG('S','A','N',' ')},	/* Sanskrit */
-  {"sah",	HB_TAG('Y','A','K',' ')},	/* Yakut */
-  {"sat",	HB_TAG('S','A','T',' ')},	/* Santali */
-  {"sck",	HB_TAG('S','A','D',' ')},	/* Sadri */
-  {"sd",	HB_TAG('S','N','D',' ')},	/* Sindhi */
-  {"se",	HB_TAG('N','S','M',' ')},	/* Northern Sami */
-  {"seh",	HB_TAG('S','N','A',' ')},	/* Sena */
-  {"sel",	HB_TAG('S','E','L',' ')},	/* Selkup */
-  {"sg",	HB_TAG('S','G','O',' ')},	/* Sango */
-  {"shn",	HB_TAG('S','H','N',' ')},	/* Shan */
-  {"si",	HB_TAG('S','N','H',' ')},	/* Sinhala */
-  {"sid",	HB_TAG('S','I','D',' ')},	/* Sidamo */
-  {"sjd",	HB_TAG('K','S','M',' ')},	/* Kildin Sami */
-  {"sk",	HB_TAG('S','K','Y',' ')},	/* Slovak */
-  {"skr",	HB_TAG('S','R','K',' ')},	/* Seraiki */
-  {"sl",	HB_TAG('S','L','V',' ')},	/* Slovenian */
-  {"sm",	HB_TAG('S','M','O',' ')},	/* Samoan */
-  {"sma",	HB_TAG('S','S','M',' ')},	/* Southern Sami */
-  {"smj",	HB_TAG('L','S','M',' ')},	/* Lule Sami */
-  {"smn",	HB_TAG('I','S','M',' ')},	/* Inari Sami */
-  {"sms",	HB_TAG('S','K','S',' ')},	/* Skolt Sami */
-  {"snk",	HB_TAG('S','N','K',' ')},	/* Soninke */
-  {"so",	HB_TAG('S','M','L',' ')},	/* Somali */
-  {"sq",	HB_TAG('S','Q','I',' ')},	/* Albanian */
-  {"sr",	HB_TAG('S','R','B',' ')},	/* Serbian */
-  {"srr",	HB_TAG('S','R','R',' ')},	/* Serer */
-  {"suq",	HB_TAG('S','U','R',' ')},	/* Suri */
-  {"sv",	HB_TAG('S','V','E',' ')},	/* Swedish */
-  {"sva",	HB_TAG('S','V','A',' ')},	/* Svan */
-  {"sw",	HB_TAG('S','W','K',' ')},	/* Swahili */
-  {"swb",	HB_TAG('C','M','R',' ')},	/* Comorian */
-  {"syr",	HB_TAG('S','Y','R',' ')},	/* Syriac */
-  {"ta",	HB_TAG('T','A','M',' ')},	/* Tamil */
-  {"tcy",	HB_TAG('T','U','L',' ')},	/* Tulu */
-  {"te",	HB_TAG('T','E','L',' ')},	/* Telugu */
-  {"tg",	HB_TAG('T','A','J',' ')},	/* Tajik */
-  {"th",	HB_TAG('T','H','A',' ')},	/* Thai */
-  {"ti",	HB_TAG('T','G','Y',' ')},	/* Tigrinya */
-  {"tig",	HB_TAG('T','G','R',' ')},	/* Tigre */
-  {"tk",	HB_TAG('T','K','M',' ')},	/* Turkmen */
-  {"tn",	HB_TAG('T','N','A',' ')},	/* Tswana */
-  {"tnz",	HB_TAG('T','N','G',' ')},	/* Tonga (Thailand) */
-  {"to",	HB_TAG('T','N','G',' ')},	/* Tonga (Tonga Islands) */
-  {"tog",	HB_TAG('T','N','G',' ')},	/* Tonga (Nyasa) */
-  {"toi",	HB_TAG('T','N','G',' ')},	/* Tonga (Zambia) */
-  {"tr",	HB_TAG('T','R','K',' ')},	/* Turkish */
-  {"ts",	HB_TAG('T','S','G',' ')},	/* Tsonga */
-  {"tt",	HB_TAG('T','A','T',' ')},	/* Tatar */
-  {"tw",	HB_TAG('T','W','I',' ')},	/* Twi */
-  {"ty",	HB_TAG('T','H','T',' ')},	/* Tahitian */
-  {"udm",	HB_TAG('U','D','M',' ')},	/* Udmurt */
-  {"ug",	HB_TAG('U','Y','G',' ')},	/* Uighur */
-  {"uk",	HB_TAG('U','K','R',' ')},	/* Ukrainian */
-  {"unr",	HB_TAG('M','U','N',' ')},	/* Mundari */
-  {"ur",	HB_TAG('U','R','D',' ')},	/* Urdu */
-  {"uz",	HB_TAG('U','Z','B',' ')},	/* Uzbek */
-  {"ve",	HB_TAG('V','E','N',' ')},	/* Venda */
-  {"vi",	HB_TAG('V','I','T',' ')},	/* Vietnamese */
-  {"wbm",	HB_TAG('W','A',' ',' ')},	/* Wa */
-  {"wbr",	HB_TAG('W','A','G',' ')},	/* Wagdi */
-  {"wo",	HB_TAG('W','L','F',' ')},	/* Wolof */
-  {"xal",	HB_TAG('K','L','M',' ')},	/* Kalmyk */
-  {"xh",	HB_TAG('X','H','S',' ')},	/* Xhosa */
-  {"xom",	HB_TAG('K','M','O',' ')},	/* Komo (Sudan) */
-  {"xsl",	HB_TAG('S','S','L',' ')},	/* South Slavey */
-  {"yi",	HB_TAG('J','I','I',' ')},	/* Yiddish */
-  {"yo",	HB_TAG('Y','B','A',' ')},	/* Yoruba */
-  {"yso",	HB_TAG('N','I','S',' ')},	/* Nisi (China) */
-  {"zh-cn",	HB_TAG('Z','H','S',' ')},	/* Chinese (China) */
-  {"zh-hk",	HB_TAG('Z','H','H',' ')},	/* Chinese (Hong Kong) */
-  {"zh-mo",	HB_TAG('Z','H','T',' ')},	/* Chinese (Macao) */
-  {"zh-sg",	HB_TAG('Z','H','S',' ')},	/* Chinese (Singapore) */
-  {"zh-tw",	HB_TAG('Z','H','T',' ')},	/* Chinese (Taiwan) */
-  {"zne",	HB_TAG('Z','N','D',' ')},	/* Zande */
-  {"zu",	HB_TAG('Z','U','L',' ')} 	/* Zulu */
-
-  /* I couldn't find the language id for these */
-
-/*{"??",	HB_TAG('A','G','W',' ')},*/	/* Agaw */
-/*{"??",	HB_TAG('A','L','S',' ')},*/	/* Alsatian */
-/*{"??",	HB_TAG('A','L','T',' ')},*/	/* Altai */
-/*{"??",	HB_TAG('A','R','K',' ')},*/	/* Arakanese */
-/*{"??",	HB_TAG('A','T','H',' ')},*/	/* Athapaskan */
-/*{"??",	HB_TAG('B','A','G',' ')},*/	/* Baghelkhandi */
-/*{"??",	HB_TAG('B','A','L',' ')},*/	/* Balkar */
-/*{"??",	HB_TAG('B','A','U',' ')},*/	/* Baule */
-/*{"??",	HB_TAG('B','B','R',' ')},*/	/* Berber */
-/*{"??",	HB_TAG('B','C','R',' ')},*/	/* Bible Cree */
-/*{"??",	HB_TAG('B','E','L',' ')},*/	/* Belarussian */
-/*{"??",	HB_TAG('B','I','L',' ')},*/	/* Bilen */
-/*{"??",	HB_TAG('B','K','F',' ')},*/	/* Blackfoot */
-/*{"??",	HB_TAG('B','L','N',' ')},*/	/* Balante */
-/*{"??",	HB_TAG('B','M','L',' ')},*/	/* Bamileke */
-/*{"??",	HB_TAG('B','R','I',' ')},*/	/* Braj Bhasha */
-/*{"??",	HB_TAG('C','H','G',' ')},*/	/* Chaha Gurage */
-/*{"??",	HB_TAG('C','H','H',' ')},*/	/* Chattisgarhi */
-/*{"??",	HB_TAG('C','H','K',' ')},*/	/* Chukchi */
-/*{"??",	HB_TAG('D','J','R',' ')},*/	/* Djerma */
-/*{"??",	HB_TAG('D','N','G',' ')},*/	/* Dangme */
-/*{"??",	HB_TAG('E','C','R',' ')},*/	/* Eastern Cree */
-/*{"??",	HB_TAG('F','A','N',' ')},*/	/* French Antillean */
-/*{"??",	HB_TAG('F','L','E',' ')},*/	/* Flemish */
-/*{"??",	HB_TAG('F','N','E',' ')},*/	/* Forest Nenets */
-/*{"??",	HB_TAG('F','T','A',' ')},*/	/* Futa */
-/*{"??",	HB_TAG('G','A','R',' ')},*/	/* Garshuni */
-/*{"??",	HB_TAG('G','E','Z',' ')},*/	/* Ge'ez */
-/*{"??",	HB_TAG('H','A','L',' ')},*/	/* Halam */
-/*{"??",	HB_TAG('H','A','R',' ')},*/	/* Harauti */
-/*{"??",	HB_TAG('H','A','W',' ')},*/	/* Hawaiin */
-/*{"??",	HB_TAG('H','B','N',' ')},*/	/* Hammer-Banna */
-/*{"??",	HB_TAG('H','M','A',' ')},*/	/* High Mari */
-/*{"??",	HB_TAG('H','N','D',' ')},*/	/* Hindko */
-/*{"??",	HB_TAG('I','J','O',' ')},*/	/* Ijo */
-/*{"??",	HB_TAG('I','L','O',' ')},*/	/* Ilokano */
-/*{"??",	HB_TAG('I','R','T',' ')},*/	/* Irish Traditional */
-/*{"??",	HB_TAG('J','U','L',' ')},*/	/* Jula */
-/*{"??",	HB_TAG('K','A','R',' ')},*/	/* Karachay */
-/*{"??",	HB_TAG('K','E','B',' ')},*/	/* Kebena */
-/*{"??",	HB_TAG('K','G','E',' ')},*/	/* Khutsuri Georgian */
-/*{"??",	HB_TAG('K','H','A',' ')},*/	/* Khakass */
-/*{"??",	HB_TAG('K','H','K',' ')},*/	/* Khanty-Kazim */
-/*{"??",	HB_TAG('K','H','S',' ')},*/	/* Khanty-Shurishkar */
-/*{"??",	HB_TAG('K','H','V',' ')},*/	/* Khanty-Vakhi */
-/*{"??",	HB_TAG('K','I','S',' ')},*/	/* Kisii */
-/*{"??",	HB_TAG('K','K','N',' ')},*/	/* Kokni */
-/*{"??",	HB_TAG('K','M','S',' ')},*/	/* Komso */
-/*{"??",	HB_TAG('K','O','D',' ')},*/	/* Kodagu */
-/*{"??",	HB_TAG('K','O','H',' ')},*/	/* Korean Old Hangul */
-/*{"??",	HB_TAG('K','O','N',' ')},*/	/* Kikongo */
-/*{"??",	HB_TAG('K','R','K',' ')},*/	/* Karakalpak */
-/*{"??",	HB_TAG('K','R','N',' ')},*/	/* Karen */
-/*{"??",	HB_TAG('K','U','L',' ')},*/	/* Kulvi */
-/*{"??",	HB_TAG('L','A','H',' ')},*/	/* Lahuli */
-/*{"??",	HB_TAG('L','A','M',' ')},*/	/* Lambani */
-/*{"??",	HB_TAG('L','C','R',' ')},*/	/* L-Cree */
-/*{"??",	HB_TAG('L','E','Z',' ')},*/	/* Lezgi */
-/*{"??",	HB_TAG('L','M','A',' ')},*/	/* Low Mari */
-/*{"??",	HB_TAG('L','U','B',' ')},*/	/* Luba */
-/*{"??",	HB_TAG('L','U','G',' ')},*/	/* Luganda */
-/*{"??",	HB_TAG('L','U','H',' ')},*/	/* Luhya */
-/*{"??",	HB_TAG('M','A','K',' ')},*/	/* Makua */
-/*{"??",	HB_TAG('M','A','L',' ')},*/	/* Malayalam Traditional */
-/*{"??",	HB_TAG('M','B','N',' ')},*/	/* Mbundu */
-/*{"??",	HB_TAG('M','I','Z',' ')},*/	/* Mizo */
-/*{"??",	HB_TAG('M','L','N',' ')},*/	/* Malinke */
-/*{"??",	HB_TAG('M','N','K',' ')},*/	/* Maninka */
-/*{"??",	HB_TAG('M','O','R',' ')},*/	/* Moroccan */
-/*{"??",	HB_TAG('N','A','G',' ')},*/	/* Naga-Assamese */
-/*{"??",	HB_TAG('N','C','R',' ')},*/	/* N-Cree */
-/*{"??",	HB_TAG('N','D','B',' ')},*/	/* Ndebele */
-/*{"??",	HB_TAG('N','G','R',' ')},*/	/* Nagari */
-/*{"??",	HB_TAG('N','H','C',' ')},*/	/* Norway House Cree */
-/*{"??",	HB_TAG('N','K','L',' ')},*/	/* Nkole */
-/*{"??",	HB_TAG('N','T','A',' ')},*/	/* Northern Tai */
-/*{"??",	HB_TAG('O','C','R',' ')},*/	/* Oji-Cree */
-/*{"??",	HB_TAG('P','A','A',' ')},*/	/* Palestinian Aramaic */
-/*{"??",	HB_TAG('P','G','R',' ')},*/	/* Polytonic Greek */
-/*{"??",	HB_TAG('P','L','G',' ')},*/	/* Palaung */
-/*{"??",	HB_TAG('Q','I','N',' ')},*/	/* Chin */
-/*{"??",	HB_TAG('R','B','U',' ')},*/	/* Russian Buriat */
-/*{"??",	HB_TAG('R','C','R',' ')},*/	/* R-Cree */
-/*{"??",	HB_TAG('R','M','S',' ')},*/	/* Rhaeto-Romanic */
-/*{"??",	HB_TAG('R','U','A',' ')},*/	/* Ruanda */
-/*{"??",	HB_TAG('S','A','Y',' ')},*/	/* Sayisi */
-/*{"??",	HB_TAG('S','E','K',' ')},*/	/* Sekota */
-/*{"??",	HB_TAG('S','I','G',' ')},*/	/* Silte Gurage */
-/*{"??",	HB_TAG('S','L','A',' ')},*/	/* Slavey */
-/*{"??",	HB_TAG('S','O','G',' ')},*/	/* Sodo Gurage */
-/*{"??",	HB_TAG('S','O','T',' ')},*/	/* Sotho */
-/*{"??",	HB_TAG('S','W','A',' ')},*/	/* Swadaya Aramaic */
-/*{"??",	HB_TAG('S','W','Z',' ')},*/	/* Swazi */
-/*{"??",	HB_TAG('S','X','T',' ')},*/	/* Sutu */
-/*{"??",	HB_TAG('T','A','B',' ')},*/	/* Tabasaran */
-/*{"??",	HB_TAG('T','C','R',' ')},*/	/* TH-Cree */
-/*{"??",	HB_TAG('T','G','N',' ')},*/	/* Tongan */
-/*{"??",	HB_TAG('T','M','N',' ')},*/	/* Temne */
-/*{"??",	HB_TAG('T','N','E',' ')},*/	/* Tundra Nenets */
-/*{"??",	HB_TAG('T','O','D',' ')},*/	/* Todo */
-/*{"??",	HB_TAG('T','U','A',' ')},*/	/* Turoyo Aramaic */
-/*{"??",	HB_TAG('T','U','V',' ')},*/	/* Tuvin */
-/*{"??",	HB_TAG('W','C','R',' ')},*/	/* West-Cree */
-/*{"??",	HB_TAG('X','B','D',' ')},*/	/* Tai Lue */
-/*{"??",	HB_TAG('Y','C','R',' ')},*/	/* Y-Cree */
-/*{"??",	HB_TAG('Y','I','C',' ')},*/	/* Yi Classic */
-/*{"??",	HB_TAG('Y','I','M',' ')},*/	/* Yi Modern */
-/*{"??",	HB_TAG('Z','H','P',' ')},*/	/* Chinese Phonetic */
-};
-
-static int
-lang_compare_first_component (const char *a,
-			      const char *b)
-{
-  unsigned int da, db;
-  const char *p;
-
-  p = strstr (a, "-");
-  da = p ? (unsigned int) (p - a) : strlen (a);
-
-  p = strstr (b, "-");
-  db = p ? (unsigned int) (p - b) : strlen (b);
-
-  return strncmp (a, b, MAX (da, db));
-}
-
-static hb_bool_t
-lang_matches (const char *lang_str, const char *spec)
-{
-  unsigned int len = strlen (spec);
-
-  return lang_str && strncmp (lang_str, spec, len) == 0 &&
-	 (lang_str[len] == '\0' || lang_str[len] == '-');
-}
-
-hb_tag_t
-hb_ot_tag_from_language (hb_language_t language)
-{
-  const char *lang_str;
-  LangTag *lang_tag;
-
-  if (language == NULL)
-    return HB_OT_TAG_DEFAULT_LANGUAGE;
-
-  lang_str = hb_language_to_string (language);
-
-  if (0 == strncmp (lang_str, "x-hbot", 6)) {
-    char tag[4];
-    int i;
-    lang_str += 6;
-    for (i = 0; i < 4 && ISALPHA (lang_str[i]); i++)
-      tag[i] = TOUPPER (lang_str[i]);
-    for (; i < 4; i++)
-      tag[i] = ' ';
-    return HB_TAG_CHAR4 (tag);
-  }
-
-  /* find a language matching in the first component */
-  lang_tag = bsearch (lang_str, ot_languages,
-		      ARRAY_LENGTH (ot_languages), sizeof (LangTag),
-		      (hb_compare_func_t) lang_compare_first_component);
-
-  /* we now need to find the best language matching */
-  if (lang_tag)
-  {
-    hb_bool_t found = FALSE;
-
-    /* go to the final one matching in the first component */
-    while (lang_tag + 1 < ot_languages + ARRAY_LENGTH (ot_languages) &&
-	   lang_compare_first_component (lang_str, (lang_tag + 1)->language) == 0)
-      lang_tag++;
-
-    /* go back, find which one matches completely */
-    while (lang_tag >= ot_languages &&
-	   lang_compare_first_component (lang_str, lang_tag->language) == 0)
-    {
-      if (lang_matches (lang_str, lang_tag->language)) {
-	found = TRUE;
-	break;
-      }
-
-      lang_tag--;
-    }
-
-    if (!found)
-      lang_tag = NULL;
-  }
-
-  if (lang_tag)
-    return lang_tag->tag;
-
-  return HB_OT_TAG_DEFAULT_LANGUAGE;
-}
-
-hb_language_t
-hb_ot_tag_to_language (hb_tag_t tag)
-{
-  unsigned int i;
-  unsigned char buf[11] = "x-hbot";
-
-  for (i = 0; i < ARRAY_LENGTH (ot_languages); i++)
-    if (ot_languages[i].tag == tag)
-      return hb_language_from_string (ot_languages[i].language);
-
-  buf[6] = tag >> 24;
-  buf[7] = (tag >> 16) & 0xFF;
-  buf[8] = (tag >> 8) & 0xFF;
-  buf[9] = tag & 0xFF;
-  buf[10] = '\0';
-  return hb_language_from_string ((char *) buf);
-}
-
-
-HB_END_DECLS
diff --git a/src/hb-ot-tag.cc b/src/hb-ot-tag.cc
new file mode 100644
index 0000000..7db1885
--- /dev/null
+++ b/src/hb-ot-tag.cc
@@ -0,0 +1,677 @@
+/*
+ * Copyright (C) 2009  Red Hat, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.hh"
+#include "hb-ot.h"
+
+#include <string.h>
+
+HB_BEGIN_DECLS
+
+
+/* hb_script_t */
+
+static hb_tag_t
+hb_ot_old_tag_from_script (hb_script_t script)
+{
+  switch ((hb_tag_t) script) {
+    case HB_SCRIPT_COPTIC:		return HB_TAG('c','o','p','t');
+    case HB_SCRIPT_HIRAGANA:		return HB_TAG('k','a','n','a');
+    case HB_SCRIPT_LAO:			return HB_TAG('l','a','o',' ');
+    case HB_SCRIPT_YI:			return HB_TAG('y','i',' ',' ');
+    /* Unicode-5.0 additions */
+    case HB_SCRIPT_NKO:			return HB_TAG('n','k','o',' ');
+    /* Unicode-5.1 additions */
+    case HB_SCRIPT_VAI:			return HB_TAG('v','a','i',' ');
+    /* Unicode-5.2 additions */
+    case HB_SCRIPT_MEETEI_MAYEK:	return HB_TAG('m','y','e','i');
+    /* Unicode-6.0 additions */
+  }
+
+  /* Else, just change first char to lowercase and return */
+  return ((hb_tag_t) script) | 0x20000000;
+}
+
+static hb_script_t
+hb_ot_old_tag_to_script (hb_tag_t tag)
+{
+  switch (tag) {
+    case HB_TAG('c','o','p','t'):	return HB_SCRIPT_COPTIC;
+    case HB_TAG('k','a','n','a'):	return HB_SCRIPT_HIRAGANA;
+    case HB_TAG('l','a','o',' '):	return HB_SCRIPT_LAO;
+    case HB_TAG('y','i',' ',' '):	return HB_SCRIPT_YI;
+    /* Unicode-5.0 additions */
+    case HB_TAG('n','k','o',' '):	return HB_SCRIPT_NKO;
+    /* Unicode-5.1 additions */
+    case HB_TAG('v','a','i',' '):	return HB_SCRIPT_VAI;
+    /* Unicode-5.2 additions */
+    case HB_TAG('m','y','e','i'):	return HB_SCRIPT_MEETEI_MAYEK;
+    /* Unicode-6.0 additions */
+  }
+
+  /* Else, just change first char to uppercase and return */
+  return (hb_script_t) (tag & ~0x20000000);
+}
+
+static hb_tag_t
+hb_ot_new_tag_from_script (hb_script_t script)
+{
+  switch ((hb_tag_t) script) {
+    case HB_SCRIPT_BENGALI:		return HB_TAG('b','n','g','2');
+    case HB_SCRIPT_DEVANAGARI:		return HB_TAG('d','e','v','2');
+    case HB_SCRIPT_GUJARATI:		return HB_TAG('g','j','r','2');
+    case HB_SCRIPT_GURMUKHI:		return HB_TAG('g','u','r','2');
+    case HB_SCRIPT_KANNADA:		return HB_TAG('k','n','d','2');
+    case HB_SCRIPT_MALAYALAM:		return HB_TAG('m','l','m','2');
+    case HB_SCRIPT_ORIYA:		return HB_TAG('o','r','y','2');
+    case HB_SCRIPT_TAMIL:		return HB_TAG('t','m','l','2');
+    case HB_SCRIPT_TELUGU:		return HB_TAG('t','e','l','2');
+  }
+
+  return HB_TAG_NONE;
+}
+
+static hb_script_t
+hb_ot_new_tag_to_script (hb_tag_t tag)
+{
+  switch (tag) {
+    case HB_TAG('b','n','g','2'):	return HB_SCRIPT_BENGALI;
+    case HB_TAG('d','e','v','2'):	return HB_SCRIPT_DEVANAGARI;
+    case HB_TAG('g','j','r','2'):	return HB_SCRIPT_GUJARATI;
+    case HB_TAG('g','u','r','2'):	return HB_SCRIPT_GURMUKHI;
+    case HB_TAG('k','n','d','2'):	return HB_SCRIPT_KANNADA;
+    case HB_TAG('m','l','m','2'):	return HB_SCRIPT_MALAYALAM;
+    case HB_TAG('o','r','y','2'):	return HB_SCRIPT_ORIYA;
+    case HB_TAG('t','m','l','2'):	return HB_SCRIPT_TAMIL;
+    case HB_TAG('t','e','l','2'):	return HB_SCRIPT_TELUGU;
+  }
+
+  return HB_SCRIPT_UNKNOWN;
+}
+
+/*
+ * Complete list at:
+ * http://www.microsoft.com/typography/otspec/scripttags.htm
+ *
+ * Most of the script tags are the same as the ISO 15924 tag but lowercased.
+ * So we just do that, and handle the exceptional cases in a switch.
+ */
+
+void
+hb_ot_tags_from_script (hb_script_t  script,
+			hb_tag_t    *script_tag_1,
+			hb_tag_t    *script_tag_2)
+{
+  hb_tag_t new_tag;
+
+  *script_tag_2 = HB_TAG_NONE;
+  *script_tag_1 = hb_ot_old_tag_from_script (script);
+
+  new_tag = hb_ot_new_tag_from_script (script);
+  if (unlikely (new_tag != HB_TAG_NONE)) {
+    *script_tag_2 = *script_tag_1;
+    *script_tag_1 = new_tag;
+  }
+}
+
+hb_script_t
+hb_ot_tag_to_script (hb_tag_t tag)
+{
+  if (unlikely ((tag & 0x000000FF) == '2'))
+    return hb_ot_new_tag_to_script (tag);
+
+  return hb_ot_old_tag_to_script (tag);
+}
+
+
+/* hb_language_t */
+
+typedef struct {
+  char language[6];
+  hb_tag_t tag;
+} LangTag;
+
+/*
+ * Complete list at:
+ * http://www.microsoft.com/typography/otspec/languagetags.htm
+ *
+ * Generated by intersecting the OpenType language tag list from
+ * Draft OpenType 1.5 spec, with with the ISO 639-3 codes from
+ * 2008/08/04, matching on name, and finally adjusted manually.
+ *
+ * Many items still missing.  Those are commented out at the end.
+ * Keep sorted for bsearch.
+ */
+static const LangTag ot_languages[] = {
+  {"aa",	HB_TAG('A','F','R',' ')},	/* Afar */
+  {"ab",	HB_TAG('A','B','K',' ')},	/* Abkhazian */
+  {"abq",	HB_TAG('A','B','A',' ')},	/* Abaza */
+  {"ady",	HB_TAG('A','D','Y',' ')},	/* Adyghe */
+  {"af",	HB_TAG('A','F','K',' ')},	/* Afrikaans */
+  {"aiw",	HB_TAG('A','R','I',' ')},	/* Aari */
+  {"am",	HB_TAG('A','M','H',' ')},	/* Amharic */
+  {"ar",	HB_TAG('A','R','A',' ')},	/* Arabic */
+  {"arn",	HB_TAG('M','A','P',' ')},	/* Mapudungun */
+  {"as",	HB_TAG('A','S','M',' ')},	/* Assamese */
+  {"av",	HB_TAG('A','V','R',' ')},	/* Avaric */
+  {"awa",	HB_TAG('A','W','A',' ')},	/* Awadhi */
+  {"ay",	HB_TAG('A','Y','M',' ')},	/* Aymara */
+  {"az",	HB_TAG('A','Z','E',' ')},	/* Azerbaijani */
+  {"ba",	HB_TAG('B','S','H',' ')},	/* Bashkir */
+  {"bal",	HB_TAG('B','L','I',' ')},	/* Baluchi */
+  {"bcq",	HB_TAG('B','C','H',' ')},	/* Bench */
+  {"bem",	HB_TAG('B','E','M',' ')},	/* Bemba (Zambia) */
+  {"bfq",	HB_TAG('B','A','D',' ')},	/* Badaga */
+  {"bft",	HB_TAG('B','L','T',' ')},	/* Balti */
+  {"bg",	HB_TAG('B','G','R',' ')},	/* Bulgarian */
+  {"bhb",	HB_TAG('B','H','I',' ')},	/* Bhili */
+  {"bho",	HB_TAG('B','H','O',' ')},	/* Bhojpuri */
+  {"bik",	HB_TAG('B','I','K',' ')},	/* Bikol */
+  {"bin",	HB_TAG('E','D','O',' ')},	/* Bini */
+  {"bm",	HB_TAG('B','M','B',' ')},	/* Bambara */
+  {"bn",	HB_TAG('B','E','N',' ')},	/* Bengali */
+  {"bo",	HB_TAG('T','I','B',' ')},	/* Tibetan */
+  {"br",	HB_TAG('B','R','E',' ')},	/* Breton */
+  {"brh",	HB_TAG('B','R','H',' ')},	/* Brahui */
+  {"bs",	HB_TAG('B','O','S',' ')},	/* Bosnian */
+  {"btb",	HB_TAG('B','T','I',' ')},	/* Beti (Cameroon) */
+  {"ca",	HB_TAG('C','A','T',' ')},	/* Catalan */
+  {"ce",	HB_TAG('C','H','E',' ')},	/* Chechen */
+  {"ceb",	HB_TAG('C','E','B',' ')},	/* Cebuano */
+  {"chp",	HB_TAG('C','H','P',' ')},	/* Chipewyan */
+  {"chr",	HB_TAG('C','H','R',' ')},	/* Cherokee */
+  {"cop",	HB_TAG('C','O','P',' ')},	/* Coptic */
+  {"cr",	HB_TAG('C','R','E',' ')},	/* Cree */
+  {"crh",	HB_TAG('C','R','T',' ')},	/* Crimean Tatar */
+  {"crm",	HB_TAG('M','C','R',' ')},	/* Moose Cree */
+  {"crx",	HB_TAG('C','R','R',' ')},	/* Carrier */
+  {"cs",	HB_TAG('C','S','Y',' ')},	/* Czech */
+  {"cu",	HB_TAG('C','S','L',' ')},	/* Church Slavic */
+  {"cv",	HB_TAG('C','H','U',' ')},	/* Chuvash */
+  {"cwd",	HB_TAG('D','C','R',' ')},	/* Woods Cree */
+  {"cy",	HB_TAG('W','E','L',' ')},	/* Welsh */
+  {"da",	HB_TAG('D','A','N',' ')},	/* Danish */
+  {"dap",	HB_TAG('N','I','S',' ')},	/* Nisi (India) */
+  {"dar",	HB_TAG('D','A','R',' ')},	/* Dargwa */
+  {"de",	HB_TAG('D','E','U',' ')},	/* German */
+  {"din",	HB_TAG('D','N','K',' ')},	/* Dinka */
+  {"dng",	HB_TAG('D','U','N',' ')},	/* Dungan */
+  {"doi",	HB_TAG('D','G','R',' ')},	/* Dogri */
+  {"dsb",	HB_TAG('L','S','B',' ')},	/* Lower Sorbian */
+  {"dv",	HB_TAG('D','I','V',' ')},	/* Dhivehi */
+  {"dz",	HB_TAG('D','Z','N',' ')},	/* Dzongkha */
+  {"ee",	HB_TAG('E','W','E',' ')},	/* Ewe */
+  {"efi",	HB_TAG('E','F','I',' ')},	/* Efik */
+  {"el",	HB_TAG('E','L','L',' ')},	/* Modern Greek (1453-) */
+  {"en",	HB_TAG('E','N','G',' ')},	/* English */
+  {"eo",	HB_TAG('N','T','O',' ')},	/* Esperanto */
+  {"eot",	HB_TAG('B','T','I',' ')},	/* Beti (Côte d'Ivoire) */
+  {"es",	HB_TAG('E','S','P',' ')},	/* Spanish */
+  {"et",	HB_TAG('E','T','I',' ')},	/* Estonian */
+  {"eu",	HB_TAG('E','U','Q',' ')},	/* Basque */
+  {"eve",	HB_TAG('E','V','N',' ')},	/* Even */
+  {"evn",	HB_TAG('E','V','K',' ')},	/* Evenki */
+  {"fa",	HB_TAG('F','A','R',' ')},	/* Persian */
+  {"ff",	HB_TAG('F','U','L',' ')},	/* Fulah */
+  {"fi",	HB_TAG('F','I','N',' ')},	/* Finnish */
+  {"fil",	HB_TAG('P','I','L',' ')},	/* Filipino */
+  {"fj",	HB_TAG('F','J','I',' ')},	/* Fijian */
+  {"fo",	HB_TAG('F','O','S',' ')},	/* Faroese */
+  {"fon",	HB_TAG('F','O','N',' ')},	/* Fon */
+  {"fr",	HB_TAG('F','R','A',' ')},	/* French */
+  {"fur",	HB_TAG('F','R','L',' ')},	/* Friulian */
+  {"fy",	HB_TAG('F','R','I',' ')},	/* Western Frisian */
+  {"ga",	HB_TAG('I','R','I',' ')},	/* Irish */
+  {"gaa",	HB_TAG('G','A','D',' ')},	/* Ga */
+  {"gag",	HB_TAG('G','A','G',' ')},	/* Gagauz */
+  {"gbm",	HB_TAG('G','A','W',' ')},	/* Garhwali */
+  {"gd",	HB_TAG('G','A','E',' ')},	/* Scottish Gaelic */
+  {"gl",	HB_TAG('G','A','L',' ')},	/* Galician */
+  {"gld",	HB_TAG('N','A','N',' ')},	/* Nanai */
+  {"gn",	HB_TAG('G','U','A',' ')},	/* Guarani */
+  {"gon",	HB_TAG('G','O','N',' ')},	/* Gondi */
+  {"grt",	HB_TAG('G','R','O',' ')},	/* Garo */
+  {"gu",	HB_TAG('G','U','J',' ')},	/* Gujarati */
+  {"guk",	HB_TAG('G','M','Z',' ')},	/* Gumuz */
+  {"gv",	HB_TAG('M','N','X',' ')},	/* Manx Gaelic */
+  {"ha",	HB_TAG('H','A','U',' ')},	/* Hausa */
+  {"har",	HB_TAG('H','R','I',' ')},	/* Harari */
+  {"he",	HB_TAG('I','W','R',' ')},	/* Hebrew */
+  {"hi",	HB_TAG('H','I','N',' ')},	/* Hindi */
+  {"hil",	HB_TAG('H','I','L',' ')},	/* Hiligaynon */
+  {"hoc",	HB_TAG('H','O',' ',' ')},	/* Ho */
+  {"hr",	HB_TAG('H','R','V',' ')},	/* Croatian */
+  {"hsb",	HB_TAG('U','S','B',' ')},	/* Upper Sorbian */
+  {"ht",	HB_TAG('H','A','I',' ')},	/* Haitian */
+  {"hu",	HB_TAG('H','U','N',' ')},	/* Hungarian */
+  {"hy",	HB_TAG('H','Y','E',' ')},	/* Armenian */
+  {"id",	HB_TAG('I','N','D',' ')},	/* Indonesian */
+  {"ig",	HB_TAG('I','B','O',' ')},	/* Igbo */
+  {"igb",	HB_TAG('E','B','I',' ')},	/* Ebira */
+  {"inh",	HB_TAG('I','N','G',' ')},	/* Ingush */
+  {"is",	HB_TAG('I','S','L',' ')},	/* Icelandic */
+  {"it",	HB_TAG('I','T','A',' ')},	/* Italian */
+  {"iu",	HB_TAG('I','N','U',' ')},	/* Inuktitut */
+  {"ja",	HB_TAG('J','A','N',' ')},	/* Japanese */
+  {"jv",	HB_TAG('J','A','V',' ')},	/* Javanese */
+  {"ka",	HB_TAG('K','A','T',' ')},	/* Georgian */
+  {"kam",	HB_TAG('K','M','B',' ')},	/* Kamba (Kenya) */
+  {"kbd",	HB_TAG('K','A','B',' ')},	/* Kabardian */
+  {"kdr",	HB_TAG('K','R','M',' ')},	/* Karaim */
+  {"kdt",	HB_TAG('K','U','Y',' ')},	/* Kuy */
+  {"kfr",	HB_TAG('K','A','C',' ')},	/* Kachchi */
+  {"kfy",	HB_TAG('K','M','N',' ')},	/* Kumaoni */
+  {"kha",	HB_TAG('K','S','I',' ')},	/* Khasi */
+  {"khw",	HB_TAG('K','H','W',' ')},	/* Khowar */
+  {"ki",	HB_TAG('K','I','K',' ')},	/* Kikuyu */
+  {"kk",	HB_TAG('K','A','Z',' ')},	/* Kazakh */
+  {"kl",	HB_TAG('G','R','N',' ')},	/* Kalaallisut */
+  {"kln",	HB_TAG('K','A','L',' ')},	/* Kalenjin */
+  {"km",	HB_TAG('K','H','M',' ')},	/* Central Khmer */
+  {"kmw",	HB_TAG('K','M','O',' ')},	/* Komo (Democratic Republic of Congo) */
+  {"kn",	HB_TAG('K','A','N',' ')},	/* Kannada */
+  {"ko",	HB_TAG('K','O','R',' ')},	/* Korean */
+  {"koi",	HB_TAG('K','O','P',' ')},	/* Komi-Permyak */
+  {"kok",	HB_TAG('K','O','K',' ')},	/* Konkani */
+  {"kpe",	HB_TAG('K','P','L',' ')},	/* Kpelle */
+  {"kpv",	HB_TAG('K','O','Z',' ')},	/* Komi-Zyrian */
+  {"kpy",	HB_TAG('K','Y','K',' ')},	/* Koryak */
+  {"kqy",	HB_TAG('K','R','T',' ')},	/* Koorete */
+  {"kr",	HB_TAG('K','N','R',' ')},	/* Kanuri */
+  {"kri",	HB_TAG('K','R','I',' ')},	/* Krio */
+  {"krl",	HB_TAG('K','R','L',' ')},	/* Karelian */
+  {"kru",	HB_TAG('K','U','U',' ')},	/* Kurukh */
+  {"ks",	HB_TAG('K','S','H',' ')},	/* Kashmiri */
+  {"ku",	HB_TAG('K','U','R',' ')},	/* Kurdish */
+  {"kum",	HB_TAG('K','U','M',' ')},	/* Kumyk */
+  {"kvd",	HB_TAG('K','U','I',' ')},	/* Kui (Indonesia) */
+  {"kxu",	HB_TAG('K','U','I',' ')},	/* Kui (India) */
+  {"ky",	HB_TAG('K','I','R',' ')},	/* Kirghiz */
+  {"la",	HB_TAG('L','A','T',' ')},	/* Latin */
+  {"lad",	HB_TAG('J','U','D',' ')},	/* Ladino */
+  {"lb",	HB_TAG('L','T','Z',' ')},	/* Luxembourgish */
+  {"lbe",	HB_TAG('L','A','K',' ')},	/* Lak */
+  {"lbj",	HB_TAG('L','D','K',' ')},	/* Ladakhi */
+  {"lif",	HB_TAG('L','M','B',' ')},	/* Limbu */
+  {"lld",	HB_TAG('L','A','D',' ')},	/* Ladin */
+  {"ln",	HB_TAG('L','I','N',' ')},	/* Lingala */
+  {"lo",	HB_TAG('L','A','O',' ')},	/* Lao */
+  {"lt",	HB_TAG('L','T','H',' ')},	/* Lithuanian */
+  {"luo",	HB_TAG('L','U','O',' ')},	/* Luo (Kenya and Tanzania) */
+  {"luw",	HB_TAG('L','U','O',' ')},	/* Luo (Cameroon) */
+  {"lv",	HB_TAG('L','V','I',' ')},	/* Latvian */
+  {"lzz",	HB_TAG('L','A','Z',' ')},	/* Laz */
+  {"mai",	HB_TAG('M','T','H',' ')},	/* Maithili */
+  {"mdc",	HB_TAG('M','L','E',' ')},	/* Male (Papua New Guinea) */
+  {"mdf",	HB_TAG('M','O','K',' ')},	/* Moksha */
+  {"mdy",	HB_TAG('M','L','E',' ')},	/* Male (Ethiopia) */
+  {"men",	HB_TAG('M','D','E',' ')},	/* Mende (Sierra Leone) */
+  {"mg",	HB_TAG('M','L','G',' ')},	/* Malagasy */
+  {"mi",	HB_TAG('M','R','I',' ')},	/* Maori */
+  {"mk",	HB_TAG('M','K','D',' ')},	/* Macedonian */
+  {"ml",	HB_TAG('M','L','R',' ')},	/* Malayalam */
+  {"mn",	HB_TAG('M','N','G',' ')},	/* Mongolian */
+  {"mnc",	HB_TAG('M','C','H',' ')},	/* Manchu */
+  {"mni",	HB_TAG('M','N','I',' ')},	/* Manipuri */
+  {"mnk",	HB_TAG('M','N','D',' ')},	/* Mandinka */
+  {"mns",	HB_TAG('M','A','N',' ')},	/* Mansi */
+  {"mnw",	HB_TAG('M','O','N',' ')},	/* Mon */
+  {"mo",	HB_TAG('M','O','L',' ')},	/* Moldavian */
+  {"moh",	HB_TAG('M','O','H',' ')},	/* Mohawk */
+  {"mpe",	HB_TAG('M','A','J',' ')},	/* Majang */
+  {"mr",	HB_TAG('M','A','R',' ')},	/* Marathi */
+  {"ms",	HB_TAG('M','L','Y',' ')},	/* Malay */
+  {"mt",	HB_TAG('M','T','S',' ')},	/* Maltese */
+  {"mwr",	HB_TAG('M','A','W',' ')},	/* Marwari */
+  {"my",	HB_TAG('B','R','M',' ')},	/* Burmese */
+  {"mym",	HB_TAG('M','E','N',' ')},	/* Me'en */
+  {"myv",	HB_TAG('E','R','Z',' ')},	/* Erzya */
+  {"nb",	HB_TAG('N','O','R',' ')},	/* Norwegian Bokmål */
+  {"nco",	HB_TAG('S','I','B',' ')},	/* Sibe */
+  {"ne",	HB_TAG('N','E','P',' ')},	/* Nepali */
+  {"new",	HB_TAG('N','E','W',' ')},	/* Newari */
+  {"ng",	HB_TAG('N','D','G',' ')},	/* Ndonga */
+  {"ngl",	HB_TAG('L','M','W',' ')},	/* Lomwe */
+  {"niu",	HB_TAG('N','I','U',' ')},	/* Niuean */
+  {"niv",	HB_TAG('G','I','L',' ')},	/* Gilyak */
+  {"nl",	HB_TAG('N','L','D',' ')},	/* Dutch */
+  {"nn",	HB_TAG('N','Y','N',' ')},	/* Norwegian Nynorsk */
+  {"no",	HB_TAG('N','O','R',' ')},	/* Norwegian (deprecated) */
+  {"nog",	HB_TAG('N','O','G',' ')},	/* Nogai */
+  {"nqo",	HB_TAG('N','K','O',' ')},	/* N'Ko */
+  {"nsk",	HB_TAG('N','A','S',' ')},	/* Naskapi */
+  {"ny",	HB_TAG('C','H','I',' ')},	/* Nyanja */
+  {"oc",	HB_TAG('O','C','I',' ')},	/* Occitan (post 1500) */
+  {"oj",	HB_TAG('O','J','B',' ')},	/* Ojibwa */
+  {"om",	HB_TAG('O','R','O',' ')},	/* Oromo */
+  {"or",	HB_TAG('O','R','I',' ')},	/* Oriya */
+  {"os",	HB_TAG('O','S','S',' ')},	/* Ossetian */
+  {"pa",	HB_TAG('P','A','N',' ')},	/* Panjabi */
+  {"pi",	HB_TAG('P','A','L',' ')},	/* Pali */
+  {"pl",	HB_TAG('P','L','K',' ')},	/* Polish */
+  {"plp",	HB_TAG('P','A','P',' ')},	/* Palpa */
+  {"prs",	HB_TAG('D','R','I',' ')},	/* Dari */
+  {"ps",	HB_TAG('P','A','S',' ')},	/* Pushto */
+  {"pt",	HB_TAG('P','T','G',' ')},	/* Portuguese */
+  {"raj",	HB_TAG('R','A','J',' ')},	/* Rajasthani */
+  {"ria",	HB_TAG('R','I','A',' ')},	/* Riang (India) */
+  {"ril",	HB_TAG('R','I','A',' ')},	/* Riang (Myanmar) */
+  {"ro",	HB_TAG('R','O','M',' ')},	/* Romanian */
+  {"rom",	HB_TAG('R','O','Y',' ')},	/* Romany */
+  {"ru",	HB_TAG('R','U','S',' ')},	/* Russian */
+  {"rue",	HB_TAG('R','S','Y',' ')},	/* Rusyn */
+  {"sa",	HB_TAG('S','A','N',' ')},	/* Sanskrit */
+  {"sah",	HB_TAG('Y','A','K',' ')},	/* Yakut */
+  {"sat",	HB_TAG('S','A','T',' ')},	/* Santali */
+  {"sck",	HB_TAG('S','A','D',' ')},	/* Sadri */
+  {"sd",	HB_TAG('S','N','D',' ')},	/* Sindhi */
+  {"se",	HB_TAG('N','S','M',' ')},	/* Northern Sami */
+  {"seh",	HB_TAG('S','N','A',' ')},	/* Sena */
+  {"sel",	HB_TAG('S','E','L',' ')},	/* Selkup */
+  {"sg",	HB_TAG('S','G','O',' ')},	/* Sango */
+  {"shn",	HB_TAG('S','H','N',' ')},	/* Shan */
+  {"si",	HB_TAG('S','N','H',' ')},	/* Sinhala */
+  {"sid",	HB_TAG('S','I','D',' ')},	/* Sidamo */
+  {"sjd",	HB_TAG('K','S','M',' ')},	/* Kildin Sami */
+  {"sk",	HB_TAG('S','K','Y',' ')},	/* Slovak */
+  {"skr",	HB_TAG('S','R','K',' ')},	/* Seraiki */
+  {"sl",	HB_TAG('S','L','V',' ')},	/* Slovenian */
+  {"sm",	HB_TAG('S','M','O',' ')},	/* Samoan */
+  {"sma",	HB_TAG('S','S','M',' ')},	/* Southern Sami */
+  {"smj",	HB_TAG('L','S','M',' ')},	/* Lule Sami */
+  {"smn",	HB_TAG('I','S','M',' ')},	/* Inari Sami */
+  {"sms",	HB_TAG('S','K','S',' ')},	/* Skolt Sami */
+  {"snk",	HB_TAG('S','N','K',' ')},	/* Soninke */
+  {"so",	HB_TAG('S','M','L',' ')},	/* Somali */
+  {"sq",	HB_TAG('S','Q','I',' ')},	/* Albanian */
+  {"sr",	HB_TAG('S','R','B',' ')},	/* Serbian */
+  {"srr",	HB_TAG('S','R','R',' ')},	/* Serer */
+  {"suq",	HB_TAG('S','U','R',' ')},	/* Suri */
+  {"sv",	HB_TAG('S','V','E',' ')},	/* Swedish */
+  {"sva",	HB_TAG('S','V','A',' ')},	/* Svan */
+  {"sw",	HB_TAG('S','W','K',' ')},	/* Swahili */
+  {"swb",	HB_TAG('C','M','R',' ')},	/* Comorian */
+  {"syr",	HB_TAG('S','Y','R',' ')},	/* Syriac */
+  {"ta",	HB_TAG('T','A','M',' ')},	/* Tamil */
+  {"tcy",	HB_TAG('T','U','L',' ')},	/* Tulu */
+  {"te",	HB_TAG('T','E','L',' ')},	/* Telugu */
+  {"tg",	HB_TAG('T','A','J',' ')},	/* Tajik */
+  {"th",	HB_TAG('T','H','A',' ')},	/* Thai */
+  {"ti",	HB_TAG('T','G','Y',' ')},	/* Tigrinya */
+  {"tig",	HB_TAG('T','G','R',' ')},	/* Tigre */
+  {"tk",	HB_TAG('T','K','M',' ')},	/* Turkmen */
+  {"tn",	HB_TAG('T','N','A',' ')},	/* Tswana */
+  {"tnz",	HB_TAG('T','N','G',' ')},	/* Tonga (Thailand) */
+  {"to",	HB_TAG('T','N','G',' ')},	/* Tonga (Tonga Islands) */
+  {"tog",	HB_TAG('T','N','G',' ')},	/* Tonga (Nyasa) */
+  {"toi",	HB_TAG('T','N','G',' ')},	/* Tonga (Zambia) */
+  {"tr",	HB_TAG('T','R','K',' ')},	/* Turkish */
+  {"ts",	HB_TAG('T','S','G',' ')},	/* Tsonga */
+  {"tt",	HB_TAG('T','A','T',' ')},	/* Tatar */
+  {"tw",	HB_TAG('T','W','I',' ')},	/* Twi */
+  {"ty",	HB_TAG('T','H','T',' ')},	/* Tahitian */
+  {"udm",	HB_TAG('U','D','M',' ')},	/* Udmurt */
+  {"ug",	HB_TAG('U','Y','G',' ')},	/* Uighur */
+  {"uk",	HB_TAG('U','K','R',' ')},	/* Ukrainian */
+  {"unr",	HB_TAG('M','U','N',' ')},	/* Mundari */
+  {"ur",	HB_TAG('U','R','D',' ')},	/* Urdu */
+  {"uz",	HB_TAG('U','Z','B',' ')},	/* Uzbek */
+  {"ve",	HB_TAG('V','E','N',' ')},	/* Venda */
+  {"vi",	HB_TAG('V','I','T',' ')},	/* Vietnamese */
+  {"wbm",	HB_TAG('W','A',' ',' ')},	/* Wa */
+  {"wbr",	HB_TAG('W','A','G',' ')},	/* Wagdi */
+  {"wo",	HB_TAG('W','L','F',' ')},	/* Wolof */
+  {"xal",	HB_TAG('K','L','M',' ')},	/* Kalmyk */
+  {"xh",	HB_TAG('X','H','S',' ')},	/* Xhosa */
+  {"xom",	HB_TAG('K','M','O',' ')},	/* Komo (Sudan) */
+  {"xsl",	HB_TAG('S','S','L',' ')},	/* South Slavey */
+  {"yi",	HB_TAG('J','I','I',' ')},	/* Yiddish */
+  {"yo",	HB_TAG('Y','B','A',' ')},	/* Yoruba */
+  {"yso",	HB_TAG('N','I','S',' ')},	/* Nisi (China) */
+  {"zh-cn",	HB_TAG('Z','H','S',' ')},	/* Chinese (China) */
+  {"zh-hk",	HB_TAG('Z','H','H',' ')},	/* Chinese (Hong Kong) */
+  {"zh-mo",	HB_TAG('Z','H','T',' ')},	/* Chinese (Macao) */
+  {"zh-sg",	HB_TAG('Z','H','S',' ')},	/* Chinese (Singapore) */
+  {"zh-tw",	HB_TAG('Z','H','T',' ')},	/* Chinese (Taiwan) */
+  {"zne",	HB_TAG('Z','N','D',' ')},	/* Zande */
+  {"zu",	HB_TAG('Z','U','L',' ')} 	/* Zulu */
+
+  /* I couldn't find the language id for these */
+
+/*{"??",	HB_TAG('A','G','W',' ')},*/	/* Agaw */
+/*{"??",	HB_TAG('A','L','S',' ')},*/	/* Alsatian */
+/*{"??",	HB_TAG('A','L','T',' ')},*/	/* Altai */
+/*{"??",	HB_TAG('A','R','K',' ')},*/	/* Arakanese */
+/*{"??",	HB_TAG('A','T','H',' ')},*/	/* Athapaskan */
+/*{"??",	HB_TAG('B','A','G',' ')},*/	/* Baghelkhandi */
+/*{"??",	HB_TAG('B','A','L',' ')},*/	/* Balkar */
+/*{"??",	HB_TAG('B','A','U',' ')},*/	/* Baule */
+/*{"??",	HB_TAG('B','B','R',' ')},*/	/* Berber */
+/*{"??",	HB_TAG('B','C','R',' ')},*/	/* Bible Cree */
+/*{"??",	HB_TAG('B','E','L',' ')},*/	/* Belarussian */
+/*{"??",	HB_TAG('B','I','L',' ')},*/	/* Bilen */
+/*{"??",	HB_TAG('B','K','F',' ')},*/	/* Blackfoot */
+/*{"??",	HB_TAG('B','L','N',' ')},*/	/* Balante */
+/*{"??",	HB_TAG('B','M','L',' ')},*/	/* Bamileke */
+/*{"??",	HB_TAG('B','R','I',' ')},*/	/* Braj Bhasha */
+/*{"??",	HB_TAG('C','H','G',' ')},*/	/* Chaha Gurage */
+/*{"??",	HB_TAG('C','H','H',' ')},*/	/* Chattisgarhi */
+/*{"??",	HB_TAG('C','H','K',' ')},*/	/* Chukchi */
+/*{"??",	HB_TAG('D','J','R',' ')},*/	/* Djerma */
+/*{"??",	HB_TAG('D','N','G',' ')},*/	/* Dangme */
+/*{"??",	HB_TAG('E','C','R',' ')},*/	/* Eastern Cree */
+/*{"??",	HB_TAG('F','A','N',' ')},*/	/* French Antillean */
+/*{"??",	HB_TAG('F','L','E',' ')},*/	/* Flemish */
+/*{"??",	HB_TAG('F','N','E',' ')},*/	/* Forest Nenets */
+/*{"??",	HB_TAG('F','T','A',' ')},*/	/* Futa */
+/*{"??",	HB_TAG('G','A','R',' ')},*/	/* Garshuni */
+/*{"??",	HB_TAG('G','E','Z',' ')},*/	/* Ge'ez */
+/*{"??",	HB_TAG('H','A','L',' ')},*/	/* Halam */
+/*{"??",	HB_TAG('H','A','R',' ')},*/	/* Harauti */
+/*{"??",	HB_TAG('H','A','W',' ')},*/	/* Hawaiin */
+/*{"??",	HB_TAG('H','B','N',' ')},*/	/* Hammer-Banna */
+/*{"??",	HB_TAG('H','M','A',' ')},*/	/* High Mari */
+/*{"??",	HB_TAG('H','N','D',' ')},*/	/* Hindko */
+/*{"??",	HB_TAG('I','J','O',' ')},*/	/* Ijo */
+/*{"??",	HB_TAG('I','L','O',' ')},*/	/* Ilokano */
+/*{"??",	HB_TAG('I','R','T',' ')},*/	/* Irish Traditional */
+/*{"??",	HB_TAG('J','U','L',' ')},*/	/* Jula */
+/*{"??",	HB_TAG('K','A','R',' ')},*/	/* Karachay */
+/*{"??",	HB_TAG('K','E','B',' ')},*/	/* Kebena */
+/*{"??",	HB_TAG('K','G','E',' ')},*/	/* Khutsuri Georgian */
+/*{"??",	HB_TAG('K','H','A',' ')},*/	/* Khakass */
+/*{"??",	HB_TAG('K','H','K',' ')},*/	/* Khanty-Kazim */
+/*{"??",	HB_TAG('K','H','S',' ')},*/	/* Khanty-Shurishkar */
+/*{"??",	HB_TAG('K','H','V',' ')},*/	/* Khanty-Vakhi */
+/*{"??",	HB_TAG('K','I','S',' ')},*/	/* Kisii */
+/*{"??",	HB_TAG('K','K','N',' ')},*/	/* Kokni */
+/*{"??",	HB_TAG('K','M','S',' ')},*/	/* Komso */
+/*{"??",	HB_TAG('K','O','D',' ')},*/	/* Kodagu */
+/*{"??",	HB_TAG('K','O','H',' ')},*/	/* Korean Old Hangul */
+/*{"??",	HB_TAG('K','O','N',' ')},*/	/* Kikongo */
+/*{"??",	HB_TAG('K','R','K',' ')},*/	/* Karakalpak */
+/*{"??",	HB_TAG('K','R','N',' ')},*/	/* Karen */
+/*{"??",	HB_TAG('K','U','L',' ')},*/	/* Kulvi */
+/*{"??",	HB_TAG('L','A','H',' ')},*/	/* Lahuli */
+/*{"??",	HB_TAG('L','A','M',' ')},*/	/* Lambani */
+/*{"??",	HB_TAG('L','C','R',' ')},*/	/* L-Cree */
+/*{"??",	HB_TAG('L','E','Z',' ')},*/	/* Lezgi */
+/*{"??",	HB_TAG('L','M','A',' ')},*/	/* Low Mari */
+/*{"??",	HB_TAG('L','U','B',' ')},*/	/* Luba */
+/*{"??",	HB_TAG('L','U','G',' ')},*/	/* Luganda */
+/*{"??",	HB_TAG('L','U','H',' ')},*/	/* Luhya */
+/*{"??",	HB_TAG('M','A','K',' ')},*/	/* Makua */
+/*{"??",	HB_TAG('M','A','L',' ')},*/	/* Malayalam Traditional */
+/*{"??",	HB_TAG('M','B','N',' ')},*/	/* Mbundu */
+/*{"??",	HB_TAG('M','I','Z',' ')},*/	/* Mizo */
+/*{"??",	HB_TAG('M','L','N',' ')},*/	/* Malinke */
+/*{"??",	HB_TAG('M','N','K',' ')},*/	/* Maninka */
+/*{"??",	HB_TAG('M','O','R',' ')},*/	/* Moroccan */
+/*{"??",	HB_TAG('N','A','G',' ')},*/	/* Naga-Assamese */
+/*{"??",	HB_TAG('N','C','R',' ')},*/	/* N-Cree */
+/*{"??",	HB_TAG('N','D','B',' ')},*/	/* Ndebele */
+/*{"??",	HB_TAG('N','G','R',' ')},*/	/* Nagari */
+/*{"??",	HB_TAG('N','H','C',' ')},*/	/* Norway House Cree */
+/*{"??",	HB_TAG('N','K','L',' ')},*/	/* Nkole */
+/*{"??",	HB_TAG('N','T','A',' ')},*/	/* Northern Tai */
+/*{"??",	HB_TAG('O','C','R',' ')},*/	/* Oji-Cree */
+/*{"??",	HB_TAG('P','A','A',' ')},*/	/* Palestinian Aramaic */
+/*{"??",	HB_TAG('P','G','R',' ')},*/	/* Polytonic Greek */
+/*{"??",	HB_TAG('P','L','G',' ')},*/	/* Palaung */
+/*{"??",	HB_TAG('Q','I','N',' ')},*/	/* Chin */
+/*{"??",	HB_TAG('R','B','U',' ')},*/	/* Russian Buriat */
+/*{"??",	HB_TAG('R','C','R',' ')},*/	/* R-Cree */
+/*{"??",	HB_TAG('R','M','S',' ')},*/	/* Rhaeto-Romanic */
+/*{"??",	HB_TAG('R','U','A',' ')},*/	/* Ruanda */
+/*{"??",	HB_TAG('S','A','Y',' ')},*/	/* Sayisi */
+/*{"??",	HB_TAG('S','E','K',' ')},*/	/* Sekota */
+/*{"??",	HB_TAG('S','I','G',' ')},*/	/* Silte Gurage */
+/*{"??",	HB_TAG('S','L','A',' ')},*/	/* Slavey */
+/*{"??",	HB_TAG('S','O','G',' ')},*/	/* Sodo Gurage */
+/*{"??",	HB_TAG('S','O','T',' ')},*/	/* Sotho */
+/*{"??",	HB_TAG('S','W','A',' ')},*/	/* Swadaya Aramaic */
+/*{"??",	HB_TAG('S','W','Z',' ')},*/	/* Swazi */
+/*{"??",	HB_TAG('S','X','T',' ')},*/	/* Sutu */
+/*{"??",	HB_TAG('T','A','B',' ')},*/	/* Tabasaran */
+/*{"??",	HB_TAG('T','C','R',' ')},*/	/* TH-Cree */
+/*{"??",	HB_TAG('T','G','N',' ')},*/	/* Tongan */
+/*{"??",	HB_TAG('T','M','N',' ')},*/	/* Temne */
+/*{"??",	HB_TAG('T','N','E',' ')},*/	/* Tundra Nenets */
+/*{"??",	HB_TAG('T','O','D',' ')},*/	/* Todo */
+/*{"??",	HB_TAG('T','U','A',' ')},*/	/* Turoyo Aramaic */
+/*{"??",	HB_TAG('T','U','V',' ')},*/	/* Tuvin */
+/*{"??",	HB_TAG('W','C','R',' ')},*/	/* West-Cree */
+/*{"??",	HB_TAG('X','B','D',' ')},*/	/* Tai Lue */
+/*{"??",	HB_TAG('Y','C','R',' ')},*/	/* Y-Cree */
+/*{"??",	HB_TAG('Y','I','C',' ')},*/	/* Yi Classic */
+/*{"??",	HB_TAG('Y','I','M',' ')},*/	/* Yi Modern */
+/*{"??",	HB_TAG('Z','H','P',' ')},*/	/* Chinese Phonetic */
+};
+
+static int
+lang_compare_first_component (const char *a,
+			      const char *b)
+{
+  unsigned int da, db;
+  const char *p;
+
+  p = strstr (a, "-");
+  da = p ? (unsigned int) (p - a) : strlen (a);
+
+  p = strstr (b, "-");
+  db = p ? (unsigned int) (p - b) : strlen (b);
+
+  return strncmp (a, b, MAX (da, db));
+}
+
+static hb_bool_t
+lang_matches (const char *lang_str, const char *spec)
+{
+  unsigned int len = strlen (spec);
+
+  return lang_str && strncmp (lang_str, spec, len) == 0 &&
+	 (lang_str[len] == '\0' || lang_str[len] == '-');
+}
+
+hb_tag_t
+hb_ot_tag_from_language (hb_language_t language)
+{
+  const char *lang_str;
+  LangTag *lang_tag;
+
+  if (language == NULL)
+    return HB_OT_TAG_DEFAULT_LANGUAGE;
+
+  lang_str = hb_language_to_string (language);
+
+  if (0 == strncmp (lang_str, "x-hbot", 6)) {
+    char tag[4];
+    int i;
+    lang_str += 6;
+    for (i = 0; i < 4 && ISALPHA (lang_str[i]); i++)
+      tag[i] = TOUPPER (lang_str[i]);
+    for (; i < 4; i++)
+      tag[i] = ' ';
+    return HB_TAG_CHAR4 (tag);
+  }
+
+  /* find a language matching in the first component */
+  lang_tag = (LangTag *) bsearch (lang_str, ot_languages,
+				  ARRAY_LENGTH (ot_languages), sizeof (LangTag),
+				  (hb_compare_func_t) lang_compare_first_component);
+
+  /* we now need to find the best language matching */
+  if (lang_tag)
+  {
+    hb_bool_t found = FALSE;
+
+    /* go to the final one matching in the first component */
+    while (lang_tag + 1 < ot_languages + ARRAY_LENGTH (ot_languages) &&
+	   lang_compare_first_component (lang_str, (lang_tag + 1)->language) == 0)
+      lang_tag++;
+
+    /* go back, find which one matches completely */
+    while (lang_tag >= ot_languages &&
+	   lang_compare_first_component (lang_str, lang_tag->language) == 0)
+    {
+      if (lang_matches (lang_str, lang_tag->language)) {
+	found = TRUE;
+	break;
+      }
+
+      lang_tag--;
+    }
+
+    if (!found)
+      lang_tag = NULL;
+  }
+
+  if (lang_tag)
+    return lang_tag->tag;
+
+  return HB_OT_TAG_DEFAULT_LANGUAGE;
+}
+
+hb_language_t
+hb_ot_tag_to_language (hb_tag_t tag)
+{
+  unsigned int i;
+  unsigned char buf[11] = "x-hbot";
+
+  for (i = 0; i < ARRAY_LENGTH (ot_languages); i++)
+    if (ot_languages[i].tag == tag)
+      return hb_language_from_string (ot_languages[i].language);
+
+  buf[6] = tag >> 24;
+  buf[7] = (tag >> 16) & 0xFF;
+  buf[8] = (tag >> 8) & 0xFF;
+  buf[9] = tag & 0xFF;
+  buf[10] = '\0';
+  return hb_language_from_string ((char *) buf);
+}
+
+
+HB_END_DECLS
diff --git a/src/hb-private.h b/src/hb-private.h
deleted file mode 100644
index ae10b5d..0000000
--- a/src/hb-private.h
+++ /dev/null
@@ -1,301 +0,0 @@
-/*
- * Copyright (C) 2007,2008,2009  Red Hat, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Red Hat Author(s): Behdad Esfahbod
- */
-
-#ifndef HB_PRIVATE_H
-#define HB_PRIVATE_H
-
-#if HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include "hb-common.h"
-
-#include <stdlib.h>
-#include <string.h>
-#include <assert.h>
-
-/* We only use these two for debug output.  However, the debug code is
- * always seen by the compiler (and optimized out in non-debug builds.
- * If including these becomes a problem, we can start thinking about
- * someway around that. */
-#include <stdio.h>
-#include <errno.h>
-
-HB_BEGIN_DECLS
-
-
-/* Essentials */
-
-#ifndef NULL
-# define NULL ((void *) 0)
-#endif
-
-#undef FALSE
-#define FALSE 0
-
-#undef TRUE
-#define TRUE 1
-
-
-/* Basics */
-
-#undef MIN
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-
-#undef MAX
-#define MAX(a,b) ((a) > (b) ? (a) : (b))
-
-#undef  ARRAY_LENGTH
-#define ARRAY_LENGTH(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
-
-#define HB_STMT_START do
-#define HB_STMT_END   while (0)
-
-#define _ASSERT_STATIC1(_line, _cond) typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1]
-#define _ASSERT_STATIC0(_line, _cond) _ASSERT_STATIC1 (_line, (_cond))
-#define ASSERT_STATIC(_cond) _ASSERT_STATIC0 (__LINE__, (_cond))
-
-
-/* Lets assert int types.  Saves trouble down the road. */
-
-ASSERT_STATIC (sizeof (int8_t) == 1);
-ASSERT_STATIC (sizeof (uint8_t) == 1);
-ASSERT_STATIC (sizeof (int16_t) == 2);
-ASSERT_STATIC (sizeof (uint16_t) == 2);
-ASSERT_STATIC (sizeof (int32_t) == 4);
-ASSERT_STATIC (sizeof (uint32_t) == 4);
-ASSERT_STATIC (sizeof (int64_t) == 8);
-ASSERT_STATIC (sizeof (uint64_t) == 8);
-
-ASSERT_STATIC (sizeof (hb_codepoint_t) == 4);
-ASSERT_STATIC (sizeof (hb_position_t) == 4);
-ASSERT_STATIC (sizeof (hb_mask_t) == 4);
-ASSERT_STATIC (sizeof (hb_var_int_t) == 4);
-
-/* Misc */
-
-
-#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
-#define _HB_BOOLEAN_EXPR(expr) ((expr) ? 1 : 0)
-#define likely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 1))
-#define unlikely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 0))
-#else
-#define likely(expr) (expr)
-#define unlikely(expr) (expr)
-#endif
-
-#ifndef __GNUC__
-#undef __attribute__
-#define __attribute__(x)
-#endif
-
-#if __GNUC__ >= 3
-#define HB_PURE_FUNC	__attribute__((pure))
-#define HB_CONST_FUNC	__attribute__((const))
-#else
-#define HB_PURE_FUNC
-#define HB_CONST_FUNC
-#endif
-#if __GNUC__ >= 4
-#define HB_UNUSED	__attribute__((unused))
-#else
-#define HB_UNUSED
-#endif
-
-#ifndef HB_INTERNAL
-# define HB_INTERNAL __attribute__((__visibility__("hidden")))
-#endif
-
-
-#if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER)
-#define snprintf _snprintf
-#endif
-
-#ifdef _MSC_VER
-#undef inline
-#define inline __inline
-#endif
-
-#ifdef __STRICT_ANSI__
-#undef inline
-#define inline __inline__
-#endif
-
-
-#if __GNUC__ >= 3
-#define HB_FUNC __PRETTY_FUNCTION__
-#elif defined(_MSC_VER)
-#define HB_FUNC __FUNCSIG__
-#else
-#define HB_FUNC __func__
-#endif
-
-
-/* Return the number of 1 bits in mask. */
-static inline HB_CONST_FUNC unsigned int
-_hb_popcount32 (uint32_t mask)
-{
-#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
-  return __builtin_popcount (mask);
-#else
-  /* "HACKMEM 169" */
-  register uint32_t y;
-  y = (mask >> 1) &033333333333;
-  y = mask - y - ((y >>1) & 033333333333);
-  return (((y + (y >> 3)) & 030707070707) % 077);
-#endif
-}
-
-/* Returns the number of bits needed to store number */
-static inline HB_CONST_FUNC unsigned int
-_hb_bit_storage (unsigned int number)
-{
-#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
-  return likely (number) ? (sizeof (unsigned int) * 8 - __builtin_clz (number)) : 0;
-#else
-  register unsigned int n_bits = 0;
-  while (number) {
-    n_bits++;
-    number >>= 1;
-  }
-  return n_bits;
-#endif
-}
-
-/* Returns the number of zero bits in the least significant side of number */
-static inline HB_CONST_FUNC unsigned int
-_hb_ctz (unsigned int number)
-{
-#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
-  return likely (number) ? __builtin_ctz (number) : 0;
-#else
-  register unsigned int n_bits = 0;
-  if (unlikely (!number)) return 0;
-  while (!(number & 1)) {
-    n_bits++;
-    number >>= 1;
-  }
-  return n_bits;
-#endif
-}
-
-/* Type of bsearch() / qsort() compare function */
-typedef int (*hb_compare_func_t) (const void *, const void *);
-
-
-/* We need external help for these */
-
-#ifdef HAVE_GLIB
-
-#include <glib.h>
-
-typedef int hb_atomic_int_t;
-#define hb_atomic_int_fetch_and_add(AI, V)	g_atomic_int_exchange_and_add (&(AI), V)
-#define hb_atomic_int_get(AI)			g_atomic_int_get (&(AI))
-#define hb_atomic_int_set(AI, V)		g_atomic_int_set (&(AI), V)
-
-typedef GStaticMutex hb_mutex_t;
-#define HB_MUTEX_INIT			G_STATIC_MUTEX_INIT
-#define hb_mutex_init(M)		g_static_mutex_init (&M)
-#define hb_mutex_lock(M)		g_static_mutex_lock (&M)
-#define hb_mutex_trylock(M)		g_static_mutex_trylock (&M)
-#define hb_mutex_unlock(M)		g_static_mutex_unlock (&M)
-
-#else
-
-#ifdef _MSC_VER
-#define _HB__STR2__(x) #x
-#define _HB__STR1__(x) _HB__STR2__(x)
-#define _HB__LOC__ __FILE__ "("_HB__STR1__(__LINE__)") : Warning Msg: "
-#pragma message(_HB__LOC__"Could not find any system to define platform macros, library will NOT be thread-safe")
-#else
-#warning "Could not find any system to define platform macros, library will NOT be thread-safe"
-#endif
-
-typedef int hb_atomic_int_t;
-#define hb_atomic_int_fetch_and_add(AI, V)	((AI) += (V), (AI) - (V))
-#define hb_atomic_int_get(AI)			(AI)
-#define hb_atomic_int_set(AI, V)		HB_STMT_START { (AI) = (V); } HB_STMT_END
-
-typedef int hb_mutex_t;
-#define HB_MUTEX_INIT				0
-#define hb_mutex_init(M)			HB_STMT_START { (M) = 0; } HB_STMT_END
-#define hb_mutex_lock(M)			HB_STMT_START { (M) = 1; } HB_STMT_END
-#define hb_mutex_trylock(M)			((M) = 1, 1)
-#define hb_mutex_unlock(M)			HB_STMT_START { (M) = 0; } HB_STMT_END
-
-#endif
-
-
-/* Big-endian handling */
-
-#define hb_be_uint16(v)		((uint16_t) ((((const uint8_t *)&(v))[0] << 8) + (((const uint8_t *)&(v))[1])))
-
-#define hb_be_uint16_put(v,V)	HB_STMT_START { v[0] = (V>>8); v[1] = (V); } HB_STMT_END
-#define hb_be_uint16_get(v)	(uint16_t) ((v[0] << 8) + v[1])
-#define hb_be_uint16_cmp(a,b)	(a[0] == b[0] && a[1] == b[1])
-
-#define hb_be_uint32_put(v,V)	HB_STMT_START { v[0] = (V>>24); v[1] = (V>>16); v[2] = (V>>8); v[3] = (V); } HB_STMT_END
-#define hb_be_uint32_get(v)	(uint32_t) ((v[0] << 24) + (v[1] << 16) + (v[2] << 8) + v[3])
-#define hb_be_uint32_cmp(a,b)	(a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3])
-
-
-/* ASCII tag/character handling */
-
-#define ISALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
-#define TOUPPER(c) (((c) >= 'a' && (c) <= 'z') ? (c) - 'a' + 'A' : (c))
-#define TOLOWER(c) (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c))
-
-#define HB_TAG_CHAR4(s)   (HB_TAG(((const char *) s)[0], \
-				  ((const char *) s)[1], \
-				  ((const char *) s)[2], \
-				  ((const char *) s)[3]))
-
-
-/* Debug */
-
-#ifndef HB_DEBUG
-#define HB_DEBUG 0
-#endif
-
-static inline hb_bool_t /* always returns TRUE */
-_hb_trace (const char *what,
-	   const char *function,
-	   const void *obj,
-	   unsigned int depth,
-	   unsigned int max_depth)
-{
-  (void) ((depth < max_depth) && fprintf (stderr, "%s(%p) %-*d-> %s\n", what, obj, depth, depth, function));
-  return TRUE;
-}
-
-
-#include "hb-object-private.h"
-
-
-HB_END_DECLS
-
-#endif /* HB_PRIVATE_H */
diff --git a/src/hb-private.hh b/src/hb-private.hh
new file mode 100644
index 0000000..d08a4d0
--- /dev/null
+++ b/src/hb-private.hh
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2007,2008,2009  Red Hat, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_PRIVATE_HH
+#define HB_PRIVATE_HH
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "hb-common.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+/* We only use these two for debug output.  However, the debug code is
+ * always seen by the compiler (and optimized out in non-debug builds.
+ * If including these becomes a problem, we can start thinking about
+ * someway around that. */
+#include <stdio.h>
+#include <errno.h>
+
+HB_BEGIN_DECLS
+
+
+/* Essentials */
+
+#ifndef NULL
+# define NULL ((void *) 0)
+#endif
+
+#undef FALSE
+#define FALSE 0
+
+#undef TRUE
+#define TRUE 1
+
+
+/* Basics */
+
+#undef MIN
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+#undef MAX
+#define MAX(a,b) ((a) > (b) ? (a) : (b))
+
+#undef  ARRAY_LENGTH
+#define ARRAY_LENGTH(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
+
+#define HB_STMT_START do
+#define HB_STMT_END   while (0)
+
+#define _ASSERT_STATIC1(_line, _cond) typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1]
+#define _ASSERT_STATIC0(_line, _cond) _ASSERT_STATIC1 (_line, (_cond))
+#define ASSERT_STATIC(_cond) _ASSERT_STATIC0 (__LINE__, (_cond))
+
+
+/* Lets assert int types.  Saves trouble down the road. */
+
+ASSERT_STATIC (sizeof (int8_t) == 1);
+ASSERT_STATIC (sizeof (uint8_t) == 1);
+ASSERT_STATIC (sizeof (int16_t) == 2);
+ASSERT_STATIC (sizeof (uint16_t) == 2);
+ASSERT_STATIC (sizeof (int32_t) == 4);
+ASSERT_STATIC (sizeof (uint32_t) == 4);
+ASSERT_STATIC (sizeof (int64_t) == 8);
+ASSERT_STATIC (sizeof (uint64_t) == 8);
+
+ASSERT_STATIC (sizeof (hb_codepoint_t) == 4);
+ASSERT_STATIC (sizeof (hb_position_t) == 4);
+ASSERT_STATIC (sizeof (hb_mask_t) == 4);
+ASSERT_STATIC (sizeof (hb_var_int_t) == 4);
+
+/* Misc */
+
+
+#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
+#define _HB_BOOLEAN_EXPR(expr) ((expr) ? 1 : 0)
+#define likely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 1))
+#define unlikely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 0))
+#else
+#define likely(expr) (expr)
+#define unlikely(expr) (expr)
+#endif
+
+#ifndef __GNUC__
+#undef __attribute__
+#define __attribute__(x)
+#endif
+
+#if __GNUC__ >= 3
+#define HB_PURE_FUNC	__attribute__((pure))
+#define HB_CONST_FUNC	__attribute__((const))
+#else
+#define HB_PURE_FUNC
+#define HB_CONST_FUNC
+#endif
+#if __GNUC__ >= 4
+#define HB_UNUSED	__attribute__((unused))
+#else
+#define HB_UNUSED
+#endif
+
+#ifndef HB_INTERNAL
+# define HB_INTERNAL __attribute__((__visibility__("hidden")))
+#endif
+
+
+#if (defined(__WIN32__) && !defined(__WINE__)) || defined(_MSC_VER)
+#define snprintf _snprintf
+#endif
+
+#ifdef _MSC_VER
+#undef inline
+#define inline __inline
+#endif
+
+#ifdef __STRICT_ANSI__
+#undef inline
+#define inline __inline__
+#endif
+
+
+#if __GNUC__ >= 3
+#define HB_FUNC __PRETTY_FUNCTION__
+#elif defined(_MSC_VER)
+#define HB_FUNC __FUNCSIG__
+#else
+#define HB_FUNC __func__
+#endif
+
+
+/* Return the number of 1 bits in mask. */
+static inline HB_CONST_FUNC unsigned int
+_hb_popcount32 (uint32_t mask)
+{
+#if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
+  return __builtin_popcount (mask);
+#else
+  /* "HACKMEM 169" */
+  register uint32_t y;
+  y = (mask >> 1) &033333333333;
+  y = mask - y - ((y >>1) & 033333333333);
+  return (((y + (y >> 3)) & 030707070707) % 077);
+#endif
+}
+
+/* Returns the number of bits needed to store number */
+static inline HB_CONST_FUNC unsigned int
+_hb_bit_storage (unsigned int number)
+{
+#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
+  return likely (number) ? (sizeof (unsigned int) * 8 - __builtin_clz (number)) : 0;
+#else
+  register unsigned int n_bits = 0;
+  while (number) {
+    n_bits++;
+    number >>= 1;
+  }
+  return n_bits;
+#endif
+}
+
+/* Returns the number of zero bits in the least significant side of number */
+static inline HB_CONST_FUNC unsigned int
+_hb_ctz (unsigned int number)
+{
+#if defined(__GNUC__) && (__GNUC__ >= 4) && defined(__OPTIMIZE__)
+  return likely (number) ? __builtin_ctz (number) : 0;
+#else
+  register unsigned int n_bits = 0;
+  if (unlikely (!number)) return 0;
+  while (!(number & 1)) {
+    n_bits++;
+    number >>= 1;
+  }
+  return n_bits;
+#endif
+}
+
+/* Type of bsearch() / qsort() compare function */
+typedef int (*hb_compare_func_t) (const void *, const void *);
+
+
+/* We need external help for these */
+
+#ifdef HAVE_GLIB
+
+#include <glib.h>
+
+typedef int hb_atomic_int_t;
+#define hb_atomic_int_fetch_and_add(AI, V)	g_atomic_int_exchange_and_add (&(AI), V)
+#define hb_atomic_int_get(AI)			g_atomic_int_get (&(AI))
+#define hb_atomic_int_set(AI, V)		g_atomic_int_set (&(AI), V)
+
+typedef GStaticMutex hb_mutex_t;
+#define HB_MUTEX_INIT			G_STATIC_MUTEX_INIT
+#define hb_mutex_init(M)		g_static_mutex_init (&M)
+#define hb_mutex_lock(M)		g_static_mutex_lock (&M)
+#define hb_mutex_trylock(M)		g_static_mutex_trylock (&M)
+#define hb_mutex_unlock(M)		g_static_mutex_unlock (&M)
+
+#else
+
+#ifdef _MSC_VER
+#define _HB__STR2__(x) #x
+#define _HB__STR1__(x) _HB__STR2__(x)
+#define _HB__LOC__ __FILE__ "("_HB__STR1__(__LINE__)") : Warning Msg: "
+#pragma message(_HB__LOC__"Could not find any system to define platform macros, library will NOT be thread-safe")
+#else
+#warning "Could not find any system to define platform macros, library will NOT be thread-safe"
+#endif
+
+typedef int hb_atomic_int_t;
+#define hb_atomic_int_fetch_and_add(AI, V)	((AI) += (V), (AI) - (V))
+#define hb_atomic_int_get(AI)			(AI)
+#define hb_atomic_int_set(AI, V)		HB_STMT_START { (AI) = (V); } HB_STMT_END
+
+typedef int hb_mutex_t;
+#define HB_MUTEX_INIT				0
+#define hb_mutex_init(M)			HB_STMT_START { (M) = 0; } HB_STMT_END
+#define hb_mutex_lock(M)			HB_STMT_START { (M) = 1; } HB_STMT_END
+#define hb_mutex_trylock(M)			((M) = 1, 1)
+#define hb_mutex_unlock(M)			HB_STMT_START { (M) = 0; } HB_STMT_END
+
+#endif
+
+
+/* Big-endian handling */
+
+#define hb_be_uint16(v)		((uint16_t) ((((const uint8_t *)&(v))[0] << 8) + (((const uint8_t *)&(v))[1])))
+
+#define hb_be_uint16_put(v,V)	HB_STMT_START { v[0] = (V>>8); v[1] = (V); } HB_STMT_END
+#define hb_be_uint16_get(v)	(uint16_t) ((v[0] << 8) + v[1])
+#define hb_be_uint16_cmp(a,b)	(a[0] == b[0] && a[1] == b[1])
+
+#define hb_be_uint32_put(v,V)	HB_STMT_START { v[0] = (V>>24); v[1] = (V>>16); v[2] = (V>>8); v[3] = (V); } HB_STMT_END
+#define hb_be_uint32_get(v)	(uint32_t) ((v[0] << 24) + (v[1] << 16) + (v[2] << 8) + v[3])
+#define hb_be_uint32_cmp(a,b)	(a[0] == b[0] && a[1] == b[1] && a[2] == b[2] && a[3] == b[3])
+
+
+/* ASCII tag/character handling */
+
+#define ISALPHA(c) (((c) >= 'a' && (c) <= 'z') || ((c) >= 'A' && (c) <= 'Z'))
+#define TOUPPER(c) (((c) >= 'a' && (c) <= 'z') ? (c) - 'a' + 'A' : (c))
+#define TOLOWER(c) (((c) >= 'A' && (c) <= 'Z') ? (c) - 'A' + 'a' : (c))
+
+#define HB_TAG_CHAR4(s)   (HB_TAG(((const char *) s)[0], \
+				  ((const char *) s)[1], \
+				  ((const char *) s)[2], \
+				  ((const char *) s)[3]))
+
+
+/* Debug */
+
+#ifndef HB_DEBUG
+#define HB_DEBUG 0
+#endif
+
+static inline hb_bool_t /* always returns TRUE */
+_hb_trace (const char *what,
+	   const char *function,
+	   const void *obj,
+	   unsigned int depth,
+	   unsigned int max_depth)
+{
+  (void) ((depth < max_depth) && fprintf (stderr, "%s(%p) %-*d-> %s\n", what, obj, depth, depth, function));
+  return TRUE;
+}
+
+
+#include "hb-object-private.hh"
+
+
+HB_END_DECLS
+
+#endif /* HB_PRIVATE_HH */
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index 9d5d418..66d951b 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -24,7 +24,7 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.h"
+#include "hb-private.hh"
 
 #include "hb-shape.h"
 
diff --git a/src/hb-unicode-private.hh b/src/hb-unicode-private.hh
index 67a60f5..af15c04 100644
--- a/src/hb-unicode-private.hh
+++ b/src/hb-unicode-private.hh
@@ -31,7 +31,7 @@
 #ifndef HB_UNICODE_PRIVATE_HH
 #define HB_UNICODE_PRIVATE_HH
 
-#include "hb-private.h"
+#include "hb-private.hh"
 
 #include "hb-unicode.h"
 
diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc
index 9295be7..282a9b6 100644
--- a/src/hb-unicode.cc
+++ b/src/hb-unicode.cc
@@ -28,7 +28,7 @@
  * Google Author(s): Behdad Esfahbod
  */
 
-#include "hb-private.h"
+#include "hb-private.hh"
 
 #include "hb-unicode-private.hh"
 
diff --git a/src/hb-view.c b/src/hb-view.c
index 60097b9..ac55908 100644
--- a/src/hb-view.c
+++ b/src/hb-view.c
@@ -42,6 +42,9 @@
 #include <cairo-ft.h>
 #include <hb-ft.h>
 
+HB_BEGIN_DECLS
+
+
 /* Controlled by cmd-line options */
 static int margin_t = 10;
 static int margin_b = 10;
@@ -533,3 +536,6 @@ main (int argc, char **argv)
 
   return 0;
 }
+
+
+HB_END_DECLS
commit f19f4f9b0965ad7473a0f3a1ffcdbf16930e35d4
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Apr 20 18:25:56 2011 -0400

    Rename hb-blob.c to hb-blob.cc in preparation of more changes

diff --git a/src/Makefile.am b/src/Makefile.am
index 70c51d1..845c24a 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -11,7 +11,7 @@ lib_LTLIBRARIES = libharfbuzz.la
 HBCFLAGS =
 HBLIBS =
 HBSOURCES =  \
-	hb-blob.c \
+	hb-blob.cc \
 	hb-blob-private.h \
 	hb-buffer.cc \
 	hb-buffer-private.hh \
diff --git a/src/hb-blob.c b/src/hb-blob.c
deleted file mode 100644
index 5419078..0000000
--- a/src/hb-blob.c
+++ /dev/null
@@ -1,356 +0,0 @@
-/*
- * Copyright (C) 2009  Red Hat, Inc.
- *
- *  This is part of HarfBuzz, a text shaping library.
- *
- * Permission is hereby granted, without written agreement and without
- * license or royalty fees, to use, copy, modify, and distribute this
- * software and its documentation for any purpose, provided that the
- * above copyright notice and the following two paragraphs appear in
- * all copies of this software.
- *
- * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
- * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
- * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
- * DAMAGE.
- *
- * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
- * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
- * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
- *
- * Red Hat Author(s): Behdad Esfahbod
- */
-
-#include "hb-private.h"
-
-#include "hb-blob-private.h"
-
-#ifdef HAVE_SYS_MMAN_H
-#ifdef HAVE_UNISTD_H
-#include <unistd.h>
-#endif /* HAVE_UNISTD_H */
-#include <sys/mman.h>
-#endif /* HAVE_SYS_MMAN_H */
-
-#include <stdio.h>
-#include <errno.h>
-
-HB_BEGIN_DECLS
-
-
-#ifndef HB_DEBUG_BLOB
-#define HB_DEBUG_BLOB (HB_DEBUG+0)
-#endif
-
-hb_blob_t _hb_blob_nil = {
-  HB_REFERENCE_COUNT_INVALID, /* ref_count */
-
-  0, /* length */
-
-  HB_MUTEX_INIT, /* lock */
-
-  0, /* lock_count */
-  HB_MEMORY_MODE_READONLY, /* mode */
-
-  NULL, /* data */
-
-  NULL, /* user_data */
-  NULL  /* destroy */
-};
-
-static void
-_hb_blob_destroy_user_data (hb_blob_t *blob)
-{
-  if (blob->destroy) {
-    blob->destroy (blob->user_data);
-    blob->user_data = NULL;
-    blob->destroy = NULL;
-  }
-}
-
-static void
-_hb_blob_unlock_and_destroy (hb_blob_t *blob)
-{
-  hb_blob_unlock (blob);
-  hb_blob_destroy (blob);
-}
-
-hb_blob_t *
-hb_blob_create (const char        *data,
-		unsigned int       length,
-		hb_memory_mode_t   mode,
-		void              *user_data,
-		hb_destroy_func_t  destroy)
-{
-  hb_blob_t *blob;
-
-  if (!length || !HB_OBJECT_DO_CREATE (hb_blob_t, blob)) {
-    if (destroy)
-      destroy (user_data);
-    return &_hb_blob_nil;
-  }
-
-  hb_mutex_init (blob->lock);
-  blob->lock_count = 0;
-
-  blob->data = data;
-  blob->length = length;
-  blob->mode = mode;
-
-  blob->user_data = user_data;
-  blob->destroy = destroy;
-
-  if (blob->mode == HB_MEMORY_MODE_DUPLICATE) {
-    blob->mode = HB_MEMORY_MODE_READONLY;
-    if (!hb_blob_try_writable (blob)) {
-      hb_blob_destroy (blob);
-      return &_hb_blob_nil;
-    }
-  }
-
-  return blob;
-}
-
-hb_blob_t *
-hb_blob_create_sub_blob (hb_blob_t    *parent,
-			 unsigned int  offset,
-			 unsigned int  length)
-{
-  hb_blob_t *blob;
-  const char *pdata;
-
-  if (!length || offset >= parent->length || !HB_OBJECT_DO_CREATE (hb_blob_t, blob))
-    return &_hb_blob_nil;
-
-  pdata = hb_blob_lock (parent);
-
-  blob->data = pdata + offset;
-  blob->length = MIN (length, parent->length - offset);
-
-  hb_mutex_lock (parent->lock);
-  blob->mode = parent->mode;
-  hb_mutex_unlock (parent->lock);
-
-  blob->user_data = hb_blob_reference (parent);
-  blob->destroy = (hb_destroy_func_t) _hb_blob_unlock_and_destroy;
-
-  return blob;
-}
-
-hb_blob_t *
-hb_blob_create_empty (void)
-{
-  return &_hb_blob_nil;
-}
-
-hb_blob_t *
-hb_blob_reference (hb_blob_t *blob)
-{
-  HB_OBJECT_DO_REFERENCE (blob);
-}
-
-void
-hb_blob_destroy (hb_blob_t *blob)
-{
-  HB_OBJECT_DO_DESTROY (blob);
-
-  _hb_blob_destroy_user_data (blob);
-
-  free (blob);
-}
-
-unsigned int
-hb_blob_get_length (hb_blob_t *blob)
-{
-  return blob->length;
-}
-
-const char *
-hb_blob_lock (hb_blob_t *blob)
-{
-  if (HB_OBJECT_IS_INERT (blob))
-    return NULL;
-
-  hb_mutex_lock (blob->lock);
-
-  (void) (HB_DEBUG_BLOB &&
-    fprintf (stderr, "%p %s (%d) -> %p\n", blob, __FUNCTION__,
-	     blob->lock_count, blob->data));
-
-  blob->lock_count++;
-
-  hb_mutex_unlock (blob->lock);
-
-  return blob->data;
-}
-
-void
-hb_blob_unlock (hb_blob_t *blob)
-{
-  if (HB_OBJECT_IS_INERT (blob))
-    return;
-
-  hb_mutex_lock (blob->lock);
-
-  (void) (HB_DEBUG_BLOB &&
-    fprintf (stderr, "%p %s (%d) -> %p\n", blob, __FUNCTION__,
-	     blob->lock_count, blob->data));
-
-  assert (blob->lock_count > 0);
-  blob->lock_count--;
-
-  hb_mutex_unlock (blob->lock);
-}
-
-hb_bool_t
-hb_blob_is_writable (hb_blob_t *blob)
-{
-  hb_memory_mode_t mode;
-
-  if (HB_OBJECT_IS_INERT (blob))
-    return FALSE;
-
-  hb_mutex_lock (blob->lock);
-
-  mode = blob->mode;
-
-  hb_mutex_unlock (blob->lock);
-
-  return mode == HB_MEMORY_MODE_WRITABLE;
-}
-
-
-static hb_bool_t
-_try_make_writable_inplace_unix_locked (hb_blob_t *blob)
-{
-#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT)
-  uintptr_t pagesize = -1, mask, length;
-  const char *addr;
-
-#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
-  pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE);
-#elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
-  pagesize = (uintptr_t) sysconf (_SC_PAGESIZE);
-#elif defined(HAVE_GETPAGESIZE)
-  pagesize = (uintptr_t) getpagesize ();
-#endif
-
-  if ((uintptr_t) -1L == pagesize) {
-    (void) (HB_DEBUG_BLOB &&
-      fprintf (stderr, "%p %s: failed to get pagesize: %s\n", blob, __FUNCTION__, strerror (errno)));
-    return FALSE;
-  }
-  (void) (HB_DEBUG_BLOB &&
-    fprintf (stderr, "%p %s: pagesize is %lu\n", blob, __FUNCTION__, (unsigned long) pagesize));
-
-  mask = ~(pagesize-1);
-  addr = (const char *) (((uintptr_t) blob->data) & mask);
-  length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask)  - addr;
-  (void) (HB_DEBUG_BLOB &&
-    fprintf (stderr, "%p %s: calling mprotect on [%p..%p] (%lu bytes)\n",
-	     blob, __FUNCTION__,
-	     addr, addr+length, (unsigned long) length));
-  if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {
-    (void) (HB_DEBUG_BLOB &&
-      fprintf (stderr, "%p %s: %s\n", blob, __FUNCTION__, strerror (errno)));
-    return FALSE;
-  }
-
-  (void) (HB_DEBUG_BLOB &&
-    fprintf (stderr, "%p %s: successfully made [%p..%p] (%lu bytes) writable\n",
-	     blob, __FUNCTION__,
-	     addr, addr+length, (unsigned long) length));
-  return TRUE;
-#else
-  return FALSE;
-#endif
-}
-
-static void
-try_writable_inplace_locked (hb_blob_t *blob)
-{
-  (void) (HB_DEBUG_BLOB &&
-    fprintf (stderr, "%p %s: making writable\n", blob, __FUNCTION__));
-
-  if (_try_make_writable_inplace_unix_locked (blob)) {
-    (void) (HB_DEBUG_BLOB &&
-      fprintf (stderr, "%p %s: making writable -> succeeded\n", blob, __FUNCTION__));
-    blob->mode = HB_MEMORY_MODE_WRITABLE;
-  } else {
-    (void) (HB_DEBUG_BLOB &&
-      fprintf (stderr, "%p %s: making writable -> FAILED\n", blob, __FUNCTION__));
-    /* Failed to make writable inplace, mark that */
-    blob->mode = HB_MEMORY_MODE_READONLY;
-  }
-}
-
-hb_bool_t
-hb_blob_try_writable_inplace (hb_blob_t *blob)
-{
-  hb_memory_mode_t mode;
-
-  if (HB_OBJECT_IS_INERT (blob))
-    return FALSE;
-
-  hb_mutex_lock (blob->lock);
-
-  if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE)
-    try_writable_inplace_locked (blob);
-
-  mode = blob->mode;
-
-  hb_mutex_unlock (blob->lock);
-
-  return mode == HB_MEMORY_MODE_WRITABLE;
-}
-
-hb_bool_t
-hb_blob_try_writable (hb_blob_t *blob)
-{
-  hb_memory_mode_t mode;
-
-  if (HB_OBJECT_IS_INERT (blob))
-    return FALSE;
-
-  hb_mutex_lock (blob->lock);
-
-  if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE)
-    try_writable_inplace_locked (blob);
-
-  if (blob->mode == HB_MEMORY_MODE_READONLY)
-  {
-    char *new_data;
-
-    (void) (HB_DEBUG_BLOB &&
-      fprintf (stderr, "%p %s (%d) -> %p\n", blob, __FUNCTION__,
-	       blob->lock_count, blob->data));
-
-    if (blob->lock_count)
-      goto done;
-
-    new_data = malloc (blob->length);
-    if (new_data) {
-      (void) (HB_DEBUG_BLOB &&
-	fprintf (stderr, "%p %s: dupped successfully -> %p\n", blob, __FUNCTION__, blob->data));
-      memcpy (new_data, blob->data, blob->length);
-      _hb_blob_destroy_user_data (blob);
-      blob->mode = HB_MEMORY_MODE_WRITABLE;
-      blob->data = new_data;
-      blob->user_data = new_data;
-      blob->destroy = free;
-    }
-  }
-
-done:
-  mode = blob->mode;
-
-  hb_mutex_unlock (blob->lock);
-
-  return mode == HB_MEMORY_MODE_WRITABLE;
-}
-
-
-HB_END_DECLS
diff --git a/src/hb-blob.cc b/src/hb-blob.cc
new file mode 100644
index 0000000..511455b
--- /dev/null
+++ b/src/hb-blob.cc
@@ -0,0 +1,356 @@
+/*
+ * Copyright (C) 2009  Red Hat, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Red Hat Author(s): Behdad Esfahbod
+ */
+
+#include "hb-private.h"
+
+#include "hb-blob-private.h"
+
+#ifdef HAVE_SYS_MMAN_H
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif /* HAVE_UNISTD_H */
+#include <sys/mman.h>
+#endif /* HAVE_SYS_MMAN_H */
+
+#include <stdio.h>
+#include <errno.h>
+
+HB_BEGIN_DECLS
+
+
+#ifndef HB_DEBUG_BLOB
+#define HB_DEBUG_BLOB (HB_DEBUG+0)
+#endif
+
+hb_blob_t _hb_blob_nil = {
+  HB_REFERENCE_COUNT_INVALID, /* ref_count */
+
+  0, /* length */
+
+  HB_MUTEX_INIT, /* lock */
+
+  0, /* lock_count */
+  HB_MEMORY_MODE_READONLY, /* mode */
+
+  NULL, /* data */
+
+  NULL, /* user_data */
+  NULL  /* destroy */
+};
+
+static void
+_hb_blob_destroy_user_data (hb_blob_t *blob)
+{
+  if (blob->destroy) {
+    blob->destroy (blob->user_data);
+    blob->user_data = NULL;
+    blob->destroy = NULL;
+  }
+}
+
+static void
+_hb_blob_unlock_and_destroy (hb_blob_t *blob)
+{
+  hb_blob_unlock (blob);
+  hb_blob_destroy (blob);
+}
+
+hb_blob_t *
+hb_blob_create (const char        *data,
+		unsigned int       length,
+		hb_memory_mode_t   mode,
+		void              *user_data,
+		hb_destroy_func_t  destroy)
+{
+  hb_blob_t *blob;
+
+  if (!length || !HB_OBJECT_DO_CREATE (hb_blob_t, blob)) {
+    if (destroy)
+      destroy (user_data);
+    return &_hb_blob_nil;
+  }
+
+  hb_mutex_init (blob->lock);
+  blob->lock_count = 0;
+
+  blob->data = data;
+  blob->length = length;
+  blob->mode = mode;
+
+  blob->user_data = user_data;
+  blob->destroy = destroy;
+
+  if (blob->mode == HB_MEMORY_MODE_DUPLICATE) {
+    blob->mode = HB_MEMORY_MODE_READONLY;
+    if (!hb_blob_try_writable (blob)) {
+      hb_blob_destroy (blob);
+      return &_hb_blob_nil;
+    }
+  }
+
+  return blob;
+}
+
+hb_blob_t *
+hb_blob_create_sub_blob (hb_blob_t    *parent,
+			 unsigned int  offset,
+			 unsigned int  length)
+{
+  hb_blob_t *blob;
+  const char *pdata;
+
+  if (!length || offset >= parent->length || !HB_OBJECT_DO_CREATE (hb_blob_t, blob))
+    return &_hb_blob_nil;
+
+  pdata = hb_blob_lock (parent);
+
+  blob->data = pdata + offset;
+  blob->length = MIN (length, parent->length - offset);
+
+  hb_mutex_lock (parent->lock);
+  blob->mode = parent->mode;
+  hb_mutex_unlock (parent->lock);
+
+  blob->user_data = hb_blob_reference (parent);
+  blob->destroy = (hb_destroy_func_t) _hb_blob_unlock_and_destroy;
+
+  return blob;
+}
+
+hb_blob_t *
+hb_blob_create_empty (void)
+{
+  return &_hb_blob_nil;
+}
+
+hb_blob_t *
+hb_blob_reference (hb_blob_t *blob)
+{
+  HB_OBJECT_DO_REFERENCE (blob);
+}
+
+void
+hb_blob_destroy (hb_blob_t *blob)
+{
+  HB_OBJECT_DO_DESTROY (blob);
+
+  _hb_blob_destroy_user_data (blob);
+
+  free (blob);
+}
+
+unsigned int
+hb_blob_get_length (hb_blob_t *blob)
+{
+  return blob->length;
+}
+
+const char *
+hb_blob_lock (hb_blob_t *blob)
+{
+  if (HB_OBJECT_IS_INERT (blob))
+    return NULL;
+
+  hb_mutex_lock (blob->lock);
+
+  (void) (HB_DEBUG_BLOB &&
+    fprintf (stderr, "%p %s (%d) -> %p\n", blob, __FUNCTION__,
+	     blob->lock_count, blob->data));
+
+  blob->lock_count++;
+
+  hb_mutex_unlock (blob->lock);
+
+  return blob->data;
+}
+
+void
+hb_blob_unlock (hb_blob_t *blob)
+{
+  if (HB_OBJECT_IS_INERT (blob))
+    return;
+
+  hb_mutex_lock (blob->lock);
+
+  (void) (HB_DEBUG_BLOB &&
+    fprintf (stderr, "%p %s (%d) -> %p\n", blob, __FUNCTION__,
+	     blob->lock_count, blob->data));
+
+  assert (blob->lock_count > 0);
+  blob->lock_count--;
+
+  hb_mutex_unlock (blob->lock);
+}
+
+hb_bool_t
+hb_blob_is_writable (hb_blob_t *blob)
+{
+  hb_memory_mode_t mode;
+
+  if (HB_OBJECT_IS_INERT (blob))
+    return FALSE;
+
+  hb_mutex_lock (blob->lock);
+
+  mode = blob->mode;
+
+  hb_mutex_unlock (blob->lock);
+
+  return mode == HB_MEMORY_MODE_WRITABLE;
+}
+
+
+static hb_bool_t
+_try_make_writable_inplace_unix_locked (hb_blob_t *blob)
+{
+#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MPROTECT)
+  uintptr_t pagesize = -1, mask, length;
+  const char *addr;
+
+#if defined(HAVE_SYSCONF) && defined(_SC_PAGE_SIZE)
+  pagesize = (uintptr_t) sysconf (_SC_PAGE_SIZE);
+#elif defined(HAVE_SYSCONF) && defined(_SC_PAGESIZE)
+  pagesize = (uintptr_t) sysconf (_SC_PAGESIZE);
+#elif defined(HAVE_GETPAGESIZE)
+  pagesize = (uintptr_t) getpagesize ();
+#endif
+
+  if ((uintptr_t) -1L == pagesize) {
+    (void) (HB_DEBUG_BLOB &&
+      fprintf (stderr, "%p %s: failed to get pagesize: %s\n", blob, __FUNCTION__, strerror (errno)));
+    return FALSE;
+  }
+  (void) (HB_DEBUG_BLOB &&
+    fprintf (stderr, "%p %s: pagesize is %lu\n", blob, __FUNCTION__, (unsigned long) pagesize));
+
+  mask = ~(pagesize-1);
+  addr = (const char *) (((uintptr_t) blob->data) & mask);
+  length = (const char *) (((uintptr_t) blob->data + blob->length + pagesize-1) & mask)  - addr;
+  (void) (HB_DEBUG_BLOB &&
+    fprintf (stderr, "%p %s: calling mprotect on [%p..%p] (%lu bytes)\n",
+	     blob, __FUNCTION__,
+	     addr, addr+length, (unsigned long) length));
+  if (-1 == mprotect ((void *) addr, length, PROT_READ | PROT_WRITE)) {
+    (void) (HB_DEBUG_BLOB &&
+      fprintf (stderr, "%p %s: %s\n", blob, __FUNCTION__, strerror (errno)));
+    return FALSE;
+  }
+
+  (void) (HB_DEBUG_BLOB &&
+    fprintf (stderr, "%p %s: successfully made [%p..%p] (%lu bytes) writable\n",
+	     blob, __FUNCTION__,
+	     addr, addr+length, (unsigned long) length));
+  return TRUE;
+#else
+  return FALSE;
+#endif
+}
+
+static void
+try_writable_inplace_locked (hb_blob_t *blob)
+{
+  (void) (HB_DEBUG_BLOB &&
+    fprintf (stderr, "%p %s: making writable\n", blob, __FUNCTION__));
+
+  if (_try_make_writable_inplace_unix_locked (blob)) {
+    (void) (HB_DEBUG_BLOB &&
+      fprintf (stderr, "%p %s: making writable -> succeeded\n", blob, __FUNCTION__));
+    blob->mode = HB_MEMORY_MODE_WRITABLE;
+  } else {
+    (void) (HB_DEBUG_BLOB &&
+      fprintf (stderr, "%p %s: making writable -> FAILED\n", blob, __FUNCTION__));
+    /* Failed to make writable inplace, mark that */
+    blob->mode = HB_MEMORY_MODE_READONLY;
+  }
+}
+
+hb_bool_t
+hb_blob_try_writable_inplace (hb_blob_t *blob)
+{
+  hb_memory_mode_t mode;
+
+  if (HB_OBJECT_IS_INERT (blob))
+    return FALSE;
+
+  hb_mutex_lock (blob->lock);
+
+  if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE)
+    try_writable_inplace_locked (blob);
+
+  mode = blob->mode;
+
+  hb_mutex_unlock (blob->lock);
+
+  return mode == HB_MEMORY_MODE_WRITABLE;
+}
+
+hb_bool_t
+hb_blob_try_writable (hb_blob_t *blob)
+{
+  hb_memory_mode_t mode;
+
+  if (HB_OBJECT_IS_INERT (blob))
+    return FALSE;
+
+  hb_mutex_lock (blob->lock);
+
+  if (blob->mode == HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE)
+    try_writable_inplace_locked (blob);
+
+  if (blob->mode == HB_MEMORY_MODE_READONLY)
+  {
+    char *new_data;
+
+    (void) (HB_DEBUG_BLOB &&
+      fprintf (stderr, "%p %s (%d) -> %p\n", blob, __FUNCTION__,
+	       blob->lock_count, blob->data));
+
+    if (blob->lock_count)
+      goto done;
+
+    new_data = (char *) malloc (blob->length);
+    if (new_data) {
+      (void) (HB_DEBUG_BLOB &&
+	fprintf (stderr, "%p %s: dupped successfully -> %p\n", blob, __FUNCTION__, blob->data));
+      memcpy (new_data, blob->data, blob->length);
+      _hb_blob_destroy_user_data (blob);
+      blob->mode = HB_MEMORY_MODE_WRITABLE;
+      blob->data = new_data;
+      blob->user_data = new_data;
+      blob->destroy = free;
+    }
+  }
+
+done:
+  mode = blob->mode;
+
+  hb_mutex_unlock (blob->lock);
+
+  return mode == HB_MEMORY_MODE_WRITABLE;
+}
+
+
+HB_END_DECLS
commit 04744e73bad22d679986173b5f0d84dbbf49dd57
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Apr 20 17:24:03 2011 -0400

    [TODO] Remove done items

diff --git a/TODO b/TODO
index dbcb809..b5eb3be 100644
--- a/TODO
+++ b/TODO
@@ -16,10 +16,6 @@ General fixes:
 API issues to fix before 1.0:
 ============================
 
-- Rename get_table to reference_table
-
-- get_table shouldn't return NULL
-
 - Figure out how many .so objects, how to link, etc
 
 - Shall y axis progress downward instead of upward?
@@ -28,8 +24,6 @@ API issues to fix before 1.0:
 
 - Real subclassing support for vfunc vectors
 
-- Investigate Mozilla's user-data issue, make sure it's addressed
-
 - Add hb-cairo glue
 
 - Fix blob, remove mutex, etc.
commit 9417c1c0d2b005eadf0c087ca695121a6200d0f7
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Apr 20 17:21:23 2011 -0400

    [API] Make hb_face_reference_table() return empty blob instead of NULL
    
    The idea here is that:
    
      - Like pretty much all other API in harfbuzz, user does not have to
        check for NULL.
    
      - In any caller code, the case of missing table should be handled
        exactly the same way that a too-short table is handled.  Turning
        a non-existent talbe into a table of size 0 makes the user code
        safer.

diff --git a/src/hb-font.cc b/src/hb-font.cc
index 2475089..96ff4f5 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -417,6 +417,8 @@ hb_face_reference_table (hb_face_t *face,
     return &_hb_blob_nil;
 
   blob = face->get_table (tag, face->user_data);
+  if (unlikely (!blob))
+    blob = hb_blob_create_empty();
 
   return blob;
 }
diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index 8f3001b..241c5a0 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -286,9 +286,6 @@ struct Sanitizer
 
     /* TODO is_sane() stuff */
 
-    if (!blob)
-      return hb_blob_create_empty ();
-
   retry:
     (void) (HB_DEBUG_SANITIZE &&
       fprintf (stderr, "Sanitizer %p start %s\n", blob, HB_FUNC));
commit c035812feb0d385a9e8c334631738e4915912c71
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Apr 20 17:03:00 2011 -0400

    [API] Rename hb_face_get_table() to hb_face_reference_table()
    
    That correctly reflects the reference ownership transfer happening.

diff --git a/src/hb-font.cc b/src/hb-font.cc
index 521120a..2475089 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -319,7 +319,7 @@ hb_face_create_for_tables (hb_get_table_func_t  get_table,
 
   face->ot_layout = _hb_ot_layout_new (face);
 
-  face->head_blob = Sanitizer<head>::sanitize (hb_face_get_table (face, HB_OT_TAG_head));
+  face->head_blob = Sanitizer<head>::sanitize (hb_face_reference_table (face, HB_OT_TAG_head));
   face->head_table = Sanitizer<head>::lock_instance (face->head_blob);
 
   return face;
@@ -408,7 +408,7 @@ hb_face_destroy (hb_face_t *face)
 }
 
 hb_blob_t *
-hb_face_get_table (hb_face_t *face,
+hb_face_reference_table (hb_face_t *face,
 		   hb_tag_t   tag)
 {
   hb_blob_t *blob;
diff --git a/src/hb-font.h b/src/hb-font.h
index 29cd5cb..397b586 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -67,13 +67,10 @@ hb_face_destroy (hb_face_t *face);
  *     table vs a zero-length table vs a very short table.  It only leads to implementations
  *     that check for non-NULL and assume that they've got a usable table going on...  This
  *     actually happened with Firefox.
- *
- *   - It has to be renamed to reference_table() since unlike any other _get_ API, a reference
- *     ownership transfer happens and the user is responsible to destroy the result.
  */
 hb_blob_t *
-hb_face_get_table (hb_face_t *face,
-		   hb_tag_t   tag);
+hb_face_reference_table (hb_face_t *face,
+			 hb_tag_t   tag);
 
 unsigned int
 hb_face_get_upem (hb_face_t *face);
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index f1287c5..7990fe9 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -47,13 +47,13 @@ _hb_ot_layout_new (hb_face_t *face)
   /* Remove this object altogether */
   hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
 
-  layout->gdef_blob = Sanitizer<GDEF>::sanitize (hb_face_get_table (face, HB_OT_TAG_GDEF));
+  layout->gdef_blob = Sanitizer<GDEF>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GDEF));
   layout->gdef = Sanitizer<GDEF>::lock_instance (layout->gdef_blob);
 
-  layout->gsub_blob = Sanitizer<GSUB>::sanitize (hb_face_get_table (face, HB_OT_TAG_GSUB));
+  layout->gsub_blob = Sanitizer<GSUB>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GSUB));
   layout->gsub = Sanitizer<GSUB>::lock_instance (layout->gsub_blob);
 
-  layout->gpos_blob = Sanitizer<GPOS>::sanitize (hb_face_get_table (face, HB_OT_TAG_GPOS));
+  layout->gpos_blob = Sanitizer<GPOS>::sanitize (hb_face_reference_table (face, HB_OT_TAG_GPOS));
   layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_blob);
 
   return layout;
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index 2b536fa..9d5d418 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -48,7 +48,7 @@ hb_shape_internal (hb_font_t          *font,
 {
 #if 0 && defined(HAVE_GRAPHITE)
   hb_blob_t *silf_blob;
-  silf_blob = hb_face_get_table (face, HB_GRAPHITE_TAG_Silf);
+  silf_blob = hb_face_reference_table (face, HB_GRAPHITE_TAG_Silf);
   if (hb_blob_get_length(silf_blob))
   {
     hb_graphite_shape(font, face, buffer, features, num_features);
commit 2d7b61a4b0ed212ca414b3281c2eae3e3db19c13
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Apr 20 16:40:56 2011 -0400

    [TODO] Remove done items

diff --git a/TODO b/TODO
index a9ef442..dbcb809 100644
--- a/TODO
+++ b/TODO
@@ -16,16 +16,10 @@ General fixes:
 API issues to fix before 1.0:
 ============================
 
-- Rename / remove hb_buffer_add_glyph()?
-
-- Sprinkle const all over public and private API
-
 - Rename get_table to reference_table
 
 - get_table shouldn't return NULL
 
-- Use tags for hb_script_t
-
 - Figure out how many .so objects, how to link, etc
 
 - Shall y axis progress downward instead of upward?
@@ -36,14 +30,14 @@ API issues to fix before 1.0:
 
 - Investigate Mozilla's user-data issue, make sure it's addressed
 
-- Add hb-glib / hb-icu two-way script conversion API
-
 - Add hb-cairo glue
 
 - Fix blob, remove mutex, etc.
 
 - Add sanitize API (since may affect blob API)
 
+- Add glib GBoxedType stuff
+
 
 API to add (maybe after 1.0):
 ============================
diff --git a/src/hb-ft.h b/src/hb-ft.h
index be5c854..5671a00 100644
--- a/src/hb-ft.h
+++ b/src/hb-ft.h
@@ -36,6 +36,7 @@
 
 HB_BEGIN_DECLS
 
+/* Note: FreeType is not thread-safe.  Hence, these functions are not either. */
 
 hb_font_funcs_t *
 hb_ft_get_font_funcs (void);
@@ -44,7 +45,6 @@ hb_face_t *
 hb_ft_face_create (FT_Face           ft_face,
 		   hb_destroy_func_t destroy);
 
-/* Note: This function is not thread-safe */
 hb_face_t *
 hb_ft_face_create_cached (FT_Face ft_face);
 
commit af02933739e03a156b9f7761fd7a63e2a02d0df1
Author: Behdad Esfahbod <behdad at behdad.org>
Date:   Wed Apr 20 15:49:31 2011 -0400

    [API] Remove hb_*_get_reference_count()
    
    This was a bizzare piece of API that I inherited from cairo.  It has
    been wrong adding them to cairo in the first place.  Remove them before
    someone uses them!

diff --git a/src/hb-blob.c b/src/hb-blob.c
index 97ffd99..5419078 100644
--- a/src/hb-blob.c
+++ b/src/hb-blob.c
@@ -152,12 +152,6 @@ hb_blob_reference (hb_blob_t *blob)
   HB_OBJECT_DO_REFERENCE (blob);
 }
 
-unsigned int
-hb_blob_get_reference_count (hb_blob_t *blob)
-{
-  HB_OBJECT_DO_GET_REFERENCE_COUNT (blob);
-}
-
 void
 hb_blob_destroy (hb_blob_t *blob)
 {
diff --git a/src/hb-blob.h b/src/hb-blob.h
index b6080f1..4097ccc 100644
--- a/src/hb-blob.h
+++ b/src/hb-blob.h
@@ -59,9 +59,6 @@ hb_blob_create_empty (void);
 hb_blob_t *
 hb_blob_reference (hb_blob_t *blob);
 
-unsigned int
-hb_blob_get_reference_count (hb_blob_t *blob);
-
 void
 hb_blob_destroy (hb_blob_t *blob);
 
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index 2f7a173..5d3b532 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -154,12 +154,6 @@ hb_buffer_reference (hb_buffer_t *buffer)
   HB_OBJECT_DO_REFERENCE (buffer);
 }
 
-unsigned int
-hb_buffer_get_reference_count (hb_buffer_t *buffer)
-{
-  HB_OBJECT_DO_GET_REFERENCE_COUNT (buffer);
-}
-
 void
 hb_buffer_destroy (hb_buffer_t *buffer)
 {
diff --git a/src/hb-buffer.h b/src/hb-buffer.h
index d12faf8..b80ce9f 100644
--- a/src/hb-buffer.h
+++ b/src/hb-buffer.h
@@ -65,9 +65,6 @@ hb_buffer_create (unsigned int pre_alloc_size);
 hb_buffer_t *
 hb_buffer_reference (hb_buffer_t *buffer);
 
-unsigned int
-hb_buffer_get_reference_count (hb_buffer_t *buffer);
-
 void
 hb_buffer_destroy (hb_buffer_t *buffer);
 
diff --git a/src/hb-font.cc b/src/hb-font.cc
index 43112c1..521120a 100644
--- a/src/hb-font.cc
+++ b/src/hb-font.cc
@@ -115,12 +115,6 @@ hb_font_funcs_reference (hb_font_funcs_t *ffuncs)
   HB_OBJECT_DO_REFERENCE (ffuncs);
 }
 
-unsigned int
-hb_font_funcs_get_reference_count (hb_font_funcs_t *ffuncs)
-{
-  HB_OBJECT_DO_GET_REFERENCE_COUNT (ffuncs);
-}
-
 void
 hb_font_funcs_destroy (hb_font_funcs_t *ffuncs)
 {
@@ -397,12 +391,6 @@ hb_face_reference (hb_face_t *face)
   HB_OBJECT_DO_REFERENCE (face);
 }
 
-unsigned int
-hb_face_get_reference_count (hb_face_t *face)
-{
-  HB_OBJECT_DO_GET_REFERENCE_COUNT (face);
-}
-
 void
 hb_face_destroy (hb_face_t *face)
 {
@@ -477,12 +465,6 @@ hb_font_reference (hb_font_t *font)
   HB_OBJECT_DO_REFERENCE (font);
 }
 
-unsigned int
-hb_font_get_reference_count (hb_font_t *font)
-{
-  HB_OBJECT_DO_GET_REFERENCE_COUNT (font);
-}
-
 void
 hb_font_destroy (hb_font_t *font)
 {
diff --git a/src/hb-font.h b/src/hb-font.h
index 4b9540b..29cd5cb 100644
--- a/src/hb-font.h
+++ b/src/hb-font.h
@@ -55,9 +55,6 @@ hb_face_create_for_tables (hb_get_table_func_t  get_table,
 hb_face_t *
 hb_face_reference (hb_face_t *face);
 
-unsigned int
-hb_face_get_reference_count (hb_face_t *face);
-
 void
 hb_face_destroy (hb_face_t *face);
 
@@ -94,9 +91,6 @@ hb_font_funcs_create (void);
 hb_font_funcs_t *
 hb_font_funcs_reference (hb_font_funcs_t *ffuncs);
 
-unsigned int
-hb_font_funcs_get_reference_count (hb_font_funcs_t *ffuncs);
-
 void
 hb_font_funcs_destroy (hb_font_funcs_t *ffuncs);
 
@@ -209,9 +203,6 @@ hb_font_create (void);
 hb_font_t *
 hb_font_reference (hb_font_t *font);
 
-unsigned int
-hb_font_get_reference_count (hb_font_t *font);
-
 void
 hb_font_destroy (hb_font_t *font);
 
diff --git a/src/hb-object-private.h b/src/hb-object-private.h
index a5b5355..c1787c5 100644
--- a/src/hb-object-private.h
+++ b/src/hb-object-private.h
@@ -116,13 +116,6 @@ _hb_trace_object (const void *obj,
     return obj; \
   } HB_STMT_END
 
-#define HB_OBJECT_DO_GET_REFERENCE_COUNT(obj) \
-  HB_STMT_START { \
-    if (unlikely (!(obj) || HB_OBJECT_IS_INERT (obj))) \
-      return 0; \
-    return HB_REFERENCE_COUNT_GET_VALUE (obj->ref_count); \
-  } HB_STMT_END
-
 #define HB_OBJECT_DO_DESTROY(obj) \
   HB_STMT_START { \
     int old_count; \
diff --git a/src/hb-unicode.cc b/src/hb-unicode.cc
index ddad884..9295be7 100644
--- a/src/hb-unicode.cc
+++ b/src/hb-unicode.cc
@@ -128,12 +128,6 @@ hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs)
   HB_OBJECT_DO_REFERENCE (ufuncs);
 }
 
-unsigned int
-hb_unicode_funcs_get_reference_count (hb_unicode_funcs_t *ufuncs)
-{
-  HB_OBJECT_DO_GET_REFERENCE_COUNT (ufuncs);
-}
-
 void
 hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs)
 {
diff --git a/src/hb-unicode.h b/src/hb-unicode.h
index 0b94aa3..0fcef06 100644
--- a/src/hb-unicode.h
+++ b/src/hb-unicode.h
@@ -46,9 +46,6 @@ hb_unicode_funcs_create (hb_unicode_funcs_t *parent_funcs);
 hb_unicode_funcs_t *
 hb_unicode_funcs_reference (hb_unicode_funcs_t *ufuncs);
 
-unsigned int
-hb_unicode_funcs_get_reference_count (hb_unicode_funcs_t *ufuncs);
-
 void
 hb_unicode_funcs_destroy (hb_unicode_funcs_t *ufuncs);
 
diff --git a/test/test-unicode.c b/test/test-unicode.c
index 923c250..b7a5702 100644
--- a/test/test-unicode.c
+++ b/test/test-unicode.c
@@ -35,7 +35,6 @@ test_nil (void)
 
   g_assert_cmpint (hb_unicode_get_script (uf, 'd'), ==, HB_SCRIPT_UNKNOWN);
 
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
   hb_unicode_funcs_destroy (uf);
 }
 
@@ -45,10 +44,6 @@ test_glib (void)
   hb_unicode_funcs_t *uf = hb_glib_get_unicode_funcs ();
 
   g_assert_cmpint (hb_unicode_get_script (uf, 'd'), ==, HB_SCRIPT_LATIN);
-
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 0);
-  hb_unicode_funcs_destroy (uf);
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 0);
 }
 
 static gboolean freed0, freed1;
@@ -92,7 +87,6 @@ test_custom (void)
   g_assert_cmpint (hb_unicode_get_script (uf, 'a'), ==, HB_SCRIPT_LATIN);
   g_assert_cmpint (hb_unicode_get_script (uf, '0'), ==, HB_SCRIPT_UNKNOWN);
 
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
   g_assert (!freed0 && !freed1);
   hb_unicode_funcs_destroy (uf);
   g_assert (freed0 && !freed1);
@@ -123,13 +117,10 @@ test_subclassing_nil (void)
   hb_unicode_funcs_t *uf, *aa;
 
   uf = hb_unicode_funcs_create (NULL);
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
 
   aa = hb_unicode_funcs_create (uf);
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 2);
 
   hb_unicode_funcs_destroy (uf);
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
 
   hb_unicode_funcs_set_script_func (aa, a_is_for_arabic_get_script,
                                     unique_pointer1, free_up);
@@ -137,8 +128,6 @@ test_subclassing_nil (void)
   g_assert_cmpint (hb_unicode_get_script (aa, 'a'), ==, HB_SCRIPT_ARABIC);
   g_assert_cmpint (hb_unicode_get_script (aa, 'b'), ==, HB_SCRIPT_UNKNOWN);
 
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (aa), ==, 1);
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
   g_assert (!freed0 && !freed1);
   hb_unicode_funcs_destroy (aa);
   g_assert (!freed0 && freed1);
@@ -151,13 +140,7 @@ test_subclassing_glib (void)
   hb_unicode_funcs_t *uf, *aa;
 
   uf = hb_glib_get_unicode_funcs ();
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 0);
-
   aa = hb_unicode_funcs_create (uf);
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 0);
-
-  hb_unicode_funcs_destroy (uf);
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 0);
 
   hb_unicode_funcs_set_script_func (aa, a_is_for_arabic_get_script,
                                     unique_pointer1, free_up);
@@ -165,7 +148,6 @@ test_subclassing_glib (void)
   g_assert_cmpint (hb_unicode_get_script (aa, 'a'), ==, HB_SCRIPT_ARABIC);
   g_assert_cmpint (hb_unicode_get_script (aa, 'b'), ==, HB_SCRIPT_LATIN);
 
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (aa), ==, 1);
   g_assert (!freed0 && !freed1);
   hb_unicode_funcs_destroy (aa);
   g_assert (!freed0 && freed1);
@@ -178,17 +160,13 @@ test_subclassing_deep (void)
   hb_unicode_funcs_t *uf, *aa;
 
   uf = hb_unicode_funcs_create (NULL);
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
 
   hb_unicode_funcs_set_script_func (uf, simple_get_script,
                                     unique_pointer0, free_up);
 
   aa = hb_unicode_funcs_create (uf);
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (aa), ==, 1);
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 2);
 
   hb_unicode_funcs_destroy (uf);
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
 
   /* make sure the 'uf' didn't get freed, since 'aa' holds a ref */
   g_assert (!freed0);
@@ -200,8 +178,6 @@ test_subclassing_deep (void)
   g_assert_cmpint (hb_unicode_get_script (aa, 'b'), ==, HB_SCRIPT_LATIN);
   g_assert_cmpint (hb_unicode_get_script (aa, '0'), ==, HB_SCRIPT_UNKNOWN);
 
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (aa), ==, 1);
-  g_assert_cmpint (hb_unicode_funcs_get_reference_count (uf), ==, 1);
   g_assert (!freed0 && !freed1);
   hb_unicode_funcs_destroy (aa);
   g_assert (freed0 && freed1);



More information about the HarfBuzz mailing list