bug 941470 - update harfbuzz to latest upstream commit (205bf834...), to pick up plan-caching for user features and misc fixes. r=jdaggett
authorJonathan Kew <jkew@mozilla.com>
Wed, 04 Dec 2013 07:07:44 +0000
changeset 158777 dda95b5cb52869b40812cd22b0dc225e821da662
parent 158776 8b51998bca5bca1e5b413ae27407562cd9e583ac
child 158778 ba41e330fc7a6cbc74278e0b9c7ab53caab7be9a
push id25761
push userryanvm@gmail.com
push dateWed, 04 Dec 2013 22:38:46 +0000
treeherdermozilla-central@1426ffa9caf2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett
bugs941470
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
bug 941470 - update harfbuzz to latest upstream commit (205bf834...), to pick up plan-caching for user features and misc fixes. r=jdaggett
gfx/harfbuzz/src/hb-atomic-private.hh
gfx/harfbuzz/src/hb-buffer-private.hh
gfx/harfbuzz/src/hb-buffer.cc
gfx/harfbuzz/src/hb-coretext.cc
gfx/harfbuzz/src/hb-fallback-shape.cc
gfx/harfbuzz/src/hb-font-private.hh
gfx/harfbuzz/src/hb-ft.cc
gfx/harfbuzz/src/hb-graphite2.cc
gfx/harfbuzz/src/hb-ot-layout-common-private.hh
gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.hh
gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.rl
gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
gfx/harfbuzz/src/hb-ot-shape-private.hh
gfx/harfbuzz/src/hb-ot-tag.cc
gfx/harfbuzz/src/hb-private.hh
gfx/harfbuzz/src/hb-shape-plan-private.hh
gfx/harfbuzz/src/hb-shape-plan.cc
gfx/harfbuzz/src/hb-uniscribe.cc
gfx/harfbuzz/src/hb-version.h
--- a/gfx/harfbuzz/src/hb-atomic-private.hh
+++ b/gfx/harfbuzz/src/hb-atomic-private.hh
@@ -73,17 +73,17 @@ typedef LONG hb_atomic_int_t;
 
 typedef int32_t hb_atomic_int_t;
 #define hb_atomic_int_add(AI, V)	(OSAtomicAdd32Barrier ((V), &(AI)) - (V))
 
 #define hb_atomic_ptr_get(P)		(OSMemoryBarrier (), (void *) *(P))
 #if (MAC_OS_X_VERSION_MIN_REQUIRED > MAC_OS_X_VERSION_10_4 || __IPHONE_VERSION_MIN_REQUIRED >= 20100)
 #define hb_atomic_ptr_cmpexch(P,O,N)	OSAtomicCompareAndSwapPtrBarrier ((void *) (O), (void *) (N), (void **) (P))
 #else
-#if __ppc64__ || __x86_64__
+#if __ppc64__ || __x86_64__ || __arm64__
 #define hb_atomic_ptr_cmpexch(P,O,N)    OSAtomicCompareAndSwap64Barrier ((int64_t) (O), (int64_t) (N), (int64_t*) (P))
 #else
 #define hb_atomic_ptr_cmpexch(P,O,N)    OSAtomicCompareAndSwap32Barrier ((int32_t) (O), (int32_t) (N), (int32_t*) (P))
 #endif
 #endif
 
 
 #elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -176,22 +176,23 @@ struct hb_buffer_t {
 				   unsigned int end);
   HB_INTERNAL void merge_out_clusters (unsigned int start,
 				       unsigned int end);
 
   /* Internal methods */
   HB_INTERNAL bool enlarge (unsigned int size);
 
   inline bool ensure (unsigned int size)
-  { return likely (size < allocated) ? true : enlarge (size); }
+  { return likely (!size || size < allocated) ? true : enlarge (size); }
 
   HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out);
   HB_INTERNAL bool shift_forward (unsigned int count);
 
-  HB_INTERNAL void *get_scratch_buffer (unsigned int *size);
+  typedef long scratch_buffer_t;
+  HB_INTERNAL scratch_buffer_t *get_scratch_buffer (unsigned int *size);
 
   inline void clear_context (unsigned int side) { context_len[side] = 0; }
 };
 
 
 #define HB_BUFFER_XALLOCATE_VAR(b, func, var, owner) \
   b->func (offsetof (hb_glyph_info_t, var) - offsetof(hb_glyph_info_t, var1), \
 	   sizeof (b->info[0].var), owner)
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -147,27 +147,28 @@ hb_buffer_t::shift_forward (unsigned int
 
   memmove (info + idx + count, info + idx, (len - idx) * sizeof (info[0]));
   len += count;
   idx += count;
 
   return true;
 }
 
-void *
+hb_buffer_t::scratch_buffer_t *
 hb_buffer_t::get_scratch_buffer (unsigned int *size)
 {
   have_output = false;
   have_positions = false;
 
   out_len = 0;
   out_info = info;
 
-  *size = allocated * sizeof (pos[0]);
-  return pos;
+  assert ((uintptr_t) pos % sizeof (scratch_buffer_t) == 0);
+  *size = allocated * sizeof (pos[0]) / sizeof (scratch_buffer_t);
+  return (scratch_buffer_t *) (void *) pos;
 }
 
 
 
 /* HarfBuzz-Internal API */
 
 void
 hb_buffer_t::reset (void)
@@ -1144,17 +1145,20 @@ hb_buffer_set_length (hb_buffer_t  *buff
     memset (buffer->info + buffer->len, 0, sizeof (buffer->info[0]) * (length - buffer->len));
     if (buffer->have_positions)
       memset (buffer->pos + buffer->len, 0, sizeof (buffer->pos[0]) * (length - buffer->len));
   }
 
   buffer->len = length;
 
   if (!length)
+  {
+    buffer->content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
     buffer->clear_context (0);
+  }
   buffer->clear_context (1);
 
   return true;
 }
 
 /**
  * hb_buffer_get_length:
  * @buffer: a buffer.
--- a/gfx/harfbuzz/src/hb-coretext.cc
+++ b/gfx/harfbuzz/src/hb-coretext.cc
@@ -389,16 +389,17 @@ static int
 hb_bool_t
 _hb_coretext_shape (hb_shape_plan_t    *shape_plan,
 		    hb_font_t          *font,
                     hb_buffer_t        *buffer,
                     const hb_feature_t *features,
                     unsigned int        num_features)
 {
   hb_face_t *face = font->face;
+  hb_coretext_shaper_face_data_t *face_data = HB_SHAPER_DATA_GET (face);
   hb_coretext_shaper_font_data_t *font_data = HB_SHAPER_DATA_GET (font);
 
   /*
    * Set up features.
    * (copied + modified from code from hb-uniscribe.cc)
    */
   hb_auto_array_t<feature_record_t> feature_records;
   hb_auto_array_t<range_record_t> range_records;
@@ -548,21 +549,31 @@ hb_bool_t
 
 #define FAIL(...) \
   HB_STMT_START { \
     DEBUG_MSG (CORETEXT, NULL, __VA_ARGS__); \
     return false; \
   } HB_STMT_END;
 
   unsigned int scratch_size;
-  char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size);
+  hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
+
+#define ALLOCATE_ARRAY(Type, name, len) \
+  Type *name = (Type *) scratch; \
+  { \
+    unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
+    assert (_consumed <= scratch_size); \
+    scratch += _consumed; \
+    scratch_size -= _consumed; \
+  }
 
 #define utf16_index() var1.u32
 
-  UniChar *pchars = (UniChar *) scratch;
+  ALLOCATE_ARRAY (UniChar, pchars, buffer->len * 2);
+
   unsigned int chars_len = 0;
   for (unsigned int i = 0; i < buffer->len; i++) {
     hb_codepoint_t c = buffer->info[i].codepoint;
     buffer->info[i].utf16_index() = chars_len;
     if (likely (c < 0x10000))
       pchars[chars_len++] = c;
     else if (unlikely (c >= 0x110000))
       pchars[chars_len++] = 0xFFFD;
@@ -581,17 +592,17 @@ hb_bool_t
   CFMutableAttributedStringRef attr_string = CFAttributedStringCreateMutable (NULL, chars_len);
   CFAttributedStringReplaceString (attr_string, CFRangeMake (0, 0), string_ref);
   CFRelease (string_ref);
   CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
 				  kCTFontAttributeName, font_data->ct_font);
 
   if (num_features)
   {
-    unsigned int *log_clusters = (unsigned int *) (pchars + chars_len);
+    ALLOCATE_ARRAY (unsigned int, log_clusters, chars_len);
 
     /* Need log_clusters to assign features. */
     chars_len = 0;
     for (unsigned int i = 0; i < buffer->len; i++)
     {
       hb_codepoint_t c = buffer->info[i].codepoint;
       unsigned int cluster = buffer->info[i].cluster;
       log_clusters[chars_len++] = cluster;
@@ -633,36 +644,70 @@ hb_bool_t
 
   CFArrayRef glyph_runs = CTLineGetGlyphRuns (line);
   unsigned int num_runs = CFArrayGetCount (glyph_runs);
 
   buffer->len = 0;
 
   const CFRange range_all = CFRangeMake (0, 0);
 
-  for (unsigned int i = 0; i < num_runs; i++) {
+  for (unsigned int i = 0; i < num_runs; i++)
+  {
     CTRunRef run = (CTRunRef) CFArrayGetValueAtIndex (glyph_runs, i);
 
+    /* CoreText does automatic font fallback (AKA "cascading") for  characters
+     * not supported by the requested font, and provides no way to turn it off,
+     * so we detect if the returned run uses a font other than the requested
+     * one and fill in the buffer with .notdef glyphs instead of random glyph
+     * indices from a different font.
+     */
+    CFDictionaryRef attributes = CTRunGetAttributes (run);
+    CTFontRef run_ct_font = static_cast<CTFontRef>(CFDictionaryGetValue (attributes, kCTFontAttributeName));
+    CGFontRef run_cg_font = CTFontCopyGraphicsFont (run_ct_font, 0);
+    if (!CFEqual (run_cg_font, face_data->cg_font))
+    {
+        CFRelease (run_cg_font);
+
+	CFRange range = CTRunGetStringRange (run);
+	buffer->ensure (buffer->len + range.length);
+	if (buffer->in_error)
+	  FAIL ("Buffer resize failed");
+	hb_glyph_info_t *info = buffer->info + buffer->len;
+	buffer->len += range.length;
+
+        for (CFIndex j = 0; j < range.length; j++)
+	{
+            CGGlyph notdef = 0;
+            double advance = CTFontGetAdvancesForGlyphs (font_data->ct_font, kCTFontHorizontalOrientation, &notdef, NULL, 1);
+
+            info->codepoint = notdef;
+	    /* TODO We have to fixup clusters later.  See vis_clusters in
+	     * hb-uniscribe.cc for example. */
+            info->cluster = range.location + j;
+
+            info->mask = advance;
+            info->var1.u32 = 0;
+            info->var2.u32 = 0;
+
+	    info++;
+        }
+        continue;
+    }
+    CFRelease (run_cg_font);
+
     unsigned int num_glyphs = CTRunGetGlyphCount (run);
     if (num_glyphs == 0)
       continue;
 
     buffer->ensure (buffer->len + num_glyphs);
 
-    /* Testing indicates that CTRunGetGlyphsPtr (almost?) always succeeds,
-     * and so copying data to our own buffer with CTRunGetGlyphs will be
-     * extremely rare. */
+    scratch = buffer->get_scratch_buffer (&scratch_size);
 
-    unsigned int scratch_size;
-    char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size);
-
-#define ALLOCATE_ARRAY(Type, name, len) \
-  Type *name = (Type *) scratch; \
-  scratch += (len) * sizeof ((name)[0]); \
-  scratch_size -= (len) * sizeof ((name)[0]);
+    /* Testing indicates that CTRunGetGlyphsPtr, etc (almost?) always
+     * succeed, and so copying data to our own buffer will be rare. */
 
     const CGGlyph* glyphs = CTRunGetGlyphsPtr (run);
     if (!glyphs) {
       ALLOCATE_ARRAY (CGGlyph, glyph_buf, num_glyphs);
       CTRunGetGlyphs (run, range_all, glyph_buf);
       glyphs = glyph_buf;
     }
 
--- a/gfx/harfbuzz/src/hb-fallback-shape.cc
+++ b/gfx/harfbuzz/src/hb-fallback-shape.cc
@@ -90,16 +90,26 @@ void
 
 hb_bool_t
 _hb_fallback_shape (hb_shape_plan_t    *shape_plan HB_UNUSED,
 		    hb_font_t          *font,
 		    hb_buffer_t        *buffer,
 		    const hb_feature_t *features HB_UNUSED,
 		    unsigned int        num_features HB_UNUSED)
 {
+  /* TODO
+   *
+   * - Apply fallback kern.
+   * - Handle Variation Selectors?
+   * - Apply normalization?
+   *
+   * This will make the fallback shaper into a dumb "TrueType"
+   * shaper which many people unfortunately still request.
+   */
+
   hb_codepoint_t space;
   font->get_glyph (' ', 0, &space);
 
   buffer->clear_positions ();
 
   unsigned int count = buffer->len;
 
   for (unsigned int i = 0; i < count; i++)
--- a/gfx/harfbuzz/src/hb-font-private.hh
+++ b/gfx/harfbuzz/src/hb-font-private.hh
@@ -113,22 +113,22 @@ struct hb_font_t {
 
   /* Convert from font-space to user-space */
   inline hb_position_t em_scale_x (int16_t v) { return em_scale (v, this->x_scale); }
   inline hb_position_t em_scale_y (int16_t v) { return em_scale (v, this->y_scale); }
 
   /* Convert from parent-font user-space to our user-space */
   inline hb_position_t parent_scale_x_distance (hb_position_t v) {
     if (unlikely (parent && parent->x_scale != x_scale))
-      return v * (int64_t) this->x_scale / this->parent->x_scale;
+      return (hb_position_t) (v * (int64_t) this->x_scale / this->parent->x_scale);
     return v;
   }
   inline hb_position_t parent_scale_y_distance (hb_position_t v) {
     if (unlikely (parent && parent->y_scale != y_scale))
-      return v * (int64_t) this->y_scale / this->parent->y_scale;
+      return (hb_position_t) (v * (int64_t) this->y_scale / this->parent->y_scale);
     return v;
   }
   inline hb_position_t parent_scale_x_position (hb_position_t v) {
     return parent_scale_x_distance (v);
   }
   inline hb_position_t parent_scale_y_position (hb_position_t v) {
     return parent_scale_y_distance (v);
   }
@@ -392,17 +392,17 @@ struct hb_font_t {
 	  get_glyph (unichar, 0, glyph))
 	return true;
     }
 
     return false;
   }
 
   private:
-  inline hb_position_t em_scale (int16_t v, int scale) { return v * (int64_t) scale / face->get_upem (); }
+  inline hb_position_t em_scale (int16_t v, int scale) { return (hb_position_t) (v * (int64_t) scale / face->get_upem ()); }
 };
 
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, font);
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
 #undef HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS
 
--- a/gfx/harfbuzz/src/hb-ft.cc
+++ b/gfx/harfbuzz/src/hb-ft.cc
@@ -413,18 +413,18 @@ hb_ft_font_create (FT_Face           ft_
 
   face = hb_ft_face_create (ft_face, destroy);
   font = hb_font_create (face);
   hb_face_destroy (face);
   hb_font_set_funcs (font,
 		     _hb_ft_get_font_funcs (),
 		     ft_face, (hb_destroy_func_t) _do_nothing);
   hb_font_set_scale (font,
-		     ((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1<<15)) >> 16,
-		     ((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1<<15)) >> 16);
+		     (int) (((uint64_t) ft_face->size->metrics.x_scale * (uint64_t) ft_face->units_per_EM + (1<<15)) >> 16),
+		     (int) (((uint64_t) ft_face->size->metrics.y_scale * (uint64_t) ft_face->units_per_EM + (1<<15)) >> 16));
   hb_font_set_ppem (font,
 		    ft_face->size->metrics.x_ppem,
 		    ft_face->size->metrics.y_ppem);
 
   return font;
 }
 
 
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -238,24 +238,19 @@ hb_bool_t
   }
 
   gr_segment *seg = NULL;
   const gr_slot *is;
   unsigned int ci = 0, ic = 0;
   float curradvx = 0., curradvy = 0.;
 
   unsigned int scratch_size;
-  char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size);
+  hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
 
-#define ALLOCATE_ARRAY(Type, name, len) \
-  Type *name = (Type *) scratch; \
-  scratch += (len) * sizeof ((name)[0]); \
-  scratch_size -= (len) * sizeof ((name)[0]);
-
-  ALLOCATE_ARRAY (uint32_t, chars, buffer->len);
+  uint32_t *chars = (uint32_t *) scratch;
 
   for (unsigned int i = 0; i < buffer->len; ++i)
     chars[i] = buffer->info[i].codepoint;
 
   hb_tag_t script_tag[2];
   hb_ot_tags_from_script (hb_buffer_get_script (buffer), &script_tag[0], &script_tag[1]);
 
   seg = gr_make_seg (grfont, grface,
@@ -271,32 +266,43 @@ hb_bool_t
 
   unsigned int glyph_count = gr_seg_n_slots (seg);
   if (unlikely (!glyph_count)) {
     if (feats) gr_featureval_destroy (feats);
     gr_seg_destroy (seg);
     return false;
   }
 
-  scratch = (char *) buffer->get_scratch_buffer (&scratch_size);
-  while ((sizeof (hb_graphite2_cluster_t) * buffer->len +
-	  sizeof (hb_codepoint_t) * glyph_count) > scratch_size)
+  scratch = buffer->get_scratch_buffer (&scratch_size);
+  while ((DIV_CEIL (sizeof (hb_graphite2_cluster_t) * buffer->len, sizeof (*scratch)) +
+	  DIV_CEIL (sizeof (hb_codepoint_t) * glyph_count, sizeof (*scratch))) > scratch_size)
   {
     buffer->ensure (buffer->allocated * 2);
     if (unlikely (buffer->in_error)) {
       if (feats) gr_featureval_destroy (feats);
       gr_seg_destroy (seg);
       return false;
     }
-    scratch = (char *) buffer->get_scratch_buffer (&scratch_size);
+    scratch = buffer->get_scratch_buffer (&scratch_size);
+  }
+
+#define ALLOCATE_ARRAY(Type, name, len) \
+  Type *name = (Type *) scratch; \
+  { \
+    unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
+    assert (_consumed <= scratch_size); \
+    scratch += _consumed; \
+    scratch_size -= _consumed; \
   }
 
   ALLOCATE_ARRAY (hb_graphite2_cluster_t, clusters, buffer->len);
   ALLOCATE_ARRAY (hb_codepoint_t, gids, glyph_count);
 
+#undef ALLOCATE_ARRAY
+
   memset (clusters, 0, sizeof (clusters[0]) * buffer->len);
 
   hb_codepoint_t *pg = gids;
   for (is = gr_seg_first_slot (seg), ic = 0; is; is = gr_slot_next_in_segment (is), ic++)
   {
     unsigned int before = gr_slot_before (is);
     unsigned int after = gr_slot_after (is);
     *pg = gr_slot_gid (is);
--- a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -372,17 +372,17 @@ struct FeatureParamsStylisticSet
 {
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE (this);
     /* Right now minorVersion is at zero.  Which means, any table supports
      * the uiNameID field. */
     return TRACE_RETURN (c->check_struct (this));
   }
 
-  USHORT	minorVersion;	/* (set to 0): This corresponds to a “minor”
+  USHORT	version;	/* (set to 0): This corresponds to a “minor”
 				 * version number. Additional data may be
 				 * added to the end of this Feature Parameters
 				 * table in the future. */
 
   USHORT	uiNameID;	/* The 'name' table name ID that specifies a
 				 * string (or strings, for multiple languages)
 				 * for a user-interface label for this
 				 * feature.  The values of uiLabelNameId and
@@ -395,16 +395,17 @@ struct FeatureParamsStylisticSet
 				 * English string should be included as a
 				 * fallback. The string should be kept to a
 				 * minimal length to fit comfortably with
 				 * different application interfaces. */
   public:
   DEFINE_SIZE_STATIC (4);
 };
 
+/* http://www.microsoft.com/typography/otspec/features_ae.htm#cv01-cv99 */
 struct FeatureParamsCharacterVariants
 {
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE (this);
     return TRACE_RETURN (c->check_struct (this) &&
 			 characters.sanitize (c));
   }
 
@@ -1107,17 +1108,17 @@ struct Device
   inline int get_delta (unsigned int ppem, int scale) const
   {
     if (!ppem) return 0;
 
     int pixels = get_delta_pixels (ppem);
 
     if (!pixels) return 0;
 
-    return pixels * (int64_t) scale / ppem;
+    return (int) (pixels * (int64_t) scale / ppem);
   }
 
 
   inline int get_delta_pixels (unsigned int ppem_size) const
   {
     unsigned int f = deltaFormat;
     if (unlikely (f < 1 || f > 3))
       return 0;
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.hh
@@ -29,247 +29,248 @@
 #ifndef HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
 #define HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
 
 #include "hb-private.hh"
 
 
 #line 36 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
 static const unsigned char _myanmar_syllable_machine_trans_keys[] = {
-	1u, 30u, 3u, 30u, 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 
+	1u, 31u, 3u, 30u, 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 
 	3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 1u, 16u, 3u, 29u, 3u, 29u, 3u, 29u, 
 	3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 5u, 29u, 5u, 8u, 
 	5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 
 	3u, 30u, 3u, 29u, 1u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 
-	3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 0
+	3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 8u, 8u, 0
 };
 
 static const char _myanmar_syllable_machine_key_spans[] = {
-	30, 28, 25, 4, 25, 23, 21, 21, 
+	31, 28, 25, 4, 25, 23, 21, 21, 
 	27, 27, 27, 27, 16, 27, 27, 27, 
 	27, 27, 27, 27, 27, 27, 25, 4, 
 	25, 23, 21, 21, 27, 27, 27, 27, 
 	28, 27, 30, 27, 27, 27, 27, 27, 
-	27, 27, 27, 27
+	27, 27, 27, 27, 1
 };
 
 static const short _myanmar_syllable_machine_index_offsets[] = {
-	0, 31, 60, 86, 91, 117, 141, 163, 
-	185, 213, 241, 269, 297, 314, 342, 370, 
-	398, 426, 454, 482, 510, 538, 566, 592, 
-	597, 623, 647, 669, 691, 719, 747, 775, 
-	803, 832, 860, 891, 919, 947, 975, 1003, 
-	1031, 1059, 1087, 1115
+	0, 32, 61, 87, 92, 118, 142, 164, 
+	186, 214, 242, 270, 298, 315, 343, 371, 
+	399, 427, 455, 483, 511, 539, 567, 593, 
+	598, 624, 648, 670, 692, 720, 748, 776, 
+	804, 833, 861, 892, 920, 948, 976, 1004, 
+	1032, 1060, 1088, 1116, 1144
 };
 
 static const char _myanmar_syllable_machine_indicies[] = {
 	1, 1, 2, 3, 4, 4, 0, 5, 
 	0, 6, 0, 1, 0, 0, 0, 7, 
 	0, 8, 1, 0, 9, 10, 11, 12, 
-	13, 14, 15, 16, 17, 18, 0, 20, 
-	21, 22, 22, 19, 23, 19, 24, 19, 
-	19, 19, 19, 19, 19, 19, 25, 19, 
-	19, 26, 27, 28, 29, 30, 31, 32, 
-	33, 34, 35, 19, 22, 22, 19, 23, 
-	19, 19, 19, 19, 19, 19, 19, 19, 
-	19, 36, 19, 19, 19, 19, 19, 19, 
-	30, 19, 19, 19, 34, 19, 22, 22, 
-	19, 23, 19, 22, 22, 19, 23, 19, 
-	19, 19, 19, 19, 19, 19, 19, 19, 
-	19, 19, 19, 19, 19, 19, 19, 30, 
-	19, 19, 19, 34, 19, 37, 19, 22, 
-	22, 19, 23, 19, 30, 19, 19, 19, 
-	19, 19, 19, 19, 19, 19, 19, 19, 
-	19, 19, 19, 30, 19, 22, 22, 19, 
-	23, 19, 19, 19, 19, 19, 19, 19, 
-	19, 19, 38, 19, 19, 19, 19, 19, 
-	19, 30, 19, 22, 22, 19, 23, 19, 
-	19, 19, 19, 19, 19, 19, 19, 19, 
-	19, 19, 19, 19, 19, 19, 19, 30, 
-	19, 20, 19, 22, 22, 19, 23, 19, 
-	24, 19, 19, 19, 19, 19, 19, 19, 
-	39, 19, 19, 39, 19, 19, 19, 30, 
-	40, 19, 19, 34, 19, 20, 19, 22, 
-	22, 19, 23, 19, 24, 19, 19, 19, 
-	19, 19, 19, 19, 19, 19, 19, 19, 
-	19, 19, 19, 30, 19, 19, 19, 34, 
-	19, 20, 19, 22, 22, 19, 23, 19, 
-	24, 19, 19, 19, 19, 19, 19, 19, 
-	39, 19, 19, 19, 19, 19, 19, 30, 
-	40, 19, 19, 34, 19, 20, 19, 22, 
-	22, 19, 23, 19, 24, 19, 19, 19, 
-	19, 19, 19, 19, 19, 19, 19, 19, 
-	19, 19, 19, 30, 40, 19, 19, 34, 
-	19, 1, 1, 19, 19, 19, 19, 19, 
-	19, 19, 19, 19, 19, 19, 19, 19, 
-	1, 19, 20, 19, 22, 22, 19, 23, 
-	19, 24, 19, 19, 19, 19, 19, 19, 
-	19, 25, 19, 19, 26, 27, 28, 29, 
-	30, 31, 32, 33, 34, 19, 20, 19, 
-	22, 22, 19, 23, 19, 24, 19, 19, 
-	19, 19, 19, 19, 19, 33, 19, 19, 
-	19, 19, 19, 19, 30, 31, 32, 33, 
-	34, 19, 20, 19, 22, 22, 19, 23, 
-	19, 24, 19, 19, 19, 19, 19, 19, 
-	19, 19, 19, 19, 19, 19, 19, 19, 
-	30, 31, 32, 33, 34, 19, 20, 19, 
-	22, 22, 19, 23, 19, 24, 19, 19, 
-	19, 19, 19, 19, 19, 19, 19, 19, 
-	19, 19, 19, 19, 30, 31, 32, 19, 
-	34, 19, 20, 19, 22, 22, 19, 23, 
-	19, 24, 19, 19, 19, 19, 19, 19, 
-	19, 19, 19, 19, 19, 19, 19, 19, 
-	30, 19, 32, 19, 34, 19, 20, 19, 
-	22, 22, 19, 23, 19, 24, 19, 19, 
-	19, 19, 19, 19, 19, 19, 19, 19, 
-	26, 19, 28, 19, 30, 31, 32, 33, 
-	34, 19, 20, 19, 22, 22, 19, 23, 
-	19, 24, 19, 19, 19, 19, 19, 19, 
-	19, 33, 19, 19, 26, 19, 19, 19, 
-	30, 31, 32, 33, 34, 19, 20, 19, 
-	22, 22, 19, 23, 19, 24, 19, 19, 
-	19, 19, 19, 19, 19, 19, 19, 19, 
-	26, 27, 28, 19, 30, 31, 32, 33, 
-	34, 19, 20, 21, 22, 22, 19, 23, 
-	19, 24, 19, 19, 19, 19, 19, 19, 
-	19, 25, 19, 19, 26, 27, 28, 29, 
-	30, 31, 32, 33, 34, 19, 3, 3, 
-	41, 5, 41, 41, 41, 41, 41, 41, 
-	41, 41, 41, 42, 41, 41, 41, 41, 
-	41, 41, 13, 41, 41, 41, 17, 41, 
-	3, 3, 41, 5, 41, 3, 3, 41, 
-	5, 41, 41, 41, 41, 41, 41, 41, 
-	41, 41, 41, 41, 41, 41, 41, 41, 
-	41, 13, 41, 41, 41, 17, 41, 43, 
-	41, 3, 3, 41, 5, 41, 13, 41, 
-	41, 41, 41, 41, 41, 41, 41, 41, 
-	41, 41, 41, 41, 41, 13, 41, 3, 
-	3, 41, 5, 41, 41, 41, 41, 41, 
-	41, 41, 41, 41, 44, 41, 41, 41, 
-	41, 41, 41, 13, 41, 3, 3, 41, 
-	5, 41, 41, 41, 41, 41, 41, 41, 
-	41, 41, 41, 41, 41, 41, 41, 41, 
-	41, 13, 41, 2, 41, 3, 3, 41, 
-	5, 41, 6, 41, 41, 41, 41, 41, 
-	41, 41, 45, 41, 41, 45, 41, 41, 
-	41, 13, 46, 41, 41, 17, 41, 2, 
-	41, 3, 3, 41, 5, 41, 6, 41, 
-	41, 41, 41, 41, 41, 41, 41, 41, 
-	41, 41, 41, 41, 41, 13, 41, 41, 
-	41, 17, 41, 2, 41, 3, 3, 41, 
-	5, 41, 6, 41, 41, 41, 41, 41, 
-	41, 41, 45, 41, 41, 41, 41, 41, 
-	41, 13, 46, 41, 41, 17, 41, 2, 
-	41, 3, 3, 41, 5, 41, 6, 41, 
-	41, 41, 41, 41, 41, 41, 41, 41, 
-	41, 41, 41, 41, 41, 13, 46, 41, 
-	41, 17, 41, 20, 21, 22, 22, 19, 
-	23, 19, 24, 19, 19, 19, 19, 19, 
-	19, 19, 47, 19, 19, 26, 27, 28, 
-	29, 30, 31, 32, 33, 34, 35, 19, 
-	20, 48, 22, 22, 19, 23, 19, 24, 
-	19, 19, 19, 19, 19, 19, 19, 25, 
-	19, 19, 26, 27, 28, 29, 30, 31, 
-	32, 33, 34, 19, 1, 1, 2, 3, 
-	3, 3, 41, 5, 41, 6, 41, 1, 
-	41, 41, 41, 1, 41, 8, 1, 41, 
-	9, 10, 11, 12, 13, 14, 15, 16, 
-	17, 18, 41, 2, 41, 3, 3, 41, 
-	5, 41, 6, 41, 41, 41, 41, 41, 
-	41, 41, 8, 41, 41, 9, 10, 11, 
-	12, 13, 14, 15, 16, 17, 41, 2, 
-	41, 3, 3, 41, 5, 41, 6, 41, 
-	41, 41, 41, 41, 41, 41, 16, 41, 
-	41, 41, 41, 41, 41, 13, 14, 15, 
-	16, 17, 41, 2, 41, 3, 3, 41, 
-	5, 41, 6, 41, 41, 41, 41, 41, 
-	41, 41, 41, 41, 41, 41, 41, 41, 
-	41, 13, 14, 15, 16, 17, 41, 2, 
-	41, 3, 3, 41, 5, 41, 6, 41, 
-	41, 41, 41, 41, 41, 41, 41, 41, 
-	41, 41, 41, 41, 41, 13, 14, 15, 
-	41, 17, 41, 2, 41, 3, 3, 41, 
-	5, 41, 6, 41, 41, 41, 41, 41, 
-	41, 41, 41, 41, 41, 41, 41, 41, 
-	41, 13, 41, 15, 41, 17, 41, 2, 
-	41, 3, 3, 41, 5, 41, 6, 41, 
-	41, 41, 41, 41, 41, 41, 41, 41, 
-	41, 9, 41, 11, 41, 13, 14, 15, 
-	16, 17, 41, 2, 41, 3, 3, 41, 
-	5, 41, 6, 41, 41, 41, 41, 41, 
-	41, 41, 16, 41, 41, 9, 41, 41, 
-	41, 13, 14, 15, 16, 17, 41, 2, 
-	41, 3, 3, 41, 5, 41, 6, 41, 
-	41, 41, 41, 41, 41, 41, 41, 41, 
-	41, 9, 10, 11, 41, 13, 14, 15, 
-	16, 17, 41, 2, 3, 3, 3, 41, 
-	5, 41, 6, 41, 41, 41, 41, 41, 
-	41, 41, 8, 41, 41, 9, 10, 11, 
-	12, 13, 14, 15, 16, 17, 41, 0
+	13, 14, 15, 16, 17, 18, 19, 0, 
+	21, 22, 23, 23, 20, 24, 20, 25, 
+	20, 20, 20, 20, 20, 20, 20, 26, 
+	20, 20, 27, 28, 29, 30, 31, 32, 
+	33, 34, 35, 36, 20, 23, 23, 20, 
+	24, 20, 20, 20, 20, 20, 20, 20, 
+	20, 20, 37, 20, 20, 20, 20, 20, 
+	20, 31, 20, 20, 20, 35, 20, 23, 
+	23, 20, 24, 20, 23, 23, 20, 24, 
+	20, 20, 20, 20, 20, 20, 20, 20, 
+	20, 20, 20, 20, 20, 20, 20, 20, 
+	31, 20, 20, 20, 35, 20, 38, 20, 
+	23, 23, 20, 24, 20, 31, 20, 20, 
+	20, 20, 20, 20, 20, 39, 20, 20, 
+	20, 20, 20, 20, 31, 20, 23, 23, 
+	20, 24, 20, 20, 20, 20, 20, 20, 
+	20, 20, 20, 39, 20, 20, 20, 20, 
+	20, 20, 31, 20, 23, 23, 20, 24, 
+	20, 20, 20, 20, 20, 20, 20, 20, 
+	20, 20, 20, 20, 20, 20, 20, 20, 
+	31, 20, 21, 20, 23, 23, 20, 24, 
+	20, 25, 20, 20, 20, 20, 20, 20, 
+	20, 40, 20, 20, 40, 20, 20, 20, 
+	31, 41, 20, 20, 35, 20, 21, 20, 
+	23, 23, 20, 24, 20, 25, 20, 20, 
+	20, 20, 20, 20, 20, 20, 20, 20, 
+	20, 20, 20, 20, 31, 20, 20, 20, 
+	35, 20, 21, 20, 23, 23, 20, 24, 
+	20, 25, 20, 20, 20, 20, 20, 20, 
+	20, 40, 20, 20, 20, 20, 20, 20, 
+	31, 41, 20, 20, 35, 20, 21, 20, 
+	23, 23, 20, 24, 20, 25, 20, 20, 
+	20, 20, 20, 20, 20, 20, 20, 20, 
+	20, 20, 20, 20, 31, 41, 20, 20, 
+	35, 20, 1, 1, 20, 20, 20, 20, 
+	20, 20, 20, 20, 20, 20, 20, 20, 
+	20, 1, 20, 21, 20, 23, 23, 20, 
+	24, 20, 25, 20, 20, 20, 20, 20, 
+	20, 20, 26, 20, 20, 27, 28, 29, 
+	30, 31, 32, 33, 34, 35, 20, 21, 
+	20, 23, 23, 20, 24, 20, 25, 20, 
+	20, 20, 20, 20, 20, 20, 34, 20, 
+	20, 20, 20, 20, 20, 31, 32, 33, 
+	34, 35, 20, 21, 20, 23, 23, 20, 
+	24, 20, 25, 20, 20, 20, 20, 20, 
+	20, 20, 20, 20, 20, 20, 20, 20, 
+	20, 31, 32, 33, 34, 35, 20, 21, 
+	20, 23, 23, 20, 24, 20, 25, 20, 
+	20, 20, 20, 20, 20, 20, 20, 20, 
+	20, 20, 20, 20, 20, 31, 32, 33, 
+	20, 35, 20, 21, 20, 23, 23, 20, 
+	24, 20, 25, 20, 20, 20, 20, 20, 
+	20, 20, 20, 20, 20, 20, 20, 20, 
+	20, 31, 20, 33, 20, 35, 20, 21, 
+	20, 23, 23, 20, 24, 20, 25, 20, 
+	20, 20, 20, 20, 20, 20, 20, 20, 
+	20, 27, 20, 29, 20, 31, 32, 33, 
+	34, 35, 20, 21, 20, 23, 23, 20, 
+	24, 20, 25, 20, 20, 20, 20, 20, 
+	20, 20, 34, 20, 20, 27, 20, 20, 
+	20, 31, 32, 33, 34, 35, 20, 21, 
+	20, 23, 23, 20, 24, 20, 25, 20, 
+	20, 20, 20, 20, 20, 20, 20, 20, 
+	20, 27, 28, 29, 20, 31, 32, 33, 
+	34, 35, 20, 21, 22, 23, 23, 20, 
+	24, 20, 25, 20, 20, 20, 20, 20, 
+	20, 20, 26, 20, 20, 27, 28, 29, 
+	30, 31, 32, 33, 34, 35, 20, 3, 
+	3, 42, 5, 42, 42, 42, 42, 42, 
+	42, 42, 42, 42, 43, 42, 42, 42, 
+	42, 42, 42, 13, 42, 42, 42, 17, 
+	42, 3, 3, 42, 5, 42, 3, 3, 
+	42, 5, 42, 42, 42, 42, 42, 42, 
+	42, 42, 42, 42, 42, 42, 42, 42, 
+	42, 42, 13, 42, 42, 42, 17, 42, 
+	44, 42, 3, 3, 42, 5, 42, 13, 
+	42, 42, 42, 42, 42, 42, 42, 45, 
+	42, 42, 42, 42, 42, 42, 13, 42, 
+	3, 3, 42, 5, 42, 42, 42, 42, 
+	42, 42, 42, 42, 42, 45, 42, 42, 
+	42, 42, 42, 42, 13, 42, 3, 3, 
+	42, 5, 42, 42, 42, 42, 42, 42, 
+	42, 42, 42, 42, 42, 42, 42, 42, 
+	42, 42, 13, 42, 2, 42, 3, 3, 
+	42, 5, 42, 6, 42, 42, 42, 42, 
+	42, 42, 42, 46, 42, 42, 46, 42, 
+	42, 42, 13, 47, 42, 42, 17, 42, 
+	2, 42, 3, 3, 42, 5, 42, 6, 
+	42, 42, 42, 42, 42, 42, 42, 42, 
+	42, 42, 42, 42, 42, 42, 13, 42, 
+	42, 42, 17, 42, 2, 42, 3, 3, 
+	42, 5, 42, 6, 42, 42, 42, 42, 
+	42, 42, 42, 46, 42, 42, 42, 42, 
+	42, 42, 13, 47, 42, 42, 17, 42, 
+	2, 42, 3, 3, 42, 5, 42, 6, 
+	42, 42, 42, 42, 42, 42, 42, 42, 
+	42, 42, 42, 42, 42, 42, 13, 47, 
+	42, 42, 17, 42, 21, 22, 23, 23, 
+	20, 24, 20, 25, 20, 20, 20, 20, 
+	20, 20, 20, 48, 20, 20, 27, 28, 
+	29, 30, 31, 32, 33, 34, 35, 36, 
+	20, 21, 49, 23, 23, 20, 24, 20, 
+	25, 20, 20, 20, 20, 20, 20, 20, 
+	26, 20, 20, 27, 28, 29, 30, 31, 
+	32, 33, 34, 35, 20, 1, 1, 2, 
+	3, 3, 3, 42, 5, 42, 6, 42, 
+	1, 42, 42, 42, 1, 42, 8, 1, 
+	42, 9, 10, 11, 12, 13, 14, 15, 
+	16, 17, 18, 42, 2, 42, 3, 3, 
+	42, 5, 42, 6, 42, 42, 42, 42, 
+	42, 42, 42, 8, 42, 42, 9, 10, 
+	11, 12, 13, 14, 15, 16, 17, 42, 
+	2, 42, 3, 3, 42, 5, 42, 6, 
+	42, 42, 42, 42, 42, 42, 42, 16, 
+	42, 42, 42, 42, 42, 42, 13, 14, 
+	15, 16, 17, 42, 2, 42, 3, 3, 
+	42, 5, 42, 6, 42, 42, 42, 42, 
+	42, 42, 42, 42, 42, 42, 42, 42, 
+	42, 42, 13, 14, 15, 16, 17, 42, 
+	2, 42, 3, 3, 42, 5, 42, 6, 
+	42, 42, 42, 42, 42, 42, 42, 42, 
+	42, 42, 42, 42, 42, 42, 13, 14, 
+	15, 42, 17, 42, 2, 42, 3, 3, 
+	42, 5, 42, 6, 42, 42, 42, 42, 
+	42, 42, 42, 42, 42, 42, 42, 42, 
+	42, 42, 13, 42, 15, 42, 17, 42, 
+	2, 42, 3, 3, 42, 5, 42, 6, 
+	42, 42, 42, 42, 42, 42, 42, 42, 
+	42, 42, 9, 42, 11, 42, 13, 14, 
+	15, 16, 17, 42, 2, 42, 3, 3, 
+	42, 5, 42, 6, 42, 42, 42, 42, 
+	42, 42, 42, 16, 42, 42, 9, 42, 
+	42, 42, 13, 14, 15, 16, 17, 42, 
+	2, 42, 3, 3, 42, 5, 42, 6, 
+	42, 42, 42, 42, 42, 42, 42, 42, 
+	42, 42, 9, 10, 11, 42, 13, 14, 
+	15, 16, 17, 42, 2, 3, 3, 3, 
+	42, 5, 42, 6, 42, 42, 42, 42, 
+	42, 42, 42, 8, 42, 42, 9, 10, 
+	11, 12, 13, 14, 15, 16, 17, 42, 
+	51, 50, 0
 };
 
 static const char _myanmar_syllable_machine_trans_targs[] = {
 	0, 1, 22, 0, 0, 23, 29, 32, 
 	35, 36, 40, 41, 42, 25, 38, 39, 
-	37, 28, 43, 0, 2, 12, 0, 3, 
-	9, 13, 14, 18, 19, 20, 5, 16, 
-	17, 15, 8, 21, 4, 6, 7, 10, 
-	11, 0, 24, 26, 27, 30, 31, 33, 
-	34
+	37, 28, 43, 44, 0, 2, 12, 0, 
+	3, 9, 13, 14, 18, 19, 20, 5, 
+	16, 17, 15, 8, 21, 4, 6, 7, 
+	10, 11, 0, 24, 26, 27, 30, 31, 
+	33, 34, 0, 0
 };
 
 static const char _myanmar_syllable_machine_trans_actions[] = {
 	3, 0, 0, 4, 5, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 0, 0, 6, 0, 0, 7, 0, 
+	0, 0, 0, 0, 6, 0, 0, 7, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 8, 0, 0, 0, 0, 0, 0, 
-	0
+	0, 0, 8, 0, 0, 0, 0, 0, 
+	0, 0, 9, 10
 };
 
 static const char _myanmar_syllable_machine_to_state_actions[] = {
 	1, 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
 };
 
 static const char _myanmar_syllable_machine_from_state_actions[] = {
 	2, 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
 };
 
 static const short _myanmar_syllable_machine_eof_trans[] = {
-	0, 20, 20, 20, 20, 20, 20, 20, 
-	20, 20, 20, 20, 20, 20, 20, 20, 
-	20, 20, 20, 20, 20, 20, 42, 42, 
-	42, 42, 42, 42, 42, 42, 42, 42, 
-	20, 20, 42, 42, 42, 42, 42, 42, 
-	42, 42, 42, 42
+	0, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 21, 21, 21, 43, 43, 
+	43, 43, 43, 43, 43, 43, 43, 43, 
+	21, 21, 43, 43, 43, 43, 43, 43, 
+	43, 43, 43, 43, 51
 };
 
 static const int myanmar_syllable_machine_start = 0;
 static const int myanmar_syllable_machine_first_final = 0;
 static const int myanmar_syllable_machine_error = -1;
 
 static const int myanmar_syllable_machine_en_main = 0;
 
 
 #line 36 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
 
 
 
-#line 90 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 93 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
 
 
 #define found_syllable(syllable_type) \
   HB_STMT_START { \
     if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
     for (unsigned int i = last; i < p+1; i++) \
       info[i].syllable() = (syllable_serial << 4) | syllable_type; \
     last = p+1; \
@@ -279,48 +280,48 @@ static const int myanmar_syllable_machin
 
 static void
 find_syllables (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   
-#line 288 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
+#line 289 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
 	{
 	cs = myanmar_syllable_machine_start;
 	ts = 0;
 	te = 0;
 	act = 0;
 	}
 
-#line 111 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 114 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
 
 
   p = 0;
   pe = eof = buffer->len;
 
   unsigned int last = 0;
   unsigned int syllable_serial = 1;
   
-#line 305 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
+#line 306 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
 	{
 	int _slen;
 	int _trans;
 	const unsigned char *_keys;
 	const char *_inds;
 	if ( p == pe )
 		goto _test_eof;
 _resume:
 	switch ( _myanmar_syllable_machine_from_state_actions[cs] ) {
 	case 2:
 #line 1 "NONE"
 	{ts = p;}
 	break;
-#line 319 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
+#line 320 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
 	}
 
 	_keys = _myanmar_syllable_machine_trans_keys + (cs<<1);
 	_inds = _myanmar_syllable_machine_indicies + _myanmar_syllable_machine_index_offsets[cs];
 
 	_slen = _myanmar_syllable_machine_key_spans[cs];
 	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].myanmar_category()) &&
 		( info[p].myanmar_category()) <= _keys[1] ?
@@ -329,63 +330,71 @@ find_syllables (hb_buffer_t *buffer)
 _eof_trans:
 	cs = _myanmar_syllable_machine_trans_targs[_trans];
 
 	if ( _myanmar_syllable_machine_trans_actions[_trans] == 0 )
 		goto _again;
 
 	switch ( _myanmar_syllable_machine_trans_actions[_trans] ) {
 	case 7:
-#line 83 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 85 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p+1;{ found_syllable (consonant_syllable); }}
 	break;
 	case 5:
-#line 84 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 86 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p+1;{ found_syllable (non_myanmar_cluster); }}
 	break;
+	case 10:
+#line 87 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (punctuation_cluster); }}
+	break;
 	case 4:
-#line 85 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 88 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p+1;{ found_syllable (broken_cluster); }}
 	break;
 	case 3:
-#line 86 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 89 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p+1;{ found_syllable (non_myanmar_cluster); }}
 	break;
 	case 6:
-#line 83 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 85 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p;p--;{ found_syllable (consonant_syllable); }}
 	break;
 	case 8:
-#line 85 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 88 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
 	{te = p;p--;{ found_syllable (broken_cluster); }}
 	break;
-#line 361 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
+	case 9:
+#line 89 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p;p--;{ found_syllable (non_myanmar_cluster); }}
+	break;
+#line 370 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
 	}
 
 _again:
 	switch ( _myanmar_syllable_machine_to_state_actions[cs] ) {
 	case 1:
 #line 1 "NONE"
 	{ts = 0;}
 	break;
-#line 370 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
+#line 379 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
 	}
 
 	if ( ++p != pe )
 		goto _resume;
 	_test_eof: {}
 	if ( p == eof )
 	{
 	if ( _myanmar_syllable_machine_eof_trans[cs] > 0 ) {
 		_trans = _myanmar_syllable_machine_eof_trans[cs] - 1;
 		goto _eof_trans;
 	}
 	}
 
 	}
 
-#line 120 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
+#line 123 "../../src/hb-ot-shape-complex-myanmar-machine.rl"
 
 }
 
 #undef found_syllable
 
 #endif /* HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.rl
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar-machine.rl
@@ -56,37 +56,40 @@ V    = 8;
 VAbv = 26;
 VBlw = 27;
 VPre = 28;
 VPst = 29;
 VS   = 30;
 ZWJ  = 6;
 ZWNJ = 5;
 Ra   = 16;
+P    = 31;
 
 j = ZWJ|ZWNJ;			# Joiners
 k = (Ra As H);			# Kinzi
 
 c = C|Ra;			# is_consonant
 
 medial_group = MY? MR? ((MW MH? | MH) As?)?;
 main_vowel_group = VPre* VAbv* VBlw* A* (DB As?)?;
 post_vowel_group = VPst MH? As* VAbv* A* (DB As?)?;
-pwo_tone_group = PT A* (DB As?)?;
+pwo_tone_group = PT A* DB? As?;
 
 complex_syllable_tail = As* medial_group main_vowel_group post_vowel_group* pwo_tone_group* V* j?;
 syllable_tail = (H | complex_syllable_tail);
 
 consonant_syllable =	k? (c|IV|D|GB).VS? (H (c|IV).VS?)* syllable_tail;
+punctuation_cluster = 	P V;
 broken_cluster =	k? VS? syllable_tail;
 other =			any;
 
 main := |*
 	consonant_syllable	=> { found_syllable (consonant_syllable); };
 	j			=> { found_syllable (non_myanmar_cluster); };
+	punctuation_cluster	=> { found_syllable (punctuation_cluster); };
 	broken_cluster		=> { found_syllable (broken_cluster); };
 	other			=> { found_syllable (non_myanmar_cluster); };
 *|;
 
 
 }%%
 
 #define found_syllable(syllable_type) \
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
@@ -114,16 +114,17 @@ static void
 override_features_myanmar (hb_ot_shape_planner_t *plan)
 {
   plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL);
 }
 
 
 enum syllable_type_t {
   consonant_syllable,
+  punctuation_cluster,
   broken_cluster,
   non_myanmar_cluster,
 };
 
 #include "hb-ot-shape-complex-myanmar-machine.hh"
 
 
 /* Note: This enum is duplicated in the -machine.rl source file.
@@ -138,17 +139,18 @@ enum myanmar_category_t {
   OT_MR  = 22, /* Various consonant medial types */
   OT_MW  = 23, /* Various consonant medial types */
   OT_MY  = 24, /* Various consonant medial types */
   OT_PT  = 25, /* Pwo and other tones */
   OT_VAbv = 26,
   OT_VBlw = 27,
   OT_VPre = 28,
   OT_VPst = 29,
-  OT_VS   = 30 /* Variation selectors */
+  OT_VS   = 30, /* Variation selectors */
+  OT_P    = 31  /* Punctuation */
 };
 
 
 static inline bool
 is_one_of (const hb_glyph_info_t &info, unsigned int flags)
 {
   /* If it ligated, all bets are off. */
   if (_hb_glyph_info_ligated (&info)) return false;
@@ -181,16 +183,20 @@ set_myanmar_properties (hb_glyph_info_t 
    */
   if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xFE00, 0xFE0F)))
     cat = (indic_category_t) OT_VS;
   else if (unlikely (u == 0x200C)) cat = (indic_category_t) OT_ZWNJ;
   else if (unlikely (u == 0x200D)) cat = (indic_category_t) OT_ZWJ;
 
   switch (u)
   {
+    case 0x104E:
+      cat = (indic_category_t) OT_C; /* The spec says C, IndicSyllableCategory doesn't have. */
+      break;
+
     case 0x002D: case 0x00A0: case 0x00D7: case 0x2012:
     case 0x2013: case 0x2014: case 0x2015: case 0x2022:
     case 0x25CC: case 0x25FB: case 0x25FC: case 0x25FD:
     case 0x25FE:
       cat = (indic_category_t) OT_GB;
       break;
 
     case 0x1004: case 0x101B: case 0x105A:
@@ -238,16 +244,20 @@ set_myanmar_properties (hb_glyph_info_t 
       cat = (indic_category_t) OT_PT;
       break;
 
     case 0x1038: case 0x1087: case 0x1088: case 0x1089:
     case 0x108A: case 0x108B: case 0x108C: case 0x108D:
     case 0x108F: case 0x109A: case 0x109B: case 0x109C:
       cat = (indic_category_t) OT_SM;
       break;
+
+    case 0x104A: case 0x104B:
+      cat = (indic_category_t) OT_P;
+      break;
   }
 
   if (cat == OT_M)
   {
     switch ((int) pos)
     {
       case POS_PRE_C:	cat = (indic_category_t) OT_VPre;
 			pos = POS_PRE_M;                  break;
@@ -401,16 +411,26 @@ initial_reordering_broken_cluster (const
 				   hb_buffer_t *buffer,
 				   unsigned int start, unsigned int end)
 {
   /* We already inserted dotted-circles, so just call the consonant_syllable. */
   initial_reordering_consonant_syllable (plan, face, buffer, start, end);
 }
 
 static void
+initial_reordering_punctuation_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
+					hb_face_t *face HB_UNUSED,
+					hb_buffer_t *buffer HB_UNUSED,
+					unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
+{
+  /* Nothing to do right now.  If we ever switch to using the output
+   * buffer in the reordering process, we'd need to next_glyph() here. */
+}
+
+static void
 initial_reordering_non_myanmar_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
 					hb_face_t *face HB_UNUSED,
 					hb_buffer_t *buffer HB_UNUSED,
 					unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
 {
   /* Nothing to do right now.  If we ever switch to using the output
    * buffer in the reordering process, we'd need to next_glyph() here. */
 }
@@ -420,16 +440,17 @@ static void
 initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
 			     hb_face_t *face,
 			     hb_buffer_t *buffer,
 			     unsigned int start, unsigned int end)
 {
   syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
   switch (syllable_type) {
   case consonant_syllable:	initial_reordering_consonant_syllable  (plan, face, buffer, start, end); return;
+  case punctuation_cluster:	initial_reordering_punctuation_cluster (plan, face, buffer, start, end); return;
   case broken_cluster:		initial_reordering_broken_cluster      (plan, face, buffer, start, end); return;
   case non_myanmar_cluster:	initial_reordering_non_myanmar_cluster (plan, face, buffer, start, end); return;
   }
 }
 
 static inline void
 insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		       hb_font_t *font,
--- a/gfx/harfbuzz/src/hb-ot-shape-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-private.hh
@@ -61,17 +61,17 @@ struct hb_ot_shape_planner_t
 {
   /* In the order that they are filled in. */
   hb_face_t *face;
   hb_segment_properties_t props;
   const struct hb_ot_complex_shaper_t *shaper;
   hb_ot_map_builder_t map;
 
   hb_ot_shape_planner_t (const hb_shape_plan_t *master_plan) :
-			 face (master_plan->face),
+			 face (master_plan->face_unsafe),
 			 props (master_plan->props),
 			 shaper (NULL),
 			 map (face, &props) {}
   ~hb_ot_shape_planner_t (void) { map.finish (); }
 
   inline void compile (hb_ot_shape_plan_t &plan)
   {
     plan.props = props;
--- a/gfx/harfbuzz/src/hb-ot-tag.cc
+++ b/gfx/harfbuzz/src/hb-ot-tag.cc
@@ -162,426 +162,608 @@ typedef struct {
 } 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.
+ * 2008-08-04, matching on name, and finally adjusted manually.
  *
- * Updated on 2012/12/07 with more research into remaining codes.
+ * Updated on 2012-12-07 with more research into remaining codes.
+ *
+ * Updated on 2013-11-23 based on usage in SIL and Microsoft fonts,
+ * the new proposal from Microsoft, and latest ISO 639-3 names.
  *
  * Some 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 */
+  {"ach",	HB_TAG('A','C','H',' ')},	/* Acoli */
   {"ada",	HB_TAG('D','N','G',' ')},	/* Dangme */
   {"ady",	HB_TAG('A','D','Y',' ')},	/* Adyghe */
   {"af",	HB_TAG('A','F','K',' ')},	/* Afrikaans */
   {"aii",	HB_TAG('S','W','A',' ')},	/* Swadaya Aramaic */
+  {"aio",	HB_TAG('A','I','O',' ')},	/* Aiton */
   {"aiw",	HB_TAG('A','R','I',' ')},	/* Aari */
+  {"ak",	HB_TAG('T','W','I',' ')},	/* Akan [macrolanguage] */
   {"alt",	HB_TAG('A','L','T',' ')},	/* [Southern] Altai */
   {"am",	HB_TAG('A','M','H',' ')},	/* Amharic */
   {"amf",	HB_TAG('H','B','N',' ')},	/* Hammer-Banna */
-  {"ar",	HB_TAG('A','R','A',' ')},	/* Arabic */
+  {"an",	HB_TAG('A','R','G',' ')},	/* Aragonese */
+  {"ang",	HB_TAG('A','N','G',' ')},	/* Old English (ca. 450-1100) */
+  {"ar",	HB_TAG('A','R','A',' ')},	/* Arabic [macrolanguage] */
+  {"arb",	HB_TAG('A','R','A',' ')},	/* Standard Arabic */
   {"arn",	HB_TAG('M','A','P',' ')},	/* Mapudungun */
+  {"ary",	HB_TAG('M','O','R',' ')},	/* Moroccan Arabic */
   {"as",	HB_TAG('A','S','M',' ')},	/* Assamese */
+  {"ast",	HB_TAG('A','S','T',' ')},	/* Asturian/Asturleonese/Bable/Leonese */
   {"ath",	HB_TAG('A','T','H',' ')},	/* Athapaskan [family] */
   {"atv",	HB_TAG('A','L','T',' ')},	/* [Northern] Altai */
   {"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 */
+  {"ay",	HB_TAG('A','Y','M',' ')},	/* Aymara [macrolanguage] */
+  {"az",	HB_TAG('A','Z','E',' ')},	/* Azerbaijani [macrolanguage] */
+  {"azb",	HB_TAG('A','Z','B',' ')},	/* South Azerbaijani */
+  {"azj",	HB_TAG('A','Z','E',' ')},	/* North Azerbaijani */
   {"ba",	HB_TAG('B','S','H',' ')},	/* Bashkir */
   {"bai",	HB_TAG('B','M','L',' ')},	/* Bamileke [family] */
-  {"bal",	HB_TAG('B','L','I',' ')},	/* Baluchi */
-  {"bci",	HB_TAG('B','A','U',' ')},	/* Baule */
+  {"bal",	HB_TAG('B','L','I',' ')},	/* Baluchi [macrolangauge] */
+  {"ban",	HB_TAG('B','A','N',' ')},	/* Balinese */
+  {"bar",	HB_TAG('B','A','R',' ')},	/* Bavarian */
+  {"bbc",	HB_TAG('B','B','C',' ')},	/* Batak Toba */
+  {"bci",	HB_TAG('B','A','U',' ')},	/* Baoulé */
+  {"bcl",	HB_TAG('B','I','K',' ')},	/* Central Bikol */
   {"bcq",	HB_TAG('B','C','H',' ')},	/* Bench */
-  {"be",	HB_TAG('B','E','L',' ')},  	/* Belarussian */
+  {"be",	HB_TAG('B','E','L',' ')},  	/* Belarusian */
   {"bem",	HB_TAG('B','E','M',' ')},	/* Bemba (Zambia) */
   {"ber",	HB_TAG('B','E','R',' ')},  	/* Berber [family] */
   {"bfq",	HB_TAG('B','A','D',' ')},	/* Badaga */
   {"bft",	HB_TAG('B','L','T',' ')},	/* Balti */
   {"bfy",	HB_TAG('B','A','G',' ')},	/* Baghelkhandi */
   {"bg",	HB_TAG('B','G','R',' ')},	/* Bulgarian */
+  {"bgc",	HB_TAG('B','G','C',' ')},	/* Haryanvi */
+  {"bgq",	HB_TAG('B','G','Q',' ')},	/* Bagri */
   {"bhb",	HB_TAG('B','H','I',' ')},	/* Bhili */
+  {"bhk",	HB_TAG('B','I','K',' ')},	/* Albay Bicolano (retired code) */
   {"bho",	HB_TAG('B','H','O',' ')},	/* Bhojpuri */
-  {"bik",	HB_TAG('B','I','K',' ')},	/* Bikol */
+  {"bi",	HB_TAG('B','I','S',' ')},	/* Bislama */
+  {"bik",	HB_TAG('B','I','K',' ')},	/* Bikol [macrolanguage] */
   {"bin",	HB_TAG('E','D','O',' ')},	/* Bini */
+  {"bjj",	HB_TAG('B','J','J',' ')},	/* Kanauji */
   {"bjt",	HB_TAG('B','L','N',' ')},	/* Balanta-Ganja */
   {"bla",	HB_TAG('B','K','F',' ')},	/* Blackfoot */
   {"ble",	HB_TAG('B','L','N',' ')},	/* Balanta-Kentohe */
+  {"blk",	HB_TAG('B','L','K',' ')},	/* Pa'O/Pa'o Karen */
+  {"bln",	HB_TAG('B','I','K',' ')},	/* Southern Catanduanes Bikol */
   {"bm",	HB_TAG('B','M','B',' ')},	/* Bambara */
   {"bn",	HB_TAG('B','E','N',' ')},	/* Bengali */
   {"bo",	HB_TAG('T','I','B',' ')},	/* Tibetan */
+  {"bpy",	HB_TAG('B','P','Y',' ')},	/* Bishnupriya */
+  {"bqi",	HB_TAG('L','R','C',' ')},	/* Bakhtiari */
   {"br",	HB_TAG('B','R','E',' ')},	/* Breton */
   {"bra",	HB_TAG('B','R','I',' ')},	/* Braj Bhasha */
   {"brh",	HB_TAG('B','R','H',' ')},	/* Brahui */
+  {"brx",	HB_TAG('B','R','X',' ')},	/* Bodo (India) */
   {"bs",	HB_TAG('B','O','S',' ')},	/* Bosnian */
   {"btb",	HB_TAG('B','T','I',' ')},	/* Beti (Cameroon) */
+  {"bto",	HB_TAG('B','I','K',' ')},	/* Rinconada Bikol */
+  {"bts",	HB_TAG('B','T','S',' ')},	/* Batak Simalungun */
+  {"bug",	HB_TAG('B','U','G',' ')},	/* Buginese */
   {"bxr",	HB_TAG('R','B','U',' ')},	/* Russian Buriat */
   {"byn",	HB_TAG('B','I','L',' ')},	/* Bilen */
   {"ca",	HB_TAG('C','A','T',' ')},	/* Catalan */
+  {"cbk",	HB_TAG('C','B','K',' ')},	/* Chavacano */
   {"ce",	HB_TAG('C','H','E',' ')},	/* Chechen */
   {"ceb",	HB_TAG('C','E','B',' ')},	/* Cebuano */
+  {"cgg",	HB_TAG('C','G','G',' ')},	/* Chiga */
+  {"ch",	HB_TAG('C','H','A',' ')},	/* Chamorro */
+  {"cho",	HB_TAG('C','H','O',' ')},	/* Choctaw */
   {"chp",	HB_TAG('C','H','P',' ')},	/* Chipewyan */
   {"chr",	HB_TAG('C','H','R',' ')},	/* Cherokee */
+  {"chy",	HB_TAG('C','H','Y',' ')},	/* Cheyenne */
+  {"ckb",	HB_TAG('K','U','R',' ')},	/* Central Kurdish (Sorani) */
   {"ckt",	HB_TAG('C','H','K',' ')},	/* Chukchi */
   {"cop",	HB_TAG('C','O','P',' ')},	/* Coptic */
   {"cr",	HB_TAG('C','R','E',' ')},	/* Cree */
   {"crh",	HB_TAG('C','R','T',' ')},	/* Crimean Tatar */
   {"crj",	HB_TAG('E','C','R',' ')},	/* [Southern] East Cree */
   {"crl",	HB_TAG('E','C','R',' ')},	/* [Northern] East Cree */
   {"crm",	HB_TAG('M','C','R',' ')},	/* Moose Cree */
   {"crx",	HB_TAG('C','R','R',' ')},	/* Carrier */
   {"cs",	HB_TAG('C','S','Y',' ')},	/* Czech */
+  {"csb",	HB_TAG('C','S','B',' ')},	/* Kashubian */
+  {"ctg",	HB_TAG('C','T','G',' ')},	/* Chittagonian */
+  {"cts",	HB_TAG('B','I','K',' ')},	/* Northern Catanduanes Bikol */
   {"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 */
-  {"dje",	HB_TAG('D','J','R',' ')},	/* Djerma */
+  {"dgo",	HB_TAG('D','G','O',' ')},	/* Dogri */
+  {"dhd",	HB_TAG('M','A','W',' ')},	/* Dhundari */
+  {"din",	HB_TAG('D','N','K',' ')},	/* Dinka [macrolanguage] */
+  {"diq",	HB_TAG('D','I','Q',' ')},	/* Dimli */
+  {"dje",	HB_TAG('D','J','R',' ')},	/* Zarma */
   {"dng",	HB_TAG('D','U','N',' ')},	/* Dungan */
-  {"doi",	HB_TAG('D','G','R',' ')},	/* Dogri */
+  {"doi",	HB_TAG('D','G','R',' ')},	/* Dogri [macrolanguage] */
   {"dsb",	HB_TAG('L','S','B',' ')},	/* Lower Sorbian */
-  {"dv",	HB_TAG('D','I','V',' ')},	/* Dhivehi */
+  {"dv",	HB_TAG('D','I','V',' ')},	/* Dhivehi/Divehi/Maldivian */
   {"dyu",	HB_TAG('J','U','L',' ')},	/* Jula */
   {"dz",	HB_TAG('D','Z','N',' ')},	/* Dzongkha */
   {"ee",	HB_TAG('E','W','E',' ')},	/* Ewe */
   {"efi",	HB_TAG('E','F','I',' ')},	/* Efik */
+  {"ekk",	HB_TAG('E','T','I',' ')},	/* Standard Estonian */
   {"el",	HB_TAG('E','L','L',' ')},	/* Modern Greek (1453-) */
+  {"emk",	HB_TAG('M','N','K',' ')},	/* Eastern Maninkakan */
   {"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 */
+  {"et",	HB_TAG('E','T','I',' ')},	/* Estonian [macrolanguage] */
   {"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 */
+  {"fa",	HB_TAG('F','A','R',' ')},	/* Persian [macrolanguage] */
+  {"ff",	HB_TAG('F','U','L',' ')},	/* Fulah [macrolanguage] */
   {"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 */
+  {"frc",	HB_TAG('F','R','C',' ')},	/* Cajun French */
+  {"frp",	HB_TAG('F','R','P',' ')},	/* Arpitan/Francoprovençal */
   {"fur",	HB_TAG('F','R','L',' ')},	/* Friulian */
+  {"fuv",	HB_TAG('F','U','V',' ')},	/* Nigerian Fulfulde */
   {"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 */
   {"gez",	HB_TAG('G','E','Z',' ')},	/* Ge'ez */
+  {"ggo",	HB_TAG('G','O','N',' ')},	/* Southern Gondi */
   {"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 */
+  {"glk",	HB_TAG('G','L','K',' ')},	/* Gilaki */
+  {"gn",	HB_TAG('G','U','A',' ')},	/* Guarani [macrolanguage] */
+  {"gno",	HB_TAG('G','O','N',' ')},	/* Northern Gondi */
+  {"gog",	HB_TAG('G','O','G',' ')},	/* Gogo */
+  {"gon",	HB_TAG('G','O','N',' ')},	/* Gondi [macrolanguage] */
   {"grt",	HB_TAG('G','R','O',' ')},	/* Garo */
   {"gru",	HB_TAG('S','O','G',' ')},	/* Sodo Gurage */
   {"gu",	HB_TAG('G','U','J',' ')},	/* Gujarati */
+  {"guc",	HB_TAG('G','U','C',' ')},	/* Wayuu */
   {"guk",	HB_TAG('G','M','Z',' ')},	/* Gumuz */
-  {"gv",	HB_TAG('M','N','X',' ')},	/* Manx Gaelic */
+/*{"guk",	HB_TAG('G','U','K',' ')},*/	/* Gumuz (in SIL fonts) */
+  {"guz",	HB_TAG('G','U','Z',' ')},	/* Ekegusii/Gusii */
+  {"gv",	HB_TAG('M','N','X',' ')},	/* Manx */
   {"ha",	HB_TAG('H','A','U',' ')},	/* Hausa */
   {"har",	HB_TAG('H','R','I',' ')},	/* Harari */
-  {"haw",	HB_TAG('H','A','W',' ')},  	/* Hawaiin */
+  {"haw",	HB_TAG('H','A','W',' ')},  	/* Hawaiian */
+  {"hay",	HB_TAG('H','A','Y',' ')},  	/* Haya */
+  {"haz",	HB_TAG('H','A','Z',' ')},  	/* Hazaragi */
   {"he",	HB_TAG('I','W','R',' ')},	/* Hebrew */
+  {"hz",	HB_TAG('H','E','R',' ')},	/* Herero */
   {"hi",	HB_TAG('H','I','N',' ')},	/* Hindi */
   {"hil",	HB_TAG('H','I','L',' ')},	/* Hiligaynon */
   {"hnd",	HB_TAG('H','N','D',' ')},	/* [Southern] Hindko */
   {"hne",	HB_TAG('C','H','H',' ')},	/* Chattisgarhi */
   {"hno",	HB_TAG('H','N','D',' ')},	/* [Northern] Hindko */
+  {"ho",	HB_TAG('H','M','O',' ')},	/* Hiri Motu */
   {"hoc",	HB_TAG('H','O',' ',' ')},	/* Ho */
   {"hoj",	HB_TAG('H','A','R',' ')},	/* Harauti */
   {"hr",	HB_TAG('H','R','V',' ')},	/* Croatian */
   {"hsb",	HB_TAG('U','S','B',' ')},	/* Upper Sorbian */
-  {"ht",	HB_TAG('H','A','I',' ')},	/* Haitian */
+  {"ht",	HB_TAG('H','A','I',' ')},	/* Haitian/Haitian Creole */
   {"hu",	HB_TAG('H','U','N',' ')},	/* Hungarian */
   {"hy",	HB_TAG('H','Y','E',' ')},	/* Armenian */
+  {"hz",	HB_TAG('H','E','R',' ')},	/* Herero */
+  {"ia",	HB_TAG('I','N','A',' ')},	/* Interlingua (International Auxiliary Language Association) */
+  {"ibb",	HB_TAG('I','B','B',' ')},	/* Ibibio */
   {"id",	HB_TAG('I','N','D',' ')},	/* Indonesian */
+  {"ie",	HB_TAG('I','L','E',' ')},	/* Interlingue/Occidental */
   {"ig",	HB_TAG('I','B','O',' ')},	/* Igbo */
   {"igb",	HB_TAG('E','B','I',' ')},	/* Ebira */
+  {"ijc",	HB_TAG('I','J','O',' ')},	/* Izon */
   {"ijo",	HB_TAG('I','J','O',' ')},	/* Ijo [family] */
+  {"ik",	HB_TAG('I','P','K',' ')},	/* Inupiaq [macrolanguage] */
   {"ilo",	HB_TAG('I','L','O',' ')},	/* Ilokano */
   {"inh",	HB_TAG('I','N','G',' ')},	/* Ingush */
+  {"io",	HB_TAG('I','D','O',' ')},	/* Ido */
   {"is",	HB_TAG('I','S','L',' ')},	/* Icelandic */
   {"it",	HB_TAG('I','T','A',' ')},	/* Italian */
-  {"iu",	HB_TAG('I','N','U',' ')},	/* Inuktitut */
+  {"iu",	HB_TAG('I','N','U',' ')},	/* Inuktitut [macrolanguage] */
   {"ja",	HB_TAG('J','A','N',' ')},	/* Japanese */
+  {"jam",	HB_TAG('J','A','M',' ')},	/* Jamaican Creole English */
+  {"jbo",	HB_TAG('J','B','O',' ')},	/* Lojban */
   {"jv",	HB_TAG('J','A','V',' ')},	/* Javanese */
   {"ka",	HB_TAG('K','A','T',' ')},	/* Georgian */
   {"kaa",	HB_TAG('K','R','K',' ')},	/* Karakalpak */
+  {"kab",	HB_TAG('K','A','B',' ')},	/* Kabyle */
   {"kam",	HB_TAG('K','M','B',' ')},	/* Kamba (Kenya) */
   {"kar",	HB_TAG('K','R','N',' ')},	/* Karen [family] */
   {"kbd",	HB_TAG('K','A','B',' ')},	/* Kabardian */
+  {"kde",	HB_TAG('K','D','E',' ')},	/* Makonde */
   {"kdr",	HB_TAG('K','R','M',' ')},	/* Karaim */
   {"kdt",	HB_TAG('K','U','Y',' ')},	/* Kuy */
   {"kex",	HB_TAG('K','K','N',' ')},	/* Kokni */
   {"kfr",	HB_TAG('K','A','C',' ')},	/* Kachchi */
   {"kfy",	HB_TAG('K','M','N',' ')},	/* Kumaoni */
+  {"kg",	HB_TAG('K','O','N',' ')},	/* Kongo [macrolanguage] */
   {"kha",	HB_TAG('K','S','I',' ')},	/* Khasi */
-  {"khb",	HB_TAG('X','B','D',' ')},	/* Tai Lue */
+  {"khb",	HB_TAG('X','B','D',' ')},	/* Lü */
+  {"kht",	HB_TAG('K','H','N',' ')},	/* Khamti (Microsoft fonts) */
+/*{"kht",	HB_TAG('K','H','T',' ')},*/	/* Khamti (OpenType spec and SIL fonts) */
   {"khw",	HB_TAG('K','H','W',' ')},	/* Khowar */
-  {"ki",	HB_TAG('K','I','K',' ')},	/* Kikuyu */
+  {"ki",	HB_TAG('K','I','K',' ')},	/* Gikuyu/Kikuyu */
+  {"kj",	HB_TAG('K','U','A',' ')},	/* Kuanyama/Kwanyama */
   {"kjh",	HB_TAG('K','H','A',' ')},	/* Khakass */
+  {"kjp",	HB_TAG('K','J','P',' ')},	/* Pwo Eastern Karen */
   {"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 */
-  {"kmb",	HB_TAG('M','B','N',' ')},	/* [North] Mbundu */
+  {"kmb",	HB_TAG('M','B','N',' ')},	/* Kimbundu */
   {"kmw",	HB_TAG('K','M','O',' ')},	/* Komo (Democratic Republic of Congo) */
   {"kn",	HB_TAG('K','A','N',' ')},	/* Kannada */
+  {"knn",	HB_TAG('K','O','K',' ')},	/* Konkani */
   {"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 */
+  {"kok",	HB_TAG('K','O','K',' ')},	/* Konkani [macrolanguage] */
+  {"kpe",	HB_TAG('K','P','L',' ')},	/* Kpelle [macrolanguage] */
   {"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 */
+  {"kr",	HB_TAG('K','N','R',' ')},	/* Kanuri [macrolanguage] */
   {"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 */
+  {"ksh",	HB_TAG('K','S','H',' ')},	/* Kölsch */
+/*{"ksw",	HB_TAG('K','R','N',' ')},*/	/* S'gaw Karen (Microsoft fonts?) */
+  {"ksw",	HB_TAG('K','S','W',' ')},	/* S'gaw Karen (OpenType spec and SIL fonts) */
+  {"ku",	HB_TAG('K','U','R',' ')},	/* Kurdish [macrolanguage] */
   {"kum",	HB_TAG('K','U','M',' ')},	/* Kumyk */
+  {"kv",	HB_TAG('K','O','M',' ')},	/* Komi [macrolanguage] */
   {"kvd",	HB_TAG('K','U','I',' ')},	/* Kui (Indonesia) */
+  {"kw",	HB_TAG('C','O','R',' ')},	/* Cornish */
   {"kxc",	HB_TAG('K','M','S',' ')},	/* Komso */
   {"kxu",	HB_TAG('K','U','I',' ')},	/* Kui (India) */
-  {"ky",	HB_TAG('K','I','R',' ')},	/* Kirghiz */
+  {"ky",	HB_TAG('K','I','R',' ')},	/* Kirghiz/Kyrgyz */
+  {"kyu",	HB_TAG('K','Y','U',' ')},	/* Western Kayah */
   {"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 */
   {"lez",	HB_TAG('L','E','Z',' ')},	/* Lezgi */
-  {"lg",	HB_TAG('L','U','G',' ')},	/* Luganda */
+  {"lg",	HB_TAG('L','U','G',' ')},	/* Ganda */
+  {"li",	HB_TAG('L','I','M',' ')},	/* Limburgan/Limburger/Limburgish */
   {"lif",	HB_TAG('L','M','B',' ')},	/* Limbu */
+  {"lij",	HB_TAG('L','I','J',' ')},	/* Ligurian */
+  {"lis",	HB_TAG('L','I','S',' ')},	/* Lisu */
+  {"ljp",	HB_TAG('L','J','P',' ')},	/* Lampung Api */
+  {"lki",	HB_TAG('L','K','I',' ')},	/* Laki */
   {"lld",	HB_TAG('L','A','D',' ')},	/* Ladin */
   {"lmn",	HB_TAG('L','A','M',' ')},	/* Lambani */
+  {"lmo",	HB_TAG('L','M','O',' ')},	/* Lombard */
   {"ln",	HB_TAG('L','I','N',' ')},	/* Lingala */
   {"lo",	HB_TAG('L','A','O',' ')},	/* Lao */
+  {"lrc",	HB_TAG('L','R','C',' ')},	/* Northern Luri */
   {"lt",	HB_TAG('L','T','H',' ')},	/* Lithuanian */
   {"lu",	HB_TAG('L','U','B',' ')},	/* Luba-Katanga */
   {"lua",	HB_TAG('L','U','B',' ')},	/* Luba-Kasai */
   {"luo",	HB_TAG('L','U','O',' ')},	/* Luo (Kenya and Tanzania) */
   {"lus",	HB_TAG('M','I','Z',' ')},	/* Mizo */
-  {"luy",	HB_TAG('L','U','H',' ')},	/* Luhya [macrolanguage] */
+  {"luy",	HB_TAG('L','U','H',' ')},	/* Luyia/Oluluyia [macrolanguage] */
+  {"luz",	HB_TAG('L','R','C',' ')},	/* Southern Luri */
   {"lv",	HB_TAG('L','V','I',' ')},	/* Latvian */
   {"lzz",	HB_TAG('L','A','Z',' ')},	/* Laz */
+  {"mad",	HB_TAG('M','A','D',' ')},	/* Madurese */
+  {"mag",	HB_TAG('M','A','G',' ')},	/* Magahi */
   {"mai",	HB_TAG('M','T','H',' ')},	/* Maithili */
+  {"mak",	HB_TAG('M','K','R',' ')},	/* Makasar */
+  {"man",	HB_TAG('M','N','K',' ')},	/* Manding/Mandingo [macrolanguage] */
   {"mdc",	HB_TAG('M','L','E',' ')},	/* Male (Papua New Guinea) */
   {"mdf",	HB_TAG('M','O','K',' ')},	/* Moksha */
+  {"mdr",	HB_TAG('M','D','R',' ')},	/* Mandar */
   {"mdy",	HB_TAG('M','L','E',' ')},	/* Male (Ethiopia) */
   {"men",	HB_TAG('M','D','E',' ')},	/* Mende (Sierra Leone) */
-  {"mg",	HB_TAG('M','L','G',' ')},	/* Malagasy */
+  {"mer",	HB_TAG('M','E','R',' ')},	/* Meru */
+  {"mfe",	HB_TAG('M','F','E',' ')},	/* Morisyen */
+  {"mg",	HB_TAG('M','L','G',' ')},	/* Malagasy [macrolanguage] */
+  {"mh",	HB_TAG('M','A','H',' ')},	/* Marshallese */
   {"mhr",	HB_TAG('L','M','A',' ')},	/* Low Mari */
   {"mi",	HB_TAG('M','R','I',' ')},	/* Maori */
+  {"min",	HB_TAG('M','I','N',' ')},	/* Minangkabau */
   {"mk",	HB_TAG('M','K','D',' ')},	/* Macedonian */
+  {"mku",	HB_TAG('M','N','K',' ')},	/* Konyanka Maninka */
+  {"mkw",	HB_TAG('M','K','W',' ')},	/* Kituba (Congo) */
   {"ml",	HB_TAG('M','L','R',' ')},	/* Malayalam */
-  {"mn",	HB_TAG('M','N','G',' ')},	/* Mongolian */
+  {"mlq",	HB_TAG('M','N','K',' ')},	/* Western Maninkakan */
+  {"mn",	HB_TAG('M','N','G',' ')},	/* Mongolian [macrolanguage] */
   {"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 */
+  {"mos",	HB_TAG('M','O','S',' ')},	/* Mossi */
   {"mpe",	HB_TAG('M','A','J',' ')},	/* Majang */
   {"mr",	HB_TAG('M','A','R',' ')},	/* Marathi */
   {"mrj",	HB_TAG('H','M','A',' ')},	/* High Mari */
-  {"ms",	HB_TAG('M','L','Y',' ')},	/* Malay */
+  {"ms",	HB_TAG('M','L','Y',' ')},	/* Malay [macrolanguage] */
+  {"msc",	HB_TAG('M','N','K',' ')},	/* Sankaran Maninka */
   {"mt",	HB_TAG('M','T','S',' ')},	/* Maltese */
-  {"mwr",	HB_TAG('M','A','W',' ')},	/* Marwari */
+  {"mtr",	HB_TAG('M','A','W',' ')},	/* Mewari */
+  {"mus",	HB_TAG('M','U','S',' ')},	/* Creek */
+  {"mve",	HB_TAG('M','A','W',' ')},	/* Marwari (Pakistan) */
+  {"mwk",	HB_TAG('M','N','K',' ')},	/* Kita Maninkakan */
+  {"mwl",	HB_TAG('M','W','L',' ')},	/* Mirandese */
+  {"mwr",	HB_TAG('M','A','W',' ')},	/* Marwari [macrolanguage] */
+  {"mww",	HB_TAG('M','W','W',' ')},	/* Hmong Daw */
   {"my",	HB_TAG('B','R','M',' ')},	/* Burmese */
   {"mym",	HB_TAG('M','E','N',' ')},	/* Me'en */
+  {"myq",	HB_TAG('M','N','K',' ')},	/* Forest Maninka (retired code) */
   {"myv",	HB_TAG('E','R','Z',' ')},	/* Erzya */
+  {"mzn",	HB_TAG('M','Z','N',' ')},	/* Mazanderani */
+  {"na",	HB_TAG('N','A','U',' ')},	/* Nauru */
   {"nag",	HB_TAG('N','A','G',' ')},	/* Naga-Assamese */
+  {"nah",	HB_TAG('N','A','H',' ')},	/* Nahuatl [family] */
+  {"nap",	HB_TAG('N','A','P',' ')},	/* Neapolitan */
   {"nb",	HB_TAG('N','O','R',' ')},	/* Norwegian Bokmål */
   {"nco",	HB_TAG('S','I','B',' ')},	/* Sibe */
   {"nd",	HB_TAG('N','D','B',' ')},	/* [North] Ndebele */
+  {"ndc",	HB_TAG('N','D','C',' ')},	/* Ndau */
+  {"nds",	HB_TAG('N','D','S',' ')},	/* Low German/Low Saxon */
   {"ne",	HB_TAG('N','E','P',' ')},	/* Nepali */
   {"new",	HB_TAG('N','E','W',' ')},	/* Newari */
   {"ng",	HB_TAG('N','D','G',' ')},	/* Ndonga */
+  {"nga",	HB_TAG('N','G','A',' ')},	/* Ngabaka */
   {"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) */
-  {"nod",	HB_TAG('N','T','A',' ')},	/* Northern Tai */
+  {"no",	HB_TAG('N','O','R',' ')},	/* Norwegian [macrolanguage] */
+  {"nod",	HB_TAG('N','T','A',' ')},	/* Northern Thai */
+  {"noe",	HB_TAG('N','O','E',' ')},	/* Nimadi */
   {"nog",	HB_TAG('N','O','G',' ')},	/* Nogai */
+  {"nov",	HB_TAG('N','O','V',' ')},	/* Novial */
   {"nqo",	HB_TAG('N','K','O',' ')},	/* N'Ko */
   {"nr",	HB_TAG('N','D','B',' ')},	/* [South] Ndebele */
   {"nsk",	HB_TAG('N','A','S',' ')},	/* Naskapi */
   {"nso",	HB_TAG('S','O','T',' ')},	/* [Northern] Sotho */
-  {"ny",	HB_TAG('C','H','I',' ')},	/* Nyanja */
-  {"nyn",	HB_TAG('N','K','L',' ')},	/* Nkole */
+  {"ny",	HB_TAG('C','H','I',' ')},	/* Chewa/Chichwa/Nyanja */
+  {"nym",	HB_TAG('N','Y','M',' ')},	/* Nyamwezi */
+  {"nyn",	HB_TAG('N','K','L',' ')},	/* Nyankole */
   {"oc",	HB_TAG('O','C','I',' ')},	/* Occitan (post 1500) */
-  {"oj",	HB_TAG('O','J','B',' ')},	/* Ojibwa */
+  {"oj",	HB_TAG('O','J','B',' ')},	/* Ojibwa [macrolanguage] */
   {"ojs",	HB_TAG('O','C','R',' ')},	/* Oji-Cree */
-  {"om",	HB_TAG('O','R','O',' ')},	/* Oromo */
+  {"om",	HB_TAG('O','R','O',' ')},	/* Oromo [macrolanguage] */
   {"or",	HB_TAG('O','R','I',' ')},	/* Oriya */
   {"os",	HB_TAG('O','S','S',' ')},	/* Ossetian */
   {"pa",	HB_TAG('P','A','N',' ')},	/* Panjabi */
+  {"pag",	HB_TAG('P','A','G',' ')},	/* Pangasinan */
+  {"pam",	HB_TAG('P','A','M',' ')},	/* Kapampangan/Pampanga */
+  {"pap",	HB_TAG('P','A','P',' ')},	/* Papiamento */
+  {"pcc",	HB_TAG('P','C','C',' ')},	/* Bouyei */
+  {"pcd",	HB_TAG('P','C','D',' ')},	/* Picard */
   {"pce",	HB_TAG('P','L','G',' ')},	/* [Ruching] Palaung */
+  {"pdc",	HB_TAG('P','D','C',' ')},	/* Pennsylvania German */
+  {"pes",	HB_TAG('F','A','R',' ')},	/* Iranian Persian */
+  {"phk",	HB_TAG('P','H','K',' ')},	/* Phake */
   {"pi",	HB_TAG('P','A','L',' ')},	/* Pali */
+  {"pih",	HB_TAG('P','I','H',' ')},	/* Pitcairn-Norfolk */
   {"pl",	HB_TAG('P','L','K',' ')},	/* Polish */
   {"pll",	HB_TAG('P','L','G',' ')},	/* [Shwe] Palaung */
   {"plp",	HB_TAG('P','A','P',' ')},	/* Palpa */
-  {"prs",	HB_TAG('D','R','I',' ')},	/* Dari */
-  {"ps",	HB_TAG('P','A','S',' ')},	/* Pushto */
+  {"pms",	HB_TAG('P','M','S',' ')},	/* Piemontese */
+  {"pnb",	HB_TAG('P','N','B',' ')},	/* Western Panjabi */
+  {"prs",	HB_TAG('D','R','I',' ')},	/* Afghan Persian/Dari */
+  {"ps",	HB_TAG('P','A','S',' ')},	/* Pashto/Pushto [macrolanguage] */
   {"pt",	HB_TAG('P','T','G',' ')},	/* Portuguese */
-  {"raj",	HB_TAG('R','A','J',' ')},	/* Rajasthani */
-  {"rbb",	HB_TAG('P','L','G',' ')},	/* [Rumai] Palaung */
+  {"pwo",	HB_TAG('P','W','O',' ')},	/* Pwo Western Karen */
+  {"qu",	HB_TAG('Q','U','Z',' ')},	/* Quechua [macrolanguage] */
+  {"quc",	HB_TAG('Q','U','C',' ')},	/* K'iche'/Quiché */
+  {"quz",	HB_TAG('Q','U','Z',' ')},	/* Cusco Quechua */
+  {"raj",	HB_TAG('R','A','J',' ')},	/* Rajasthani [macrolanguage] */
+  {"rbb",	HB_TAG('P','L','G',' ')},	/* Rumai Palaung */
+  {"rej",	HB_TAG('R','E','J',' ')},	/* Rejang */
   {"ria",	HB_TAG('R','I','A',' ')},	/* Riang (India) */
   {"ril",	HB_TAG('R','I','A',' ')},	/* Riang (Myanmar) */
-  {"rki",	HB_TAG('A','R','K',' ')},	/* Arakanese */
-  {"rm",	HB_TAG('R','M','S',' ')},	/* Rhaeto-Romanic */
+  {"rki",	HB_TAG('A','R','K',' ')},	/* Rakhine */
+  {"rm",	HB_TAG('R','M','S',' ')},	/* Romansh */
+  {"rmy",	HB_TAG('R','M','Y',' ')},	/* Vlax Romani */
+  {"rn",	HB_TAG('R','U','N',' ')},	/* Rundi */
   {"ro",	HB_TAG('R','O','M',' ')},	/* Romanian */
-  {"rom",	HB_TAG('R','O','Y',' ')},	/* Romany */
+  {"rom",	HB_TAG('R','O','Y',' ')},	/* Romany [macrolanguage] */
   {"ru",	HB_TAG('R','U','S',' ')},	/* Russian */
   {"rue",	HB_TAG('R','S','Y',' ')},	/* Rusyn */
-  {"rw",	HB_TAG('R','U','A',' ')},	/* Ruanda */
+  {"rup",	HB_TAG('R','U','P',' ')},	/* Aromanian/Arumanian/Macedo-Romanian */
+  {"rw",	HB_TAG('R','U','A',' ')},	/* Kinyarwanda */
+  {"rwr",	HB_TAG('M','A','W',' ')},	/* Marwari (India) */
   {"sa",	HB_TAG('S','A','N',' ')},	/* Sanskrit */
   {"sah",	HB_TAG('Y','A','K',' ')},	/* Yakut */
+  {"sas",	HB_TAG('S','A','S',' ')},	/* Sasak */
   {"sat",	HB_TAG('S','A','T',' ')},	/* Santali */
   {"sck",	HB_TAG('S','A','D',' ')},	/* Sadri */
+  {"sc",	HB_TAG('S','R','D',' ')},	/* Sardinian [macrolanguage] */
+  {"scn",	HB_TAG('S','C','N',' ')},	/* Sicilian */
+  {"sco",	HB_TAG('S','C','O',' ')},	/* Scots */
   {"scs",	HB_TAG('S','L','A',' ')},	/* [North] Slavey */
   {"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 */
+  {"sga",	HB_TAG('S','G','A',' ')},	/* Old Irish (to 900) */
+  {"sgs",	HB_TAG('S','G','S',' ')},	/* Samogitian */
+  {"sgw",	HB_TAG('C','H','G',' ')},	/* Sebat Bet Gurage */
+/*{"sgw",	HB_TAG('S','G','W',' ')},*/	/* Sebat Bet Gurage (in SIL fonts) */
   {"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 */
+  {"sn",	HB_TAG('S','N','A',' ')},	/* Shona */
   {"snk",	HB_TAG('S','N','K',' ')},	/* Soninke */
   {"so",	HB_TAG('S','M','L',' ')},	/* Somali */
-  {"sq",	HB_TAG('S','Q','I',' ')},	/* Albanian */
+  {"sop",	HB_TAG('S','O','P',' ')},	/* Songe */
+  {"sq",	HB_TAG('S','Q','I',' ')},	/* Albanian [macrolanguage] */
   {"sr",	HB_TAG('S','R','B',' ')},	/* Serbian */
   {"srr",	HB_TAG('S','R','R',' ')},	/* Serer */
-  {"ss",	HB_TAG('S','W','Z',' ')},	/* Swazi */
+  {"ss",	HB_TAG('S','W','Z',' ')},	/* Swati */
   {"st",	HB_TAG('S','O','T',' ')},	/* [Southern] Sotho */
+  {"stq",	HB_TAG('S','T','Q',' ')},	/* Saterfriesisch */
+  {"stv",	HB_TAG('S','I','G',' ')},	/* Silt'e */
+  {"su",	HB_TAG('S','U','N',' ')},	/* Sundanese */
+  {"suk",	HB_TAG('S','U','K',' ')},	/* Sukama */
   {"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 */
+  {"sw",	HB_TAG('S','W','K',' ')},	/* Swahili [macrolanguage] */
   {"swb",	HB_TAG('C','M','R',' ')},	/* Comorian */
-  {"syr",	HB_TAG('S','Y','R',' ')},	/* Syriac */
+  {"swh",	HB_TAG('S','W','K',' ')},	/* Kiswahili/Swahili */
+  {"swv",	HB_TAG('M','A','W',' ')},	/* Shekhawati */
+  {"sxu",	HB_TAG('S','X','U',' ')},	/* Upper Saxon */
+  {"syl",	HB_TAG('S','Y','L',' ')},	/* Sylheti */
+  {"syr",	HB_TAG('S','Y','R',' ')},	/* Syriac [macrolanguage] */
+  {"szl",	HB_TAG('S','Z','L',' ')},	/* Silesian */
   {"ta",	HB_TAG('T','A','M',' ')},	/* Tamil */
   {"tab",	HB_TAG('T','A','B',' ')},	/* Tabasaran */
   {"tcy",	HB_TAG('T','U','L',' ')},	/* Tulu */
+  {"tdd",	HB_TAG('T','D','D',' ')},	/* Tai Nüa */
   {"te",	HB_TAG('T','E','L',' ')},	/* Telugu */
   {"tem",	HB_TAG('T','M','N',' ')},	/* Temne */
+  {"tet",	HB_TAG('T','E','T',' ')},	/* Tetum */
   {"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 */
+  {"tiv",	HB_TAG('T','I','V',' ')},	/* Tiv */
   {"tk",	HB_TAG('T','K','M',' ')},	/* Turkmen */
+  {"tl",	HB_TAG('T','G','L',' ')},	/* Tagalog */
+  {"tmh",	HB_TAG('t','m','h',' ')},	/* Tamashek [macrolanguage] */
   {"tn",	HB_TAG('T','N','A',' ')},	/* Tswana */
   {"to",	HB_TAG('T','G','N',' ')},	/* Tonga (Tonga Islands) */
+  {"tpi",	HB_TAG('T','P','I',' ')},	/* Tok Pisin */
   {"tr",	HB_TAG('T','R','K',' ')},	/* Turkish */
   {"tru",	HB_TAG('T','U','A',' ')},	/* Turoyo Aramaic */
   {"ts",	HB_TAG('T','S','G',' ')},	/* Tsonga */
   {"tt",	HB_TAG('T','A','T',' ')},	/* Tatar */
+  {"tum",	HB_TAG('T','U','M',' ')},	/* Tumbuka */
   {"tw",	HB_TAG('T','W','I',' ')},	/* Twi */
   {"ty",	HB_TAG('T','H','T',' ')},	/* Tahitian */
   {"tyv",	HB_TAG('T','U','V',' ')},	/* Tuvin */
+  {"tyz",	HB_TAG('T','Y','Z',' ')},	/* Tày */
+  {"tzm",	HB_TAG('T','Z','M',' ')},	/* Central Atlas Tamazight */
   {"udm",	HB_TAG('U','D','M',' ')},	/* Udmurt */
   {"ug",	HB_TAG('U','Y','G',' ')},	/* Uighur */
   {"uk",	HB_TAG('U','K','R',' ')},	/* Ukrainian */
-  {"umb",	HB_TAG('M','B','N',' ')},	/* [South] Mbundu */
+  {"umb",	HB_TAG('U','M','B',' ')},	/* Umbundu */
   {"unr",	HB_TAG('M','U','N',' ')},	/* Mundari */
   {"ur",	HB_TAG('U','R','D',' ')},	/* Urdu */
-  {"uz",	HB_TAG('U','Z','B',' ')},	/* Uzbek */
+  {"uz",	HB_TAG('U','Z','B',' ')},	/* Uzbek [macrolanguage] */
+  {"uzn",	HB_TAG('U','Z','B',' ')},	/* Northern Uzbek */
+  {"uzs",	HB_TAG('U','Z','B',' ')},	/* Southern Uzbek */
   {"ve",	HB_TAG('V','E','N',' ')},	/* Venda */
+  {"vec",	HB_TAG('V','E','C',' ')},	/* Venetian */
+  {"vls",	HB_TAG('F','L','E',' ')},	/* Vlaams */
   {"vi",	HB_TAG('V','I','T',' ')},	/* Vietnamese */
-  {"vmw",	HB_TAG('M','A','K',' ')},	/* Makua */
+  {"vmw",	HB_TAG('M','A','K',' ')},	/* Makhuwa */
+  {"vo",	HB_TAG('V','O','L',' ')},	/* Volapük */
+  {"vro",	HB_TAG('V','R','O',' ')},	/* Võro */
+  {"wa",	HB_TAG('W','L','N',' ')},	/* Walloon */
+  {"war",	HB_TAG('W','A','R',' ')},	/* Waray (Philippines) */
   {"wbm",	HB_TAG('W','A',' ',' ')},	/* Wa */
   {"wbr",	HB_TAG('W','A','G',' ')},	/* Wagdi */
+  {"wle",	HB_TAG('S','I','G',' ')},	/* Wolane */
+  {"wry",	HB_TAG('M','A','W',' ')},	/* Merwari */
+  {"wtm",	HB_TAG('W','T','M',' ')},	/* Mewati */
   {"wo",	HB_TAG('W','L','F',' ')},	/* Wolof */
   {"xal",	HB_TAG('K','L','M',' ')},	/* Kalmyk */
   {"xh",	HB_TAG('X','H','S',' ')},	/* Xhosa */
+  {"xog",	HB_TAG('X','O','G',' ')},	/* Soga */
   {"xom",	HB_TAG('K','M','O',' ')},	/* Komo (Sudan) */
   {"xsl",	HB_TAG('S','S','L',' ')},	/* South Slavey */
-  {"yi",	HB_TAG('J','I','I',' ')},	/* Yiddish */
+  {"xst",	HB_TAG('S','I','G',' ')},	/* Silt'e (retired code) */
+  {"xwo",	HB_TAG('T','O','D',' ')},	/* Written Oirat (Todo) */
+  {"yao",	HB_TAG('Y','A','O',' ')},	/* Yao */
+  {"yi",	HB_TAG('J','I','I',' ')},	/* Yiddish [macrolanguage] */
   {"yo",	HB_TAG('Y','B','A',' ')},	/* Yoruba */
   {"yso",	HB_TAG('N','I','S',' ')},	/* Nisi (China) */
+  {"za",	HB_TAG('Z','H','A',' ')},	/* Chuang/Zhuang [macrolanguage] */
+  {"zea",	HB_TAG('Z','E','A',' ')},	/* Zeeuws */
   {"zne",	HB_TAG('Z','N','D',' ')},	/* Zande */
-  {"zu",	HB_TAG('Z','U','L',' ')} 	/* Zulu */
+  {"zu",	HB_TAG('Z','U','L',' ')}, 	/* Zulu */
+  {"zum",	HB_TAG('L','R','C',' ')}	/* Kumzari */
 
   /* The corresponding languages IDs for the following IDs are unclear,
    * overlap, or are architecturally weird. Needs more research. */
 
 /*{"ahg/awn/xan?",	HB_TAG('A','G','W',' ')},*/	/* Agaw */
 /*{"gsw?/gsw-FR?",	HB_TAG('A','L','S',' ')},*/	/* Alsatian */
 /*{"krc",	HB_TAG('B','A','L',' ')},*/	/* Balkar */
 /*{"??",	HB_TAG('B','C','R',' ')},*/	/* Bible Cree */
-/*{"sgw?",	HB_TAG('C','H','G',' ')},*/	/* Chaha Gurage */
+/*{"zh?",	HB_TAG('C','H','N',' ')},*/	/* Chinese (seen in Microsoft fonts) */
 /*{"acf/gcf?",	HB_TAG('F','A','N',' ')},*/	/* French Antillean */
-/*{"vls/nl-be",	HB_TAG('F','L','E',' ')},*/	/* Flemish */
 /*{"enf?/yrk?",	HB_TAG('F','N','E',' ')},*/	/* Forest Nenets */
 /*{"fuf?",	HB_TAG('F','T','A',' ')},*/	/* Futa */
 /*{"ar-Syrc?",	HB_TAG('G','A','R',' ')},*/	/* Garshuni */
 /*{"cfm/rnl?",	HB_TAG('H','A','L',' ')},*/	/* Halam */
+/*{"fonipa",	HB_TAG('I','P','P','H')},*/	/* Phonetic transcription—IPA conventions */
 /*{"ga-Latg?/Latg?",	HB_TAG('I','R','T',' ')},*/	/* Irish Traditional */
 /*{"krc",	HB_TAG('K','A','R',' ')},*/	/* Karachay */
 /*{"alw?/ktb?",	HB_TAG('K','E','B',' ')},*/	/* Kebena */
 /*{"Geok",	HB_TAG('K','G','E',' ')},*/	/* Khutsuri Georgian */
 /*{"kca",	HB_TAG('K','H','K',' ')},*/	/* Khanty-Kazim */
 /*{"kca",	HB_TAG('K','H','S',' ')},*/	/* Khanty-Shurishkar */
 /*{"kca",	HB_TAG('K','H','V',' ')},*/	/* Khanty-Vakhi */
 /*{"guz?/kqs?/kss?",	HB_TAG('K','I','S',' ')},*/	/* Kisii */
 /*{"kfa/kfi?/kpb?/xua?/xuj?",	HB_TAG('K','O','D',' ')},*/	/* Kodagu */
 /*{"okm?/oko?",	HB_TAG('K','O','H',' ')},*/	/* Korean Old Hangul */
 /*{"kon?/ktu?/...",	HB_TAG('K','O','N',' ')},*/	/* Kikongo */
 /*{"kfx?",	HB_TAG('K','U','L',' ')},*/	/* Kulvi */
 /*{"??",	HB_TAG('L','A','H',' ')},*/	/* Lahuli */
 /*{"??",	HB_TAG('L','C','R',' ')},*/	/* L-Cree */
 /*{"??",	HB_TAG('M','A','L',' ')},*/	/* Malayalam Traditional */
 /*{"mnk?/mlq?/...",	HB_TAG('M','L','N',' ')},*/	/* Malinke */
-/*{"man?/myq?/mku?/msc?/...",	HB_TAG('M','N','K',' ')},*/	/* Maninka */
-/*{"??",	HB_TAG('M','O','R',' ')},*/	/* Moroccan */
 /*{"??",	HB_TAG('N','C','R',' ')},*/	/* N-Cree */
 /*{"??",	HB_TAG('N','H','C',' ')},*/	/* Norway House Cree */
 /*{"jpa?/sam?",	HB_TAG('P','A','A',' ')},*/	/* Palestinian Aramaic */
 /*{"polyton",	HB_TAG('P','G','R',' ')},*/	/* Polytonic Greek */
 /*{"??",	HB_TAG('Q','I','N',' ')},*/	/* Asho Chin */
 /*{"??",	HB_TAG('R','C','R',' ')},*/	/* R-Cree */
 /*{"chp?",	HB_TAG('S','A','Y',' ')},*/	/* Sayisi */
 /*{"xan?",	HB_TAG('S','E','K',' ')},*/	/* Sekota */
-/*{"stv/wle?/xst?",	HB_TAG('S','I','G',' ')},*/	/* Silte Gurage */
 /*{"ngo?",	HB_TAG('S','X','T',' ')},*/	/* Sutu */
 /*{"??",	HB_TAG('T','C','R',' ')},*/	/* TH-Cree */
 /*{"tnz?/tog?/toi?",	HB_TAG('T','N','G',' ')},*/	/* Tonga */
 /*{"enh?/yrk?",	HB_TAG('T','N','E',' ')},*/	/* Tundra Nenets */
-/*{"??",	HB_TAG('T','O','D',' ')},*/	/* Todo */
 /*{"??",	HB_TAG('W','C','R',' ')},*/	/* West-Cree */
-/*{"??",	HB_TAG('Y','C','R',' ')},*/	/* Y-Cree */
+/*{"cre?",	HB_TAG('Y','C','R',' ')},*/	/* Y-Cree */
 /*{"??",	HB_TAG('Y','I','C',' ')},*/	/* Yi Classic */
 /*{"ii?/Yiii?",	HB_TAG('Y','I','M',' ')},*/	/* Yi Modern */
 /*{"??",	HB_TAG('Z','H','P',' ')},*/	/* Chinese Phonetic */
 };
 
 static const LangTag ot_languages_zh[] = {
   {"zh-cn",	HB_TAG('Z','H','S',' ')},	/* Chinese (China) */
   {"zh-hk",	HB_TAG('Z','H','H',' ')},	/* Chinese (Hong Kong) */
--- a/gfx/harfbuzz/src/hb-private.hh
+++ b/gfx/harfbuzz/src/hb-private.hh
@@ -74,16 +74,19 @@ typedef const _hb_void_t &hb_void_t;
 #undef MIN
 template <typename Type>
 static inline Type MIN (const Type &a, const Type &b) { return a < b ? a : b; }
 
 #undef MAX
 template <typename Type>
 static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
 
+static inline unsigned int DIV_CEIL (const unsigned int a, unsigned int b)
+{ return (a + (b - 1)) / b; }
+
 
 #undef  ARRAY_LENGTH
 template <typename Type, unsigned int n>
 static inline unsigned int ARRAY_LENGTH (const Type (&)[n]) { return n; }
 /* A const version, but does not detect erratically being called on pointers. */
 #define ARRAY_LENGTH_CONST(__array) ((signed int) (sizeof (__array) / sizeof (__array[0])))
 
 #define HB_STMT_START do
--- a/gfx/harfbuzz/src/hb-shape-plan-private.hh
+++ b/gfx/harfbuzz/src/hb-shape-plan-private.hh
@@ -34,22 +34,25 @@
 
 
 struct hb_shape_plan_t
 {
   hb_object_header_t header;
   ASSERT_POD ();
 
   hb_bool_t default_shaper_list;
-  hb_face_t *face;
+  hb_face_t *face_unsafe; /* We don't carry a reference to face. */
   hb_segment_properties_t props;
 
   hb_shape_func_t *shaper_func;
   const char *shaper_name;
 
+  hb_feature_t *user_features;
+  unsigned int num_user_features;
+
   struct hb_shaper_data_t shaper_data;
 };
 
 #define HB_SHAPER_DATA_CREATE_FUNC_EXTRA_ARGS \
 	, const hb_feature_t            *user_features \
 	, unsigned int                   num_user_features
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_PROTOTYPE(shaper, shape_plan);
 #include "hb-shaper-list.hh"
--- a/gfx/harfbuzz/src/hb-shape-plan.cc
+++ b/gfx/harfbuzz/src/hb-shape-plan.cc
@@ -41,17 +41,17 @@ hb_shape_plan_plan (hb_shape_plan_t    *
 		    const hb_feature_t *user_features,
 		    unsigned int        num_user_features,
 		    const char * const *shaper_list)
 {
   const hb_shaper_pair_t *shapers = _hb_shapers_get ();
 
 #define HB_SHAPER_PLAN(shaper) \
 	HB_STMT_START { \
-	  if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face)) { \
+	  if (hb_##shaper##_shaper_face_data_ensure (shape_plan->face_unsafe)) { \
 	    HB_SHAPER_DATA (shaper, shape_plan) = \
 	      HB_SHAPER_DATA_CREATE_FUNC (shaper, shape_plan) (shape_plan, user_features, num_user_features); \
 	    shape_plan->shaper_func = _hb_##shaper##_shape; \
 	    shape_plan->shaper_name = #shaper; \
 	    return; \
 	  } \
 	} HB_STMT_END
 
@@ -102,28 +102,37 @@ hb_shape_plan_create (hb_face_t         
 		      const hb_segment_properties_t *props,
 		      const hb_feature_t            *user_features,
 		      unsigned int                   num_user_features,
 		      const char * const            *shaper_list)
 {
   assert (props->direction != HB_DIRECTION_INVALID);
 
   hb_shape_plan_t *shape_plan;
+  hb_feature_t *features = NULL;
 
   if (unlikely (!face))
     face = hb_face_get_empty ();
   if (unlikely (!props || hb_object_is_inert (face)))
     return hb_shape_plan_get_empty ();
-  if (!(shape_plan = hb_object_create<hb_shape_plan_t> ()))
+  if (num_user_features && !(features = (hb_feature_t *) malloc (num_user_features * sizeof (hb_feature_t))))
     return hb_shape_plan_get_empty ();
+  if (!(shape_plan = hb_object_create<hb_shape_plan_t> ())) {
+    free (features);
+    return hb_shape_plan_get_empty ();
+  }
 
   hb_face_make_immutable (face);
   shape_plan->default_shaper_list = shaper_list == NULL;
-  shape_plan->face = hb_face_reference (face);
+  shape_plan->face_unsafe = face;
   shape_plan->props = *props;
+  shape_plan->num_user_features = num_user_features;
+  shape_plan->user_features = features;
+  if (num_user_features)
+    memcpy (features, user_features, num_user_features * sizeof (hb_feature_t));
 
   hb_shape_plan_plan (shape_plan, user_features, num_user_features, shaper_list);
 
   return shape_plan;
 }
 
 /**
  * hb_shape_plan_get_empty:
@@ -142,16 +151,19 @@ hb_shape_plan_get_empty (void)
 
     true, /* default_shaper_list */
     NULL, /* face */
     HB_SEGMENT_PROPERTIES_DEFAULT, /* props */
 
     NULL, /* shaper_func */
     NULL, /* shaper_name */
 
+    NULL, /* user_features */
+    0,    /* num_user_featurs */
+
     {
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_INVALID,
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
     }
   };
 
   return const_cast<hb_shape_plan_t *> (&_hb_shape_plan_nil);
@@ -185,17 +197,17 @@ void
 hb_shape_plan_destroy (hb_shape_plan_t *shape_plan)
 {
   if (!hb_object_destroy (shape_plan)) return;
 
 #define HB_SHAPER_IMPLEMENT(shaper) HB_SHAPER_DATA_DESTROY(shaper, shape_plan);
 #include "hb-shaper-list.hh"
 #undef HB_SHAPER_IMPLEMENT
 
-  hb_face_destroy (shape_plan->face);
+  free (shape_plan->user_features);
 
   free (shape_plan);
 }
 
 /**
  * hb_shape_plan_set_user_data: (skip)
  * @shape_plan: a shape plan.
  * @key: 
@@ -259,17 +271,17 @@ hb_shape_plan_execute (hb_shape_plan_t  
 		       const hb_feature_t *features,
 		       unsigned int        num_features)
 {
   if (unlikely (hb_object_is_inert (shape_plan) ||
 		hb_object_is_inert (font) ||
 		hb_object_is_inert (buffer)))
     return false;
 
-  assert (shape_plan->face == font->face);
+  assert (shape_plan->face_unsafe == font->face);
   assert (hb_segment_properties_equal (&shape_plan->props, &buffer->props));
 
 #define HB_SHAPER_EXECUTE(shaper) \
 	HB_STMT_START { \
 	  return HB_SHAPER_DATA (shaper, shape_plan) && \
 		 hb_##shaper##_shaper_font_data_ensure (font) && \
 		 _hb_##shaper##_shape (shape_plan, font, buffer, features, num_features); \
 	} HB_STMT_END
@@ -296,33 +308,65 @@ hb_shape_plan_execute (hb_shape_plan_t  
 static unsigned int
 hb_shape_plan_hash (const hb_shape_plan_t *shape_plan)
 {
   return hb_segment_properties_hash (&shape_plan->props) +
 	 shape_plan->default_shaper_list ? 0 : (intptr_t) shape_plan->shaper_func;
 }
 #endif
 
-/* TODO no user-feature caching for now. */
+/* User-feature caching is currently somewhat dumb:
+ * it only finds matches where the feature array is identical,
+ * not cases where the feature lists would be compatible for plan purposes
+ * but have different ranges, for example.
+ */
 struct hb_shape_plan_proposal_t
 {
   const hb_segment_properties_t  props;
   const char * const            *shaper_list;
+  const hb_feature_t            *user_features;
+  unsigned int                   num_user_features;
   hb_shape_func_t               *shaper_func;
 };
 
+static inline hb_bool_t
+hb_shape_plan_user_features_match (const hb_shape_plan_t          *shape_plan,
+				   const hb_shape_plan_proposal_t *proposal)
+{
+  if (proposal->num_user_features != shape_plan->num_user_features) return false;
+  for (unsigned int i = 0, n = proposal->num_user_features; i < n; i++)
+    if (proposal->user_features[i].tag   != shape_plan->user_features[i].tag   ||
+        proposal->user_features[i].value != shape_plan->user_features[i].value ||
+        proposal->user_features[i].start != shape_plan->user_features[i].start ||
+        proposal->user_features[i].end   != shape_plan->user_features[i].end) return false;
+  return true;
+}
+
 static hb_bool_t
 hb_shape_plan_matches (const hb_shape_plan_t          *shape_plan,
 		       const hb_shape_plan_proposal_t *proposal)
 {
   return hb_segment_properties_equal (&shape_plan->props, &proposal->props) &&
+	 hb_shape_plan_user_features_match (shape_plan, proposal) &&
 	 ((shape_plan->default_shaper_list && proposal->shaper_list == NULL) ||
 	  (shape_plan->shaper_func == proposal->shaper_func));
 }
 
+static inline hb_bool_t
+hb_non_global_user_features_present (const hb_feature_t *user_features,
+				     unsigned int        num_user_features)
+{
+  while (num_user_features)
+    if (user_features->start != 0 || user_features->end != (unsigned int) -1)
+      return true;
+    else
+      num_user_features--, user_features++;
+  return false;
+}
+
 /**
  * hb_shape_plan_create_cached:
  * @face: 
  * @props: 
  * @user_features: (array length=num_user_features):
  * @num_user_features: 
  * @shaper_list: (array zero-terminated=1):
  *
@@ -334,22 +378,21 @@ hb_shape_plan_matches (const hb_shape_pl
  **/
 hb_shape_plan_t *
 hb_shape_plan_create_cached (hb_face_t                     *face,
 			     const hb_segment_properties_t *props,
 			     const hb_feature_t            *user_features,
 			     unsigned int                   num_user_features,
 			     const char * const            *shaper_list)
 {
-  if (num_user_features)
-    return hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list);
-
   hb_shape_plan_proposal_t proposal = {
     *props,
     shaper_list,
+    user_features,
+    num_user_features,
     NULL
   };
 
   if (shaper_list) {
     /* Choose shaper.  Adapted from hb_shape_plan_plan(). */
 #define HB_SHAPER_PLAN(shaper) \
 	  HB_STMT_START { \
 	    if (hb_##shaper##_shaper_face_data_ensure (face)) \
@@ -377,32 +420,34 @@ retry:
   for (hb_face_t::plan_node_t *node = cached_plan_nodes; node; node = node->next)
     if (hb_shape_plan_matches (node->shape_plan, &proposal))
       return hb_shape_plan_reference (node->shape_plan);
 
   /* Not found. */
 
   hb_shape_plan_t *shape_plan = hb_shape_plan_create (face, props, user_features, num_user_features, shaper_list);
 
+  /* Don't add the plan to the cache if there were user features with non-global ranges */
+
+  if (hb_non_global_user_features_present (user_features, num_user_features))
+    return shape_plan;
+
   hb_face_t::plan_node_t *node = (hb_face_t::plan_node_t *) calloc (1, sizeof (hb_face_t::plan_node_t));
   if (unlikely (!node))
     return shape_plan;
 
   node->shape_plan = shape_plan;
   node->next = cached_plan_nodes;
 
   if (!hb_atomic_ptr_cmpexch (&face->shape_plans, cached_plan_nodes, node)) {
     hb_shape_plan_destroy (shape_plan);
     free (node);
     goto retry;
   }
 
-  /* Release our reference on face. */
-  hb_face_destroy (face);
-
   return hb_shape_plan_reference (shape_plan);
 }
 
 /**
  * hb_shape_plan_get_shaper:
  * @shape_plan: a shape plan.
  *
  * 
--- a/gfx/harfbuzz/src/hb-uniscribe.cc
+++ b/gfx/harfbuzz/src/hb-uniscribe.cc
@@ -724,44 +724,46 @@ hb_bool_t
     return false; \
   } HB_STMT_END;
 
   HRESULT hr;
 
 retry:
 
   unsigned int scratch_size;
-  char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size);
-
-  /* Allocate char buffers; they all fit */
+  hb_buffer_t::scratch_buffer_t *scratch = buffer->get_scratch_buffer (&scratch_size);
 
 #define ALLOCATE_ARRAY(Type, name, len) \
   Type *name = (Type *) scratch; \
-  scratch += (len) * sizeof ((name)[0]); \
-  scratch_size -= (len) * sizeof ((name)[0]);
+  { \
+    unsigned int _consumed = DIV_CEIL ((len) * sizeof (Type), sizeof (*scratch)); \
+    assert (_consumed <= scratch_size); \
+    scratch += _consumed; \
+    scratch_size -= _consumed; \
+  }
 
 #define utf16_index() var1.u32
 
-  WCHAR *pchars = (WCHAR *) scratch;
+  ALLOCATE_ARRAY (WCHAR, pchars, buffer->len * 2);
+
   unsigned int chars_len = 0;
   for (unsigned int i = 0; i < buffer->len; i++)
   {
     hb_codepoint_t c = buffer->info[i].codepoint;
     buffer->info[i].utf16_index() = chars_len;
     if (likely (c < 0x10000))
       pchars[chars_len++] = c;
     else if (unlikely (c >= 0x110000))
       pchars[chars_len++] = 0xFFFD;
     else {
       pchars[chars_len++] = 0xD800 + ((c - 0x10000) >> 10);
       pchars[chars_len++] = 0xDC00 + ((c - 0x10000) & ((1 << 10) - 1));
     }
   }
 
-  ALLOCATE_ARRAY (WCHAR, wchars, chars_len);
   ALLOCATE_ARRAY (WORD, log_clusters, chars_len);
   ALLOCATE_ARRAY (SCRIPT_CHARPROP, char_props, chars_len);
 
   if (num_features)
   {
     /* Need log_clusters to assign features. */
     chars_len = 0;
     for (unsigned int i = 0; i < buffer->len; i++)
@@ -769,22 +771,23 @@ retry:
       hb_codepoint_t c = buffer->info[i].codepoint;
       unsigned int cluster = buffer->info[i].cluster;
       log_clusters[chars_len++] = cluster;
       if (c >= 0x10000 && c < 0x110000)
 	log_clusters[chars_len++] = cluster; /* Surrogates. */
     }
   }
 
-  /* On Windows, we don't care about alignment...*/
-  unsigned int glyphs_size = scratch_size / (sizeof (WORD) +
-					     sizeof (SCRIPT_GLYPHPROP) +
-					     sizeof (int) +
-					     sizeof (GOFFSET) +
-					     sizeof (uint32_t));
+  /* All the following types are sized in multiples of sizeof(int). */
+  unsigned int glyphs_size = scratch_size / ((sizeof (WORD) +
+					      sizeof (SCRIPT_GLYPHPROP) +
+					      sizeof (int) +
+					      sizeof (GOFFSET) +
+					      sizeof (uint32_t))
+					     / sizeof (int));
 
   ALLOCATE_ARRAY (WORD, glyphs, glyphs_size);
   ALLOCATE_ARRAY (SCRIPT_GLYPHPROP, glyph_props, glyphs_size);
   ALLOCATE_ARRAY (int, advances, glyphs_size);
   ALLOCATE_ARRAY (GOFFSET, offsets, glyphs_size);
   ALLOCATE_ARRAY (uint32_t, vis_clusters, glyphs_size);
 
   /* Note:
@@ -807,17 +810,17 @@ retry:
 
   /* MinGW32 doesn't define fMergeNeutralItems, so we bruteforce */
   //bidi_control.fMergeNeutralItems = true;
   *(uint32_t*)&bidi_control |= 1<<24;
 
   bidi_state.uBidiLevel = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1;
   bidi_state.fOverrideDirection = 1;
 
-  hr = funcs->ScriptItemizeOpenType (wchars,
+  hr = funcs->ScriptItemizeOpenType (pchars,
 				     chars_len,
 				     MAX_ITEMS,
 				     &bidi_control,
 				     &bidi_state,
 				     items,
 				     script_tags,
 				     &item_count);
   if (unlikely (FAILED (hr)))
@@ -882,17 +885,17 @@ retry:
     hr = funcs->ScriptShapeOpenType (font_data->hdc,
 				     &font_data->script_cache,
 				     &items[i].a,
 				     script_tags[i],
 				     language_tag,
 				     range_char_counts.array,
 				     range_properties.array,
 				     range_properties.len,
-				     wchars + chars_offset,
+				     pchars + chars_offset,
 				     item_chars_len,
 				     glyphs_size - glyphs_offset,
 				     /* out */
 				     log_clusters + chars_offset,
 				     char_props + chars_offset,
 				     glyphs + glyphs_offset,
 				     glyph_props + glyphs_offset,
 				     (int *) &glyphs_len);
@@ -924,17 +927,17 @@ retry:
     hr = funcs->ScriptPlaceOpenType (font_data->hdc,
 				     &font_data->script_cache,
 				     &items[i].a,
 				     script_tags[i],
 				     language_tag,
 				     range_char_counts.array,
 				     range_properties.array,
 				     range_properties.len,
-				     wchars + chars_offset,
+				     pchars + chars_offset,
 				     log_clusters + chars_offset,
 				     char_props + chars_offset,
 				     item_chars_len,
 				     glyphs + glyphs_offset,
 				     glyph_props + glyphs_offset,
 				     glyphs_len,
 				     /* out */
 				     advances + glyphs_offset,
--- a/gfx/harfbuzz/src/hb-version.h
+++ b/gfx/harfbuzz/src/hb-version.h
@@ -33,19 +33,19 @@
 
 #include "hb-common.h"
 
 HB_BEGIN_DECLS
 
 
 #define HB_VERSION_MAJOR 0
 #define HB_VERSION_MINOR 9
-#define HB_VERSION_MICRO 23
+#define HB_VERSION_MICRO 24
 
-#define HB_VERSION_STRING "0.9.23"
+#define HB_VERSION_STRING "0.9.24"
 
 #define HB_VERSION_CHECK(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) >= \
 	 HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
 
 
 void
 hb_version (unsigned int *major,