Bug 1382083 - Update harfbuzz to version 1.4.7. r=jfkthame
authorRyan VanderMeulen <ryanvm@gmail.com>
Wed, 19 Jul 2017 12:50:00 -0400
changeset 418686 1440d943e924df9a132d1d1e73974cc9bfc8695f
parent 418685 c72f79ecb0dda81850fd2434db9f58b60da51d29
child 418687 e4e7f0468271b6bdc8c703d1d52c9de6e2325be3
push id7566
push usermtabara@mozilla.com
push dateWed, 02 Aug 2017 08:25:16 +0000
treeherdermozilla-beta@86913f512c3c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjfkthame
bugs1382083
milestone56.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 1382083 - Update harfbuzz to version 1.4.7. r=jfkthame
gfx/harfbuzz/Makefile.am
gfx/harfbuzz/NEWS
gfx/harfbuzz/README-mozilla
gfx/harfbuzz/configure.ac
gfx/harfbuzz/src/gen-arabic-table.py
gfx/harfbuzz/src/gen-indic-table.py
gfx/harfbuzz/src/gen-use-table.py
gfx/harfbuzz/src/harfbuzz-icu.pc
gfx/harfbuzz/src/harfbuzz.pc
gfx/harfbuzz/src/hb-atomic-private.hh
gfx/harfbuzz/src/hb-buffer.cc
gfx/harfbuzz/src/hb-common.cc
gfx/harfbuzz/src/hb-coretext.cc
gfx/harfbuzz/src/hb-ot-font.cc
gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
gfx/harfbuzz/src/hb-ot-layout-private.hh
gfx/harfbuzz/src/hb-ot-layout.cc
gfx/harfbuzz/src/hb-ot-map-private.hh
gfx/harfbuzz/src/hb-ot-map.cc
gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh
gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
gfx/harfbuzz/src/hb-ot-shape-complex-indic-table.cc
gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc
gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.hh
gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.rl
gfx/harfbuzz/src/hb-ot-shape-complex-use-table.cc
gfx/harfbuzz/src/hb-private.hh
gfx/harfbuzz/src/hb-unicode-private.hh
gfx/harfbuzz/src/hb-utf-private.hh
gfx/harfbuzz/src/hb-version.h
--- a/gfx/harfbuzz/Makefile.am
+++ b/gfx/harfbuzz/Makefile.am
@@ -6,16 +6,17 @@ ACLOCAL_AMFLAGS = -I m4
 
 SUBDIRS = src util test docs win32
 
 EXTRA_DIST = \
 	autogen.sh \
 	harfbuzz.doap \
 	README.python \
 	BUILD.md \
+	CMakeLists.txt \
 	$(NULL)
 
 MAINTAINERCLEANFILES = \
 	$(GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL) \
 	$(GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL) \
 	$(GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN) \
 	$(srcdir)/INSTALL \
 	$(srcdir)/ChangeLog \
--- a/gfx/harfbuzz/NEWS
+++ b/gfx/harfbuzz/NEWS
@@ -1,8 +1,27 @@
+Overview of changes leading to 1.4.7
+Tuesday, July 18, 2017
+====================================
+
+- Multiple Indic, Tibetan, and Cham fixes.
+- CoreText: Allow disabling kerning.
+- Adjust Arabic feature order again.
+- Misc build fixes.
+
+
+Overview of changes leading to 1.4.6
+Sunday, April 23, 2017
+====================================
+
+- Graphite2: Fix RTL positioning issue.
+- Backlist GDEF of more versions of Padauk and Tahoma.
+- New, experimental, cmake alternative build system.
+
+
 Overview of changes leading to 1.4.5
 Friday, March 10, 2017
 ====================================
 
 - Revert "Fix Context lookup application when moving back after a glyph..."
   This introduced memory access problems.  To be fixed properly soon.
 
 
--- a/gfx/harfbuzz/README-mozilla
+++ b/gfx/harfbuzz/README-mozilla
@@ -1,14 +1,14 @@
-gfx/harfbuzz status as of 2017-04-24:
+gfx/harfbuzz status as of 2017-07-19:
 
 This directory contains the harfbuzz source from the 'master' branch of
 https://github.com/behdad/harfbuzz.
 
-Current version: 1.4.6
+Current version: 1.4.7
 
 UPDATING:
 
 Note that gfx/harfbuzz/src/hb-version.h is not present in the upstream Git
 repository. It is created at build time by the harfbuzz build system;
 but as we don't use that build system in mozilla, it is necessary to refresh
 this file when updating harfbuzz, and check it into the mozilla tree.
 
--- a/gfx/harfbuzz/configure.ac
+++ b/gfx/harfbuzz/configure.ac
@@ -1,11 +1,11 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [1.4.5],
+        [1.4.7],
         [https://github.com/behdad/harfbuzz/issues/new],
         [harfbuzz],
         [http://harfbuzz.org/])
 
 AC_CONFIG_MACRO_DIR([m4])
 AC_CONFIG_SRCDIR([src/harfbuzz.pc.in])
 AC_CONFIG_HEADERS([config.h])
 
--- a/gfx/harfbuzz/src/gen-arabic-table.py
+++ b/gfx/harfbuzz/src/gen-arabic-table.py
@@ -129,17 +129,17 @@ def print_joining_table(f):
 	print "  switch (u >> %d)" % page_bits
 	print "  {"
 	pages = set([u>>page_bits for u in [s for s,e in ranges]+[e for s,e in ranges]])
 	for p in sorted(pages):
 		print "    case 0x%0Xu:" % p
 		for (start,end) in ranges:
 			if p not in [start>>page_bits, end>>page_bits]: continue
 			offset = "joining_offset_0x%04xu" % start
-			print "      if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return joining_table[u - 0x%04Xu + %s];" % (start, end, start, offset)
+			print "      if (hb_in_range<hb_codepoint_t> (u, 0x%04Xu, 0x%04Xu)) return joining_table[u - 0x%04Xu + %s];" % (start, end, start, offset)
 		print "      break;"
 		print ""
 	print "    default:"
 	print "      break;"
 	print "  }"
 	print "  return X;"
 	print "}"
 	print
--- a/gfx/harfbuzz/src/gen-indic-table.py
+++ b/gfx/harfbuzz/src/gen-indic-table.py
@@ -227,17 +227,17 @@ print "{"
 print "  switch (u >> %d)" % page_bits
 print "  {"
 pages = set([u>>page_bits for u in starts+ends+singles.keys()])
 for p in sorted(pages):
 	print "    case 0x%0Xu:" % p
 	for (start,end) in zip (starts, ends):
 		if p not in [start>>page_bits, end>>page_bits]: continue
 		offset = "indic_offset_0x%04xu" % start
-		print "      if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return indic_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset)
+		print "      if (hb_in_range<hb_codepoint_t> (u, 0x%04Xu, 0x%04Xu)) return indic_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset)
 	for u,d in singles.items ():
 		if p != u>>page_bits: continue
 		print "      if (unlikely (u == 0x%04Xu)) return _(%s,%s);" % (u, short[0][d[0]], short[1][d[1]])
 	print "      break;"
 	print ""
 print "    default:"
 print "      break;"
 print "  }"
--- a/gfx/harfbuzz/src/gen-use-table.py
+++ b/gfx/harfbuzz/src/gen-use-table.py
@@ -205,21 +205,23 @@ def is_SYM(U, UISC, UGC):
 	if U == 0x25CC: return False #SPEC-DRAFT
 	#SPEC-DRAFT return UGC in [So, Sc] or UISC == Symbol_Letter
 	return UGC in [So, Sc]
 def is_SYM_MOD(U, UISC, UGC):
 	return U in [0x1B6B, 0x1B6C, 0x1B6D, 0x1B6E, 0x1B6F, 0x1B70, 0x1B71, 0x1B72, 0x1B73]
 def is_VARIATION_SELECTOR(U, UISC, UGC):
 	return 0xFE00 <= U <= 0xFE0F
 def is_VOWEL(U, UISC, UGC):
+	# https://github.com/roozbehp/unicode-data/issues/6
 	return (UISC == Pure_Killer or
-		(UGC != Lo and UISC in [Vowel, Vowel_Dependent]))
+		(UGC != Lo and UISC in [Vowel, Vowel_Dependent] and U not in [0xAA29]))
 def is_VOWEL_MOD(U, UISC, UGC):
+	# https://github.com/roozbehp/unicode-data/issues/6
 	return (UISC in [Tone_Mark, Cantillation_Mark, Register_Shifter, Visarga] or
-		(UGC != Lo and UISC == Bindu))
+		(UGC != Lo and (UISC == Bindu or U in [0xAA29])))
 
 use_mapping = {
 	'B':	is_BASE,
 	'IND':	is_BASE_IND,
 	'N':	is_BASE_NUM,
 	'GB':	is_BASE_OTHER,
 	'CGJ':	is_CGJ,
 	'F':	is_CONS_FINAL,
@@ -444,17 +446,17 @@ print "{"
 print "  switch (u >> %d)" % page_bits
 print "  {"
 pages = set([u>>page_bits for u in starts+ends+singles.keys()])
 for p in sorted(pages):
 	print "    case 0x%0Xu:" % p
 	for (start,end) in zip (starts, ends):
 		if p not in [start>>page_bits, end>>page_bits]: continue
 		offset = "use_offset_0x%04xu" % start
-		print "      if (hb_in_range (u, 0x%04Xu, 0x%04Xu)) return use_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset)
+		print "      if (hb_in_range<hb_codepoint_t> (u, 0x%04Xu, 0x%04Xu)) return use_table[u - 0x%04Xu + %s];" % (start, end-1, start, offset)
 	for u,d in singles.items ():
 		if p != u>>page_bits: continue
 		print "      if (unlikely (u == 0x%04Xu)) return %s;" % (u, d[0])
 	print "      break;"
 	print ""
 print "    default:"
 print "      break;"
 print "  }"
--- a/gfx/harfbuzz/src/harfbuzz-icu.pc
+++ b/gfx/harfbuzz/src/harfbuzz-icu.pc
@@ -1,13 +1,13 @@
 prefix=/usr/local
 exec_prefix=/usr/local
 libdir=/usr/local/lib
 includedir=/usr/local/include
 
 Name: harfbuzz
 Description: HarfBuzz text shaping library ICU integration
-Version: 1.4.5
+Version: 1.4.7
 
 Requires: harfbuzz
 Requires.private: icu-uc
 Libs: -L${libdir} -lharfbuzz-icu
 Cflags: -I${includedir}/harfbuzz
--- a/gfx/harfbuzz/src/harfbuzz.pc
+++ b/gfx/harfbuzz/src/harfbuzz.pc
@@ -1,13 +1,13 @@
 prefix=/usr/local
 exec_prefix=/usr/local
 libdir=/usr/local/lib
 includedir=/usr/local/include
 
 Name: harfbuzz
 Description: HarfBuzz text shaping library
-Version: 1.4.5
+Version: 1.4.7
 
 Libs: -L${libdir} -lharfbuzz
 Libs.private:    
 Requires.private: glib-2.0 >= 2.19.1 
 Cflags: -I${includedir}/harfbuzz
--- a/gfx/harfbuzz/src/hb-atomic-private.hh
+++ b/gfx/harfbuzz/src/hb-atomic-private.hh
@@ -119,35 +119,35 @@ typedef unsigned int hb_atomic_int_impl_
 #define hb_atomic_ptr_impl_cmpexch(P,O,N)	( ({__machine_rw_barrier ();}), atomic_cas_ptr ((void **) (P), (void *) (O), (void *) (N)) == (void *) (O) ? true : false)
 
 
 #elif !defined(HB_NO_MT) && defined(_AIX) && defined(__IBMCPP__)
 
 #include <builtins.h>
 
 
-static inline int hb_fetch_and_add(volatile int* AI, unsigned int V) {
+static inline int _hb_fetch_and_add(volatile int* AI, unsigned int V) {
   __lwsync();
   int result = __fetch_and_add(AI, V);
   __isync();
   return result;
 }
-static inline int hb_compare_and_swaplp(volatile long* P, long O, long N) {
+static inline int _hb_compare_and_swaplp(volatile long* P, long O, long N) {
   __sync();
   int result = __compare_and_swaplp (P, &O, N);
   __sync();
   return result;
 }
 
 typedef int hb_atomic_int_impl_t;
 #define HB_ATOMIC_INT_IMPL_INIT(V) (V)
-#define hb_atomic_int_impl_add(AI, V)           hb_fetch_and_add (&(AI), (V))
+#define hb_atomic_int_impl_add(AI, V)           _hb_fetch_and_add (&(AI), (V))
 
 #define hb_atomic_ptr_impl_get(P)               (__sync(), (void *) *(P))
-#define hb_atomic_ptr_impl_cmpexch(P,O,N)       hb_compare_and_swaplp ((long*)(P), (long)(O), (long)(N))
+#define hb_atomic_ptr_impl_cmpexch(P,O,N)       _hb_compare_and_swaplp ((long*)(P), (long)(O), (long)(N))
 
 #elif !defined(HB_NO_MT)
 
 #define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */
 
 typedef volatile int hb_atomic_int_impl_t;
 #define HB_ATOMIC_INT_IMPL_INIT(V) (V)
 #define hb_atomic_int_impl_add(AI, V)		(((AI) += (V)) - (V))
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -547,17 +547,17 @@ hb_buffer_t::reverse_clusters (void)
 
 void
 hb_buffer_t::merge_clusters_impl (unsigned int start,
 				  unsigned int end)
 {
   if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
     return;
 
-  unsigned int cluster = info[start].cluster;
+  uint32_t cluster = info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
     cluster = MIN (cluster, info[i].cluster);
 
   /* Extend end */
   while (end < len && info[end - 1].cluster == info[end].cluster)
     end++;
 
@@ -578,17 +578,17 @@ hb_buffer_t::merge_out_clusters (unsigne
 				 unsigned int end)
 {
   if (cluster_level == HB_BUFFER_CLUSTER_LEVEL_CHARACTERS)
     return;
 
   if (unlikely (end - start < 2))
     return;
 
-  unsigned int cluster = out_info[start].cluster;
+  uint32_t cluster = out_info[start].cluster;
 
   for (unsigned int i = start + 1; i < end; i++)
     cluster = MIN (cluster, out_info[i].cluster);
 
   /* Extend start */
   while (start && out_info[start - 1].cluster == out_info[start].cluster)
     start--;
 
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -216,19 +216,28 @@ struct hb_language_item_t {
   struct hb_language_item_t *next;
   hb_language_t lang;
 
   inline bool operator == (const char *s) const {
     return lang_equal (lang, s);
   }
 
   inline hb_language_item_t & operator = (const char *s) {
-    lang = (hb_language_t) strdup (s);
-    for (unsigned char *p = (unsigned char *) lang; *p; p++)
-      *p = canon_map[*p];
+    /* If a custom allocated is used calling strdup() pairs
+    badly with a call to the custom free() in finish() below.
+    Therefore don't call strdup(), implement its behavior.
+    */
+    size_t len = strlen(s) + 1;
+    lang = (hb_language_t) malloc(len);
+    if (likely (lang))
+    {
+      memcpy((unsigned char *) lang, s, len);
+      for (unsigned char *p = (unsigned char *) lang; *p; p++)
+	*p = canon_map[*p];
+    }
 
     return *this;
   }
 
   void finish (void) { free ((void *) lang); }
 };
 
 
@@ -260,16 +269,21 @@ retry:
       return lang;
 
   /* Not found; allocate one. */
   hb_language_item_t *lang = (hb_language_item_t *) calloc (1, sizeof (hb_language_item_t));
   if (unlikely (!lang))
     return NULL;
   lang->next = first_lang;
   *lang = key;
+  if (unlikely (!lang->lang))
+  {
+    free (lang);
+    return NULL;
+  }
 
   if (!hb_atomic_ptr_cmpexch (&langs, first_lang, lang)) {
     lang->finish ();
     free (lang);
     goto retry;
   }
 
 #ifdef HB_USE_ATEXIT
@@ -652,16 +666,40 @@ parse_uint (const char **pp, const char 
     return false;
 
   *pv = v;
   *pp += pend - p;
   return true;
 }
 
 static bool
+parse_uint32 (const char **pp, const char *end, uint32_t *pv)
+{
+  char buf[32];
+  unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
+  strncpy (buf, *pp, len);
+  buf[len] = '\0';
+
+  char *p = buf;
+  char *pend = p;
+  unsigned int v;
+
+  /* Intentionally use strtol instead of strtoul, such that
+   * -1 turns into "big number"... */
+  errno = 0;
+  v = strtol (p, &pend, 0);
+  if (errno || p == pend)
+    return false;
+
+  *pv = v;
+  *pp += pend - p;
+  return true;
+}
+
+static bool
 parse_float (const char **pp, const char *end, float *pv)
 {
   char buf[32];
   unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
   strncpy (buf, *pp, len);
   buf[len] = '\0';
 
   char *p = buf;
@@ -674,17 +712,17 @@ parse_float (const char **pp, const char
     return false;
 
   *pv = v;
   *pp += pend - p;
   return true;
 }
 
 static bool
-parse_bool (const char **pp, const char *end, unsigned int *pv)
+parse_bool (const char **pp, const char *end, uint32_t *pv)
 {
   parse_space (pp, end);
 
   const char *p = *pp;
   while (*pp < end && ISALPHA(**pp))
     (*pp)++;
 
   /* CSS allows on/off as aliases 1/0. */
@@ -773,17 +811,17 @@ parse_feature_indices (const char **pp, 
 
   return parse_char (pp, end, ']');
 }
 
 static bool
 parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
 {
   bool had_equal = parse_char (pp, end, '=');
-  bool had_value = parse_uint (pp, end, &feature->value) ||
+  bool had_value = parse_uint32 (pp, end, &feature->value) ||
                    parse_bool (pp, end, &feature->value);
   /* CSS doesn't use equal-sign between tag and value.
    * If there was an equal-sign, then there *must* be a value.
    * A value without an eqaul-sign is ok, but not required. */
   return !had_equal || had_value;
 }
 
 static bool
--- a/gfx/harfbuzz/src/hb-coretext.cc
+++ b/gfx/harfbuzz/src/hb-coretext.cc
@@ -636,32 +636,33 @@ hb_bool_t
 	if (active_features.len)
 	{
 	  CFMutableArrayRef features_array = CFArrayCreateMutable(kCFAllocatorDefault, 0, &kCFTypeArrayCallBacks);
 
 	  /* TODO sort and resolve conflicting features? */
 	  /* active_features.qsort (); */
 	  for (unsigned int j = 0; j < active_features.len; j++)
 	  {
-	    CFStringRef keys[2] = {
+	    CFStringRef keys[] = {
 	      kCTFontFeatureTypeIdentifierKey,
 	      kCTFontFeatureSelectorIdentifierKey
 	    };
-	    CFNumberRef values[2] = {
+	    CFNumberRef values[] = {
 	      CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.feature),
 	      CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &active_features[j].rec.setting)
 	    };
+	    ASSERT_STATIC (ARRAY_LENGTH (keys) == ARRAY_LENGTH (values));
 	    CFDictionaryRef dict = CFDictionaryCreate (kCFAllocatorDefault,
 						       (const void **) keys,
 						       (const void **) values,
-						       2,
+						       ARRAY_LENGTH (keys),
 						       &kCFTypeDictionaryKeyCallBacks,
 						       &kCFTypeDictionaryValueCallBacks);
-	    CFRelease (values[0]);
-	    CFRelease (values[1]);
+	    for (unsigned int i = 0; i < ARRAY_LENGTH (values); i++)
+	      CFRelease (values[i]);
 
 	    CFArrayAppendValue (features_array, dict);
 	    CFRelease (dict);
 
 	  }
 
 	  CFDictionaryRef attributes = CFDictionaryCreate (kCFAllocatorDefault,
 							   (const void **) &kCTFontFeatureSettingsAttribute,
@@ -694,19 +695,16 @@ hb_bool_t
 	  goto fail_features;
 	*feature = event->feature;
       } else {
         active_feature_t *feature = active_features.find (&event->feature);
 	if (feature)
 	  active_features.remove (feature - active_features.array);
       }
     }
-
-    if (!range_records.len) /* No active feature found. */
-      goto fail_features;
   }
   else
   {
   fail_features:
     num_features = 0;
   }
 
   unsigned int scratch_size;
@@ -828,17 +826,17 @@ resize_and_retry:
 	  FAIL ("CFStringCreateWithCStringNoCopy failed");
 	CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
 					kCTLanguageAttributeName, lang);
 	CFRelease (lang);
       }
       CFAttributedStringSetAttribute (attr_string, CFRangeMake (0, chars_len),
 				      kCTFontAttributeName, face_data->ct_font);
 
-      if (num_features)
+      if (num_features && range_records.len)
       {
 	unsigned int start = 0;
 	range_record_t *last_range = &range_records[0];
 	for (unsigned int k = 0; k < chars_len; k++)
 	{
 	  range_record_t *range = last_range;
 	  while (log_clusters[k] < range->index_first)
 	    range--;
@@ -854,25 +852,50 @@ resize_and_retry:
 	  }
 
 	  last_range = range;
 	}
 	if (start != chars_len && last_range->font)
 	  CFAttributedStringSetAttribute (attr_string, CFRangeMake (start, chars_len - start),
 					  kCTFontAttributeName, last_range->font);
       }
+      /* Enable/disable kern if requested.
+       *
+       * Note: once kern is disabled, reenabling it doesn't currently seem to work in CoreText.
+       */
+      if (num_features)
+      {
+	unsigned int zeroint = 0;
+	CFNumberRef zero = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &zeroint);
+	for (unsigned int i = 0; i < num_features; i++)
+	{
+	  const hb_feature_t &feature = features[i];
+	  if (feature.tag == HB_TAG('k','e','r','n') &&
+	      feature.start < chars_len && feature.start < feature.end)
+	  {
+	    CFRange feature_range = CFRangeMake (feature.start,
+	                                         MIN (feature.end, chars_len) - feature.start);
+	    if (feature.value)
+	      CFAttributedStringRemoveAttribute (attr_string, feature_range, kCTKernAttributeName);
+	    else
+	      CFAttributedStringSetAttribute (attr_string, feature_range, kCTKernAttributeName, zero);
+	  }
+	}
+	CFRelease (zero);
+      }
 
       int level = HB_DIRECTION_IS_FORWARD (buffer->props.direction) ? 0 : 1;
       CFNumberRef level_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &level);
       CFDictionaryRef options = CFDictionaryCreate (kCFAllocatorDefault,
 						    (const void **) &kCTTypesetterOptionForcedEmbeddingLevel,
 						    (const void **) &level_number,
 						    1,
 						    &kCFTypeDictionaryKeyCallBacks,
 						    &kCFTypeDictionaryValueCallBacks);
+      CFRelease (level_number);
       if (unlikely (!options))
         FAIL ("CFDictionaryCreate failed");
 
       CTTypesetterRef typesetter = CTTypesetterCreateWithAttributedStringAndOptions (attr_string, options);
       CFRelease (options);
       CFRelease (attr_string);
       if (unlikely (!typesetter))
 	FAIL ("CTTypesetterCreateWithAttributedStringAndOptions failed");
--- a/gfx/harfbuzz/src/hb-ot-font.cc
+++ b/gfx/harfbuzz/src/hb-ot-font.cc
@@ -133,17 +133,17 @@ struct hb_ot_face_metrics_accelerator_t
        * for this direction: return default advance.  Otherwise, it means that the
        * glyph index is out of bound: return zero. */
       if (this->num_metrics)
 	return 0;
       else
 	return this->default_advance;
     }
 
-    return this->table->longMetric[MIN (glyph, this->num_advances - 1)].advance
+    return this->table->longMetric[MIN (glyph, (uint32_t) this->num_advances - 1)].advance
 	 + this->var->get_advance_var (glyph, font->coords, font->num_coords); // TODO Optimize?!
   }
 };
 
 struct hb_ot_face_glyf_accelerator_t
 {
   bool short_offset;
   unsigned int num_glyphs;
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -314,17 +314,17 @@ struct hb_apply_context_t :
 
     inline may_skip_t
     may_skip (const hb_apply_context_t *c,
 	      const hb_glyph_info_t    &info) const
     {
       if (!c->check_glyph_property (&info, lookup_props))
 	return SKIP_YES;
 
-      if (unlikely (_hb_glyph_info_is_default_ignorable_and_not_fvs (&info) &&
+      if (unlikely (_hb_glyph_info_is_default_ignorable_and_not_hidden (&info) &&
 		    (ignore_zwnj || !_hb_glyph_info_is_zwnj (&info)) &&
 		    (ignore_zwj || !_hb_glyph_info_is_zwj (&info))))
 	return SKIP_MAYBE;
 
       return SKIP_NO;
     }
 
     protected:
@@ -341,19 +341,19 @@ struct hb_apply_context_t :
   {
     inline void init (hb_apply_context_t *c_, bool context_match = false)
     {
       c = c_;
       match_glyph_data = NULL;
       matcher.set_match_func (NULL, NULL);
       matcher.set_lookup_props (c->lookup_props);
       /* Ignore ZWNJ if we are matching GSUB context, or matching GPOS. */
-      matcher.set_ignore_zwnj (context_match || c->table_index == 1);
+      matcher.set_ignore_zwnj (c->table_index == 1 || (context_match && c->auto_zwnj));
       /* Ignore ZWJ if we are matching GSUB context, or matching GPOS, or if asked to. */
-      matcher.set_ignore_zwj (context_match || c->table_index == 1 || c->auto_zwj);
+      matcher.set_ignore_zwj  (c->table_index == 1 || (context_match || c->auto_zwj));
       matcher.set_mask (context_match ? -1 : c->lookup_mask);
     }
     inline void set_lookup_props (unsigned int lookup_props)
     {
       matcher.set_lookup_props (lookup_props);
     }
     inline void set_match_func (matcher_t::match_func_t match_func_,
 				const void *match_data_,
@@ -452,55 +452,60 @@ struct hb_apply_context_t :
       return default_return_value ();
 
     nesting_level_left--;
     bool ret = recurse_func (this, lookup_index);
     nesting_level_left++;
     return ret;
   }
 
-  unsigned int table_index; /* GSUB/GPOS */
+  skipping_iterator_t iter_input, iter_context;
+
   hb_font_t *font;
   hb_face_t *face;
   hb_buffer_t *buffer;
+  recurse_func_t recurse_func;
+  const GDEF &gdef;
+  const VariationStore &var_store;
+
   hb_direction_t direction;
   hb_mask_t lookup_mask;
-  bool auto_zwj;
-  recurse_func_t recurse_func;
-  unsigned int nesting_level_left;
+  unsigned int table_index; /* GSUB/GPOS */
+  unsigned int lookup_index;
   unsigned int lookup_props;
-  const GDEF &gdef;
+  unsigned int nesting_level_left;
+  unsigned int debug_depth;
+
+  bool auto_zwnj;
+  bool auto_zwj;
   bool has_glyph_classes;
-  const VariationStore &var_store;
-  skipping_iterator_t iter_input, iter_context;
-  unsigned int lookup_index;
-  unsigned int debug_depth;
 
 
   hb_apply_context_t (unsigned int table_index_,
 		      hb_font_t *font_,
 		      hb_buffer_t *buffer_) :
-			table_index (table_index_),
+			iter_input (), iter_context (),
 			font (font_), face (font->face), buffer (buffer_),
+			recurse_func (NULL),
+			gdef (*hb_ot_layout_from_face (face)->gdef),
+			var_store (gdef.get_var_store ()),
 			direction (buffer_->props.direction),
 			lookup_mask (1),
-			auto_zwj (true),
-			recurse_func (NULL),
-			nesting_level_left (HB_MAX_NESTING_LEVEL),
+			table_index (table_index_),
+			lookup_index ((unsigned int) -1),
 			lookup_props (0),
-			gdef (*hb_ot_layout_from_face (face)->gdef),
-			has_glyph_classes (gdef.has_glyph_classes ()),
-			var_store (gdef.get_var_store ()),
-			iter_input (),
-			iter_context (),
-			lookup_index ((unsigned int) -1),
-			debug_depth (0) {}
+			nesting_level_left (HB_MAX_NESTING_LEVEL),
+			debug_depth (0),
+			auto_zwnj (true),
+			auto_zwj (true),
+			has_glyph_classes (gdef.has_glyph_classes ()) {}
 
   inline void set_lookup_mask (hb_mask_t mask) { lookup_mask = mask; }
   inline void set_auto_zwj (bool auto_zwj_) { auto_zwj = auto_zwj_; }
+  inline void set_auto_zwnj (bool auto_zwnj_) { auto_zwnj = auto_zwnj_; }
   inline void set_recurse_func (recurse_func_t func) { recurse_func = func; }
   inline void set_lookup_index (unsigned int lookup_index_) { lookup_index = lookup_index_; }
   inline void set_lookup_props (unsigned int lookup_props_)
   {
     lookup_props = lookup_props_;
     iter_input.init (this, false);
     iter_context.init (this, true);
   }
--- a/gfx/harfbuzz/src/hb-ot-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh
@@ -235,17 +235,18 @@ static inline unsigned int
  * - For Cf: whether it's ZWJ, ZWNJ, or something else.
  * - For Ws: index of which space character this is, if space fallback
  *   is needed, ie. we don't set this by default, only if asked to.
  */
 
 enum hb_unicode_props_flags_t {
   UPROPS_MASK_GEN_CAT	= 0x001Fu,
   UPROPS_MASK_IGNORABLE	= 0x0020u,
-  UPROPS_MASK_FVS	= 0x0040u, /* MONGOLIAN FREE VARIATION SELECTOR 1..3 */
+  UPROPS_MASK_HIDDEN	= 0x0040u, /* MONGOLIAN FREE VARIATION SELECTOR 1..3,
+                                    * or TAG characters */
 
   /* If GEN_CAT=FORMAT, top byte masks: */
   UPROPS_MASK_Cf_ZWJ	= 0x0100u,
   UPROPS_MASK_Cf_ZWNJ	= 0x0200u
 };
 HB_MARK_AS_FLAG_T (hb_unicode_props_flags_t);
 
 static inline void
@@ -268,17 +269,21 @@ static inline void
       /* Mongolian Free Variation Selectors need to be remembered
        * because although we need to hide them like default-ignorables,
        * they need to non-ignorable during shaping.  This is similar to
        * what we do for joiners in Indic-like shapers, but since the
        * FVSes are GC=Mn, we have use a separate bit to remember them.
        * Fixes:
        * https://github.com/behdad/harfbuzz/issues/234
        */
-      if (unlikely (hb_in_range (u, 0x180Bu, 0x180Du))) props |= UPROPS_MASK_FVS;
+      if (unlikely (hb_in_range (u, 0x180Bu, 0x180Du))) props |= UPROPS_MASK_HIDDEN;
+      /* TAG characters need similar treatment. Fixes:
+       * https://github.com/behdad/harfbuzz/issues/463
+       */
+      if (unlikely (hb_in_range (u, 0xE0020u, 0xE007Fu))) props |= UPROPS_MASK_HIDDEN;
     }
     else if (unlikely (HB_UNICODE_GENERAL_CATEGORY_IS_NON_ENCLOSING_MARK_OR_MODIFIER_SYMBOL (gen_cat)))
     {
       /* The above check is just an optimization to let in only things we need further
        * processing on. */
 
       /* Only Mn and Mc can have non-zero ccc:
        * http://www.unicode.org/policies/stability_policy.html#Property_Value
@@ -368,19 +373,19 @@ static inline bool _hb_glyph_info_ligate
 
 static inline hb_bool_t
 _hb_glyph_info_is_default_ignorable (const hb_glyph_info_t *info)
 {
   return (info->unicode_props() & UPROPS_MASK_IGNORABLE) &&
 	 !_hb_glyph_info_ligated (info);
 }
 static inline hb_bool_t
-_hb_glyph_info_is_default_ignorable_and_not_fvs (const hb_glyph_info_t *info)
+_hb_glyph_info_is_default_ignorable_and_not_hidden (const hb_glyph_info_t *info)
 {
-  return ((info->unicode_props() & (UPROPS_MASK_IGNORABLE|UPROPS_MASK_FVS))
+  return ((info->unicode_props() & (UPROPS_MASK_IGNORABLE|UPROPS_MASK_HIDDEN))
 	  == UPROPS_MASK_IGNORABLE) &&
 	 !_hb_glyph_info_ligated (info);
 }
 
 static inline bool
 _hb_glyph_info_is_unicode_format (const hb_glyph_info_t *info)
 {
   return _hb_glyph_info_get_general_category (info) ==
--- a/gfx/harfbuzz/src/hb-ot-layout.cc
+++ b/gfx/harfbuzz/src/hb-ot-layout.cc
@@ -274,17 +274,17 @@ hb_ot_layout_get_attach_points (hb_face_
 }
 
 unsigned int
 hb_ot_layout_get_ligature_carets (hb_font_t      *font,
 				  hb_direction_t  direction,
 				  hb_codepoint_t  glyph,
 				  unsigned int    start_offset,
 				  unsigned int   *caret_count /* IN/OUT */,
-				  int            *caret_array /* OUT */)
+				  hb_position_t  *caret_array /* OUT */)
 {
   return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
 }
 
 
 /*
  * GSUB/GPOS
  */
@@ -1214,16 +1214,17 @@ inline void hb_ot_map_t::apply (const Pr
     const stage_map_t *stage = &stages[table_index][stage_index];
     for (; i < stage->last_lookup; i++)
     {
       unsigned int lookup_index = lookups[table_index][i].index;
       if (!buffer->message (font, "start lookup %d", lookup_index)) continue;
       c.set_lookup_index (lookup_index);
       c.set_lookup_mask (lookups[table_index][i].mask);
       c.set_auto_zwj (lookups[table_index][i].auto_zwj);
+      c.set_auto_zwnj (lookups[table_index][i].auto_zwnj);
       apply_string<Proxy> (&c,
 			   proxy.table.get_lookup (lookup_index),
 			   proxy.accels[lookup_index]);
       (void) buffer->message (font, "end lookup %d", lookup_index);
     }
 
     if (stage->pause_func)
     {
--- a/gfx/harfbuzz/src/hb-ot-map-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-map-private.hh
@@ -45,24 +45,26 @@ struct hb_ot_map_t
   struct feature_map_t {
     hb_tag_t tag; /* should be first for our bsearch to work */
     unsigned int index[2]; /* GSUB/GPOS */
     unsigned int stage[2]; /* GSUB/GPOS */
     unsigned int shift;
     hb_mask_t mask;
     hb_mask_t _1_mask; /* mask for value=1, for quick access */
     unsigned int needs_fallback : 1;
+    unsigned int auto_zwnj : 1;
     unsigned int auto_zwj : 1;
 
     static int cmp (const feature_map_t *a, const feature_map_t *b)
     { return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0; }
   };
 
   struct lookup_map_t {
     unsigned short index;
+    unsigned short auto_zwnj : 1;
     unsigned short auto_zwj : 1;
     hb_mask_t mask;
 
     static int cmp (const lookup_map_t *a, const lookup_map_t *b)
     { return a->index < b->index ? -1 : a->index > b->index ? 1 : 0; }
   };
 
   typedef void (*pause_func_t) (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer);
@@ -145,18 +147,19 @@ struct hb_ot_map_t
   hb_prealloced_array_t<lookup_map_t, 32> lookups[2]; /* GSUB/GPOS */
   hb_prealloced_array_t<stage_map_t, 4> stages[2]; /* GSUB/GPOS */
 };
 
 enum hb_ot_map_feature_flags_t {
   F_NONE		= 0x0000u,
   F_GLOBAL		= 0x0001u, /* Feature applies to all characters; results in no mask allocated for it. */
   F_HAS_FALLBACK	= 0x0002u, /* Has fallback implementation, so include mask bit even if feature not found. */
-  F_MANUAL_ZWJ		= 0x0004u, /* Don't skip over ZWJ when matching. */
-  F_GLOBAL_SEARCH	= 0x0008u  /* If feature not found in LangSys, look for it in global feature list and pick one. */
+  F_MANUAL_ZWNJ		= 0x0004u, /* Don't skip over ZWNJ when matching **context**. */
+  F_MANUAL_ZWJ		= 0x0008u, /* Don't skip over ZWJ when matching **input**. */
+  F_GLOBAL_SEARCH	= 0x0010u  /* If feature not found in LangSys, look for it in global feature list and pick one. */
 };
 HB_MARK_AS_FLAG_T (hb_ot_map_feature_flags_t);
 /* Macro version for where const is desired. */
 #define F_COMBINE(l,r) (hb_ot_map_feature_flags_t ((unsigned int) (l) | (unsigned int) (r)))
 
 
 struct hb_ot_map_builder_t
 {
@@ -191,17 +194,18 @@ struct hb_ot_map_builder_t
   private:
 
   HB_INTERNAL void add_lookups (hb_ot_map_t  &m,
 				hb_face_t    *face,
 				unsigned int  table_index,
 				unsigned int  feature_index,
 				unsigned int  variations_index,
 				hb_mask_t     mask,
-				bool          auto_zwj);
+				bool          auto_zwnj = true,
+				bool          auto_zwj = true);
 
   struct feature_info_t {
     hb_tag_t tag;
     unsigned int seq; /* sequence#, used for stable sorting only */
     unsigned int max_value;
     hb_ot_map_feature_flags_t flags;
     unsigned int default_value; /* for non-global features, what should the unset glyphs take */
     unsigned int stage[2]; /* GSUB/GPOS */
--- a/gfx/harfbuzz/src/hb-ot-map.cc
+++ b/gfx/harfbuzz/src/hb-ot-map.cc
@@ -80,16 +80,17 @@ void hb_ot_map_builder_t::add_feature (h
 
 void
 hb_ot_map_builder_t::add_lookups (hb_ot_map_t  &m,
 				  hb_face_t    *face,
 				  unsigned int  table_index,
 				  unsigned int  feature_index,
 				  unsigned int  variations_index,
 				  hb_mask_t     mask,
+				  bool          auto_zwnj,
 				  bool          auto_zwj)
 {
   unsigned int lookup_indices[32];
   unsigned int offset, len;
   unsigned int table_lookup_count;
 
   table_lookup_count = hb_ot_layout_table_get_lookup_count (face, table_tags[table_index]);
 
@@ -107,16 +108,17 @@ hb_ot_map_builder_t::add_lookups (hb_ot_
     {
       if (lookup_indices[i] >= table_lookup_count)
 	continue;
       hb_ot_map_t::lookup_map_t *lookup = m.lookups[table_index].push ();
       if (unlikely (!lookup))
         return;
       lookup->mask = mask;
       lookup->index = lookup_indices[i];
+      lookup->auto_zwnj = auto_zwnj;
       lookup->auto_zwj = auto_zwj;
     }
 
     offset += len;
   } while (len == ARRAY_LENGTH (lookup_indices));
 }
 
 
@@ -238,16 +240,17 @@ hb_ot_map_builder_t::compile (hb_ot_map_
     if (unlikely (!map))
       break;
 
     map->tag = info->tag;
     map->index[0] = feature_index[0];
     map->index[1] = feature_index[1];
     map->stage[0] = info->stage[0];
     map->stage[1] = info->stage[1];
+    map->auto_zwnj = !(info->flags & F_MANUAL_ZWNJ);
     map->auto_zwj = !(info->flags & F_MANUAL_ZWJ);
     if ((info->flags & F_GLOBAL) && info->max_value == 1) {
       /* Uses the global bit */
       map->shift = 0;
       map->mask = 1;
     } else {
       map->shift = next_bit;
       map->mask = (1u << (next_bit + bits_needed)) - (1u << next_bit);
@@ -279,39 +282,40 @@ hb_ot_map_builder_t::compile (hb_ot_map_
     unsigned int last_num_lookups = 0;
     for (unsigned stage = 0; stage < current_stage[table_index]; stage++)
     {
       if (required_feature_index[table_index] != HB_OT_LAYOUT_NO_FEATURE_INDEX &&
 	  required_feature_stage[table_index] == stage)
 	add_lookups (m, face, table_index,
 		     required_feature_index[table_index],
 		     variations_index,
-		     1 /* mask */,
-		     true /* auto_zwj */);
+		     1 /* mask */);
 
       for (unsigned i = 0; i < m.features.len; i++)
         if (m.features[i].stage[table_index] == stage)
 	  add_lookups (m, face, table_index,
 		       m.features[i].index[table_index],
 		       variations_index,
 		       m.features[i].mask,
+		       m.features[i].auto_zwnj,
 		       m.features[i].auto_zwj);
 
       /* Sort lookups and merge duplicates */
       if (last_num_lookups < m.lookups[table_index].len)
       {
 	m.lookups[table_index].qsort (last_num_lookups, m.lookups[table_index].len);
 
 	unsigned int j = last_num_lookups;
 	for (unsigned int i = j + 1; i < m.lookups[table_index].len; i++)
 	  if (m.lookups[table_index][i].index != m.lookups[table_index][j].index)
 	    m.lookups[table_index][++j] = m.lookups[table_index][i];
 	  else
 	  {
 	    m.lookups[table_index][j].mask |= m.lookups[table_index][i].mask;
+	    m.lookups[table_index][j].auto_zwnj &= m.lookups[table_index][i].auto_zwnj;
 	    m.lookups[table_index][j].auto_zwj &= m.lookups[table_index][i].auto_zwj;
 	  }
 	m.lookups[table_index].shrink (j + 1);
       }
 
       last_num_lookups = m.lookups[table_index].len;
 
       if (stage_index < stages[table_index].len && stages[table_index][stage_index].index == stage) {
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh
@@ -134,38 +134,38 @@ static const uint8_t joining_table[] =
 
 
 static unsigned int
 joining_type (hb_codepoint_t u)
 {
   switch (u >> 12)
   {
     case 0x0u:
-      if (hb_in_range (u, 0x0600u, 0x08E2u)) return joining_table[u - 0x0600u + joining_offset_0x0600u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x0600u, 0x08E2u)) return joining_table[u - 0x0600u + joining_offset_0x0600u];
       break;
 
     case 0x1u:
-      if (hb_in_range (u, 0x1806u, 0x18AAu)) return joining_table[u - 0x1806u + joining_offset_0x1806u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1806u, 0x18AAu)) return joining_table[u - 0x1806u + joining_offset_0x1806u];
       break;
 
     case 0x2u:
-      if (hb_in_range (u, 0x200Cu, 0x2069u)) return joining_table[u - 0x200Cu + joining_offset_0x200cu];
+      if (hb_in_range<hb_codepoint_t> (u, 0x200Cu, 0x2069u)) return joining_table[u - 0x200Cu + joining_offset_0x200cu];
       break;
 
     case 0xAu:
-      if (hb_in_range (u, 0xA840u, 0xA873u)) return joining_table[u - 0xA840u + joining_offset_0xa840u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xA840u, 0xA873u)) return joining_table[u - 0xA840u + joining_offset_0xa840u];
       break;
 
     case 0x10u:
-      if (hb_in_range (u, 0x10AC0u, 0x10AEFu)) return joining_table[u - 0x10AC0u + joining_offset_0x10ac0u];
-      if (hb_in_range (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x10AC0u, 0x10AEFu)) return joining_table[u - 0x10AC0u + joining_offset_0x10ac0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x10B80u, 0x10BAFu)) return joining_table[u - 0x10B80u + joining_offset_0x10b80u];
       break;
 
     case 0x1Eu:
-      if (hb_in_range (u, 0x1E900u, 0x1E943u)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1E900u, 0x1E943u)) return joining_table[u - 0x1E900u + joining_offset_0x1e900u];
       break;
 
     default:
       break;
   }
   return X;
 }
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -194,16 +194,19 @@ collect_features_arabic (hb_ot_shape_pla
    * substitutions.  We now follow the order of the spec, which makes
    * for better experience if that's what Uniscribe is doing.
    *
    * At least for Arabic, looks like Uniscribe has a pause between
    * rlig and calt.  Otherwise the IranNastaliq's ALLAH ligature won't
    * work.  However, testing shows that rlig and calt are applied
    * together for Mongolian in Uniscribe.  As such, we only add a
    * pause for Arabic, not other scripts.
+   *
+   * A pause after calt is required to make KFGQPC Uthmanic Script HAFS
+   * work correctly.  See https://github.com/behdad/harfbuzz/issues/505
    */
 
   map->add_gsub_pause (nuke_joiners);
 
   map->add_global_bool_feature (HB_TAG('s','t','c','h'));
   map->add_gsub_pause (record_stch);
 
   map->add_global_bool_feature (HB_TAG('c','c','m','p'));
@@ -217,27 +220,29 @@ collect_features_arabic (hb_ot_shape_pla
     map->add_feature (arabic_features[i], 1, has_fallback ? F_HAS_FALLBACK : F_NONE);
     map->add_gsub_pause (NULL);
   }
 
   map->add_feature (HB_TAG('r','l','i','g'), 1, F_GLOBAL|F_HAS_FALLBACK);
   if (plan->props.script == HB_SCRIPT_ARABIC)
     map->add_gsub_pause (arabic_fallback_shape);
 
+  /* No pause after rclt.  See 98460779bae19e4d64d29461ff154b3527bf8420. */
+  map->add_global_bool_feature (HB_TAG('r','c','l','t'));
   map->add_global_bool_feature (HB_TAG('c','a','l','t'));
+  map->add_gsub_pause (NULL);
 
   /* The spec includes 'cswh'.  Earlier versions of Windows
    * used to enable this by default, but testing suggests
    * that Windows 8 and later do not enable it by default,
    * and spec now says 'Off by default'.
    * We disabled this in ae23c24c32.
    * Note that IranNastaliq uses this feature extensively
    * to fixup broken glyph sequences.  Oh well...
    * Test case: U+0643,U+0640,U+0631. */
-  //map->add_gsub_pause (NULL);
   //map->add_global_bool_feature (HB_TAG('c','s','w','h'));
   map->add_global_bool_feature (HB_TAG('m','s','e','t'));
 }
 
 #include "hb-ot-shape-complex-arabic-fallback.hh"
 
 struct arabic_shape_plan_t
 {
@@ -340,17 +345,17 @@ arabic_joining (hb_buffer_t *buffer)
 
 static void
 mongolian_variation_selectors (hb_buffer_t *buffer)
 {
   /* Copy arabic_shaping_action() from base to Mongolian variation selectors. */
   unsigned int count = buffer->len;
   hb_glyph_info_t *info = buffer->info;
   for (unsigned int i = 1; i < count; i++)
-    if (unlikely (hb_in_range (info[i].codepoint, 0x180Bu, 0x180Du)))
+    if (unlikely (hb_in_range<hb_codepoint_t> (info[i].codepoint, 0x180Bu, 0x180Du)))
       info[i].arabic_shaping_action() = info[i - 1].arabic_shaping_action();
 }
 
 void
 setup_masks_arabic_plan (const arabic_shape_plan_t *arabic_plan,
 			 hb_buffer_t               *buffer,
 			 hb_script_t                script)
 {
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-hangul.cc
@@ -100,26 +100,26 @@ data_destroy_hangul (void *data)
 #define TBase 0x11A7u
 #define LCount 19u
 #define VCount 21u
 #define TCount 28u
 #define SBase 0xAC00u
 #define NCount (VCount * TCount)
 #define SCount (LCount * NCount)
 
-#define isCombiningL(u) (hb_in_range ((u), LBase, LBase+LCount-1))
-#define isCombiningV(u) (hb_in_range ((u), VBase, VBase+VCount-1))
-#define isCombiningT(u) (hb_in_range ((u), TBase+1, TBase+TCount-1))
-#define isCombinedS(u) (hb_in_range ((u), SBase, SBase+SCount-1))
+#define isCombiningL(u) (hb_in_range<hb_codepoint_t> ((u), LBase, LBase+LCount-1))
+#define isCombiningV(u) (hb_in_range<hb_codepoint_t> ((u), VBase, VBase+VCount-1))
+#define isCombiningT(u) (hb_in_range<hb_codepoint_t> ((u), TBase+1, TBase+TCount-1))
+#define isCombinedS(u) (hb_in_range<hb_codepoint_t> ((u), SBase, SBase+SCount-1))
 
-#define isL(u) (hb_in_ranges ((u), 0x1100u, 0x115Fu, 0xA960u, 0xA97Cu))
-#define isV(u) (hb_in_ranges ((u), 0x1160u, 0x11A7u, 0xD7B0u, 0xD7C6u))
-#define isT(u) (hb_in_ranges ((u), 0x11A8u, 0x11FFu, 0xD7CBu, 0xD7FBu))
+#define isL(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x1100u, 0x115Fu, 0xA960u, 0xA97Cu))
+#define isV(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x1160u, 0x11A7u, 0xD7B0u, 0xD7C6u))
+#define isT(u) (hb_in_ranges<hb_codepoint_t> ((u), 0x11A8u, 0x11FFu, 0xD7CBu, 0xD7FBu))
 
-#define isHangulTone(u) (hb_in_range ((u), 0x302Eu, 0x302Fu))
+#define isHangulTone(u) (hb_in_range<hb_codepoint_t> ((u), 0x302Eu, 0x302Fu))
 
 /* buffer var allocations */
 #define hangul_shaping_feature() complex_var_u8_0() /* hangul jamo shaping feature */
 
 static bool
 is_zero_width_char (hb_font_t *font,
 		    hb_codepoint_t unicode)
 {
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
@@ -127,17 +127,17 @@ enum indic_syllabic_category_t {
   INDIC_SYLLABIC_CATEGORY_JOINER			= OT_ZWJ,
   INDIC_SYLLABIC_CATEGORY_MODIFYING_LETTER		= OT_X,
   INDIC_SYLLABIC_CATEGORY_NON_JOINER			= OT_ZWNJ,
   INDIC_SYLLABIC_CATEGORY_NUKTA				= OT_N,
   INDIC_SYLLABIC_CATEGORY_NUMBER			= OT_PLACEHOLDER,
   INDIC_SYLLABIC_CATEGORY_NUMBER_JOINER			= OT_PLACEHOLDER, /* Don't care. */
   INDIC_SYLLABIC_CATEGORY_PURE_KILLER			= OT_M, /* Is like a vowel matra. */
   INDIC_SYLLABIC_CATEGORY_REGISTER_SHIFTER		= OT_RS,
-  INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER		= OT_M, /* Misc Khmer signs. */
+  INDIC_SYLLABIC_CATEGORY_SYLLABLE_MODIFIER		= OT_SM,
   INDIC_SYLLABIC_CATEGORY_TONE_LETTER			= OT_X,
   INDIC_SYLLABIC_CATEGORY_TONE_MARK			= OT_N,
   INDIC_SYLLABIC_CATEGORY_VIRAMA			= OT_H,
   INDIC_SYLLABIC_CATEGORY_VISARGA			= OT_SM,
   INDIC_SYLLABIC_CATEGORY_VOWEL				= OT_V,
   INDIC_SYLLABIC_CATEGORY_VOWEL_DEPENDENT		= OT_M,
   INDIC_SYLLABIC_CATEGORY_VOWEL_INDEPENDENT		= OT_V
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-table.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-table.cc
@@ -393,38 +393,38 @@ static const INDIC_TABLE_ELEMENT_TYPE in
 }; /* Table items: 1784; occupancy: 69% */
 
 INDIC_TABLE_ELEMENT_TYPE
 hb_indic_get_categories (hb_codepoint_t u)
 {
   switch (u >> 12)
   {
     case 0x0u:
-      if (hb_in_range (u, 0x0028u, 0x003Fu)) return indic_table[u - 0x0028u + indic_offset_0x0028u];
-      if (hb_in_range (u, 0x00B0u, 0x00D7u)) return indic_table[u - 0x00B0u + indic_offset_0x00b0u];
-      if (hb_in_range (u, 0x0900u, 0x0DF7u)) return indic_table[u - 0x0900u + indic_offset_0x0900u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x0028u, 0x003Fu)) return indic_table[u - 0x0028u + indic_offset_0x0028u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x00B0u, 0x00D7u)) return indic_table[u - 0x00B0u + indic_offset_0x00b0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x0900u, 0x0DF7u)) return indic_table[u - 0x0900u + indic_offset_0x0900u];
       if (unlikely (u == 0x00A0u)) return _(CP,x);
       break;
 
     case 0x1u:
-      if (hb_in_range (u, 0x1000u, 0x109Fu)) return indic_table[u - 0x1000u + indic_offset_0x1000u];
-      if (hb_in_range (u, 0x1780u, 0x17EFu)) return indic_table[u - 0x1780u + indic_offset_0x1780u];
-      if (hb_in_range (u, 0x1CD0u, 0x1CFFu)) return indic_table[u - 0x1CD0u + indic_offset_0x1cd0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1000u, 0x109Fu)) return indic_table[u - 0x1000u + indic_offset_0x1000u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1780u, 0x17EFu)) return indic_table[u - 0x1780u + indic_offset_0x1780u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1CD0u, 0x1CFFu)) return indic_table[u - 0x1CD0u + indic_offset_0x1cd0u];
       break;
 
     case 0x2u:
-      if (hb_in_range (u, 0x2008u, 0x2017u)) return indic_table[u - 0x2008u + indic_offset_0x2008u];
-      if (hb_in_range (u, 0x2070u, 0x2087u)) return indic_table[u - 0x2070u + indic_offset_0x2070u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x2008u, 0x2017u)) return indic_table[u - 0x2008u + indic_offset_0x2008u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x2070u, 0x2087u)) return indic_table[u - 0x2070u + indic_offset_0x2070u];
       if (unlikely (u == 0x25CCu)) return _(CP,x);
       break;
 
     case 0xAu:
-      if (hb_in_range (u, 0xA8E0u, 0xA8F7u)) return indic_table[u - 0xA8E0u + indic_offset_0xa8e0u];
-      if (hb_in_range (u, 0xA9E0u, 0xA9FFu)) return indic_table[u - 0xA9E0u + indic_offset_0xa9e0u];
-      if (hb_in_range (u, 0xAA60u, 0xAA7Fu)) return indic_table[u - 0xAA60u + indic_offset_0xaa60u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xA8E0u, 0xA8F7u)) return indic_table[u - 0xA8E0u + indic_offset_0xa8e0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xA9E0u, 0xA9FFu)) return indic_table[u - 0xA9E0u + indic_offset_0xa9e0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xAA60u, 0xAA7Fu)) return indic_table[u - 0xAA60u + indic_offset_0xaa60u];
       break;
 
     case 0x11u:
       // According to ScriptExtensions.txt, these Grantha marks may also be used in Tamil,
       // so the Indic shaper needs to know their categories.
       if (unlikely (u == 0x11303)) return _(Vs,R);
       if (unlikely (u == 0x1133c)) return _(N,B);
       break;
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
@@ -172,41 +172,50 @@ set_indic_properties (hb_glyph_info_t &i
   indic_position_t pos = (indic_position_t) (type >> 8);
 
 
   /*
    * Re-assign category
    */
 
   /* The following act more like the Bindus. */
-  if (unlikely (hb_in_range (u, 0x0953u, 0x0954u)))
+  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x0953u, 0x0954u)))
     cat = OT_SM;
   /* The following act like consonants. */
-  else if (unlikely (hb_in_ranges (u, 0x0A72u, 0x0A73u,
+  else if (unlikely (hb_in_ranges<hb_codepoint_t> (u, 0x0A72u, 0x0A73u,
 				      0x1CF5u, 0x1CF6u)))
     cat = OT_C;
   /* TODO: The following should only be allowed after a Visarga.
    * For now, just treat them like regular tone marks. */
-  else if (unlikely (hb_in_range (u, 0x1CE2u, 0x1CE8u)))
+  else if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x1CE2u, 0x1CE8u)))
     cat = OT_A;
   /* TODO: The following should only be allowed after some of
    * the nasalization marks, maybe only for U+1CE9..U+1CF1.
    * For now, just treat them like tone marks. */
   else if (unlikely (u == 0x1CEDu))
     cat = OT_A;
   /* The following take marks in standalone clusters, similar to Avagraha. */
-  else if (unlikely (hb_in_ranges (u, 0xA8F2u, 0xA8F7u,
+  else if (unlikely (hb_in_ranges<hb_codepoint_t> (u, 0xA8F2u, 0xA8F7u,
 				      0x1CE9u, 0x1CECu,
 				      0x1CEEu, 0x1CF1u)))
   {
     cat = OT_Symbol;
     ASSERT_STATIC ((int) INDIC_SYLLABIC_CATEGORY_AVAGRAHA == OT_Symbol);
   }
+  else if (unlikely (hb_in_range (u, 0x17CDu, 0x17D1u) ||
+		     u == 0x17CBu || u == 0x17D3u || u == 0x17DDu)) /* Khmer Various signs */
+  {
+    /* These can occur mid-syllable (eg. before matras), even though Unicode marks them as Syllable_Modifier.
+     * https://github.com/roozbehp/unicode-data/issues/5 */
+    cat = OT_M;
+    pos = POS_ABOVE_C;
+  }
+
   else if (unlikely (u == 0x17C6u)) cat = OT_N; /* Khmer Bindu doesn't like to be repositioned. */
-  else if (unlikely (hb_in_range (u, 0x2010u, 0x2011u)))
+  else if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x2010u, 0x2011u)))
 				    cat = OT_PLACEHOLDER;
   else if (unlikely (u == 0x25CCu)) cat = OT_DOTTEDCIRCLE;
 
 
   /*
    * Re-assign position.
    */
 
@@ -406,22 +415,22 @@ collect_features_indic (hb_ot_shape_plan
   /* The Indic specs do not require ccmp, but we apply it here since if
    * there is a use of it, it's typically at the beginning. */
   map->add_global_bool_feature (HB_TAG('c','c','m','p'));
 
 
   unsigned int i = 0;
   map->add_gsub_pause (initial_reordering);
   for (; i < INDIC_BASIC_FEATURES; i++) {
-    map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ);
+    map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ);
     map->add_gsub_pause (NULL);
   }
   map->add_gsub_pause (final_reordering);
   for (; i < INDIC_NUM_FEATURES; i++) {
-    map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ);
+    map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ | F_MANUAL_ZWNJ);
   }
 
   map->add_global_bool_feature (HB_TAG('c','a','l','t'));
   map->add_global_bool_feature (HB_TAG('c','l','i','g'));
 
   map->add_gsub_pause (clear_syllables);
 }
 
@@ -1733,17 +1742,17 @@ decompose_indic (const hb_ot_shape_norma
     /* This one has no decomposition in Unicode, but needs no decomposition either. */
     /* case 0x0AC9u  : return false; */
 
     /* Oriya */
     case 0x0B57u  : *a = no decomp, -> RIGHT; return true;
 #endif
   }
 
-  if ((ab == 0x0DDAu || hb_in_range (ab, 0x0DDCu, 0x0DDEu)))
+  if ((ab == 0x0DDAu || hb_in_range<hb_codepoint_t> (ab, 0x0DDCu, 0x0DDEu)))
   {
     /*
      * Sinhala split matras...  Let the fun begin.
      *
      * These four characters have Unicode decompositions.  However, Uniscribe
      * decomposes them "Khmer-style", that is, it uses the character itself to
      * get the second half.  The first half of all four decompositions is always
      * U+0DD9.
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-myanmar.cc
@@ -170,17 +170,17 @@ set_myanmar_properties (hb_glyph_info_t 
   hb_codepoint_t u = info.codepoint;
   unsigned int type = hb_indic_get_categories (u);
   indic_category_t cat = (indic_category_t) (type & 0x7Fu);
   indic_position_t pos = (indic_position_t) (type >> 8);
 
   /* Myanmar
    * http://www.microsoft.com/typography/OpenTypeDev/myanmar/intro.htm#analyze
    */
-  if (unlikely (hb_in_range (u, 0xFE00u, 0xFE0Fu)))
+  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xFE00u, 0xFE0Fu)))
     cat = (indic_category_t) OT_VS;
 
   switch (u)
   {
     case 0x104Eu:
       cat = (indic_category_t) OT_C; /* The spec says C, IndicSyllableCategory doesn't have. */
       break;
 
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-thai.cc
@@ -47,17 +47,17 @@ static thai_consonant_type_t
 get_consonant_type (hb_codepoint_t u)
 {
   if (u == 0x0E1Bu || u == 0x0E1Du || u == 0x0E1Fu/* || u == 0x0E2Cu*/)
     return AC;
   if (u == 0x0E0Du || u == 0x0E10u)
     return RC;
   if (u == 0x0E0Eu || u == 0x0E0Fu)
     return DC;
-  if (hb_in_range (u, 0x0E01u, 0x0E2Eu))
+  if (hb_in_range<hb_codepoint_t> (u, 0x0E01u, 0x0E2Eu))
     return NC;
   return NOT_CONSONANT;
 }
 
 
 enum thai_mark_type_t
 {
   AV,
@@ -65,22 +65,22 @@ enum thai_mark_type_t
   T,
   NOT_MARK,
   NUM_MARK_TYPES = NOT_MARK
 };
 
 static thai_mark_type_t
 get_mark_type (hb_codepoint_t u)
 {
-  if (u == 0x0E31u || hb_in_range (u, 0x0E34u, 0x0E37u) ||
-      u == 0x0E47u || hb_in_range (u, 0x0E4Du, 0x0E4Eu))
+  if (u == 0x0E31u || hb_in_range<hb_codepoint_t> (u, 0x0E34u, 0x0E37u) ||
+      u == 0x0E47u || hb_in_range<hb_codepoint_t> (u, 0x0E4Du, 0x0E4Eu))
     return AV;
-  if (hb_in_range (u, 0x0E38u, 0x0E3Au))
+  if (hb_in_range<hb_codepoint_t> (u, 0x0E38u, 0x0E3Au))
     return BV;
-  if (hb_in_range (u, 0x0E48u, 0x0E4Cu))
+  if (hb_in_range<hb_codepoint_t> (u, 0x0E48u, 0x0E4Cu))
     return T;
   return NOT_MARK;
 }
 
 
 enum thai_action_t
 {
   NOP,
@@ -305,17 +305,17 @@ preprocess_text_thai (const hb_ot_shape_
    * Note how the Lao versions are the same as Thai + 0x80.
    */
 
   /* We only get one script at a time, so a script-agnostic implementation
    * is adequate here. */
 #define IS_SARA_AM(x) (((x) & ~0x0080u) == 0x0E33u)
 #define NIKHAHIT_FROM_SARA_AM(x) ((x) - 0x0E33u + 0x0E4Du)
 #define SARA_AA_FROM_SARA_AM(x) ((x) - 1)
-#define IS_TONE_MARK(x) (hb_in_ranges ((x) & ~0x0080u, 0x0E34u, 0x0E37u, 0x0E47u, 0x0E4Eu, 0x0E31u, 0x0E31u))
+#define IS_TONE_MARK(x) (hb_in_ranges<hb_codepoint_t> ((x) & ~0x0080u, 0x0E34u, 0x0E37u, 0x0E47u, 0x0E4Eu, 0x0E31u, 0x0E31u))
 
   buffer->clear_output ();
   unsigned int count = buffer->len;
   for (buffer->idx = 0; buffer->idx < count && !buffer->in_error;)
   {
     hb_codepoint_t u = buffer->cur().codepoint;
     if (likely (!IS_SARA_AM (u))) {
       buffer->next_glyph ();
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.hh
@@ -33,41 +33,41 @@
 
 #include "hb-private.hh"
 
 
 #line 38 "hb-ot-shape-complex-use-machine.hh"
 static const unsigned char _use_syllable_machine_trans_keys[] = {
 	1u, 1u, 0u, 39u, 21u, 21u, 8u, 39u, 8u, 39u, 1u, 1u, 8u, 39u, 8u, 39u, 
 	8u, 39u, 8u, 26u, 8u, 26u, 8u, 26u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 
-	8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 13u, 21u, 
-	4u, 4u, 13u, 13u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 26u, 8u, 26u, 
-	8u, 26u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 
-	8u, 39u, 8u, 39u, 8u, 39u, 1u, 1u, 1u, 39u, 8u, 39u, 21u, 42u, 41u, 42u, 
-	42u, 42u, 0
+	8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 
+	13u, 21u, 4u, 4u, 13u, 13u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 26u, 
+	8u, 26u, 8u, 26u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 
+	8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 8u, 39u, 1u, 1u, 1u, 39u, 8u, 39u, 
+	21u, 42u, 41u, 42u, 42u, 42u, 0
 };
 
 static const char _use_syllable_machine_key_spans[] = {
 	1, 40, 1, 32, 32, 1, 32, 32, 
 	32, 19, 19, 19, 32, 32, 32, 32, 
-	32, 32, 32, 32, 32, 32, 32, 9, 
-	1, 1, 32, 32, 32, 32, 19, 19, 
-	19, 32, 32, 32, 32, 32, 32, 32, 
-	32, 32, 32, 1, 39, 32, 22, 2, 
-	1
+	32, 32, 32, 32, 32, 32, 32, 32, 
+	9, 1, 1, 32, 32, 32, 32, 19, 
+	19, 19, 32, 32, 32, 32, 32, 32, 
+	32, 32, 32, 32, 32, 1, 39, 32, 
+	22, 2, 1
 };
 
 static const short _use_syllable_machine_index_offsets[] = {
 	0, 2, 43, 45, 78, 111, 113, 146, 
 	179, 212, 232, 252, 272, 305, 338, 371, 
 	404, 437, 470, 503, 536, 569, 602, 635, 
-	645, 647, 649, 682, 715, 748, 781, 801, 
-	821, 841, 874, 907, 940, 973, 1006, 1039, 
-	1072, 1105, 1138, 1171, 1173, 1213, 1246, 1269, 
-	1272
+	668, 678, 680, 682, 715, 748, 781, 814, 
+	834, 854, 874, 907, 940, 973, 1006, 1039, 
+	1072, 1105, 1138, 1171, 1204, 1237, 1239, 1279, 
+	1312, 1335, 1338
 };
 
 static const char _use_syllable_machine_indicies[] = {
 	1, 0, 2, 3, 4, 2, 5, 3, 
 	4, 4, 6, 4, 4, 1, 7, 4, 
 	4, 4, 2, 2, 8, 9, 4, 4, 
 	10, 11, 12, 13, 14, 15, 16, 10, 
 	17, 18, 19, 20, 21, 22, 4, 23, 
@@ -126,177 +126,185 @@ static const char _use_syllable_machine_
 	28, 28, 28, 28, 28, 28, 28, 28, 
 	44, 28, 45, 46, 47, 28, 29, 28, 
 	28, 28, 28, 28, 28, 28, 28, 28, 
 	28, 28, 28, 28, 33, 34, 35, 36, 
 	37, 28, 39, 33, 28, 28, 28, 42, 
 	43, 44, 28, 45, 46, 47, 28, 29, 
 	28, 28, 28, 28, 28, 28, 28, 28, 
 	28, 28, 28, 28, 28, 33, 34, 35, 
-	36, 37, 28, 28, 33, 28, 28, 28, 
+	36, 37, 28, 50, 33, 28, 28, 28, 
 	42, 43, 44, 28, 45, 46, 47, 28, 
 	29, 28, 28, 28, 28, 28, 28, 28, 
 	28, 28, 28, 28, 28, 28, 33, 34, 
-	35, 36, 37, 38, 39, 33, 28, 28, 
+	35, 36, 37, 28, 28, 33, 28, 28, 
 	28, 42, 43, 44, 28, 45, 46, 47, 
-	28, 29, 28, 28, 30, 31, 28, 28, 
+	28, 29, 28, 28, 28, 28, 28, 28, 
 	28, 28, 28, 28, 28, 28, 28, 33, 
-	34, 35, 36, 37, 38, 39, 33, 40, 
-	28, 41, 42, 43, 44, 28, 45, 46, 
+	34, 35, 36, 37, 38, 39, 33, 28, 
+	28, 28, 42, 43, 44, 28, 45, 46, 
 	47, 28, 29, 28, 28, 30, 31, 28, 
 	28, 28, 28, 28, 28, 28, 28, 28, 
 	33, 34, 35, 36, 37, 38, 39, 33, 
-	40, 32, 41, 42, 43, 44, 28, 45, 
-	46, 47, 28, 51, 50, 50, 50, 50, 
-	50, 50, 50, 52, 50, 5, 53, 51, 
-	50, 6, 54, 54, 1, 55, 54, 54, 
-	54, 54, 54, 54, 54, 54, 56, 10, 
-	11, 12, 13, 14, 15, 16, 10, 17, 
-	19, 19, 20, 21, 22, 54, 23, 24, 
-	25, 54, 6, 54, 54, 1, 55, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
+	40, 28, 41, 42, 43, 44, 28, 45, 
+	46, 47, 28, 29, 28, 28, 30, 31, 
+	28, 28, 28, 28, 28, 28, 28, 28, 
+	28, 33, 34, 35, 36, 37, 38, 39, 
+	33, 40, 32, 41, 42, 43, 44, 28, 
+	45, 46, 47, 28, 52, 51, 51, 51, 
+	51, 51, 51, 51, 53, 51, 5, 54, 
+	52, 51, 6, 55, 55, 1, 56, 55, 
+	55, 55, 55, 55, 55, 55, 55, 57, 
 	10, 11, 12, 13, 14, 15, 16, 10, 
-	17, 19, 19, 20, 21, 22, 54, 23, 
-	24, 25, 54, 6, 54, 54, 54, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	54, 10, 11, 12, 13, 14, 54, 54, 
-	54, 54, 54, 54, 20, 21, 22, 54, 
-	23, 24, 25, 54, 6, 54, 54, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 11, 12, 13, 14, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	54, 23, 24, 25, 54, 6, 54, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 54, 54, 12, 13, 14, 
-	54, 6, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 13, 14, 54, 6, 54, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 54, 54, 54, 54, 14, 
-	54, 6, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	54, 12, 13, 14, 54, 54, 54, 54, 
-	54, 54, 54, 54, 54, 54, 23, 24, 
-	25, 54, 6, 54, 54, 54, 54, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 12, 13, 14, 54, 54, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	24, 25, 54, 6, 54, 54, 54, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 12, 13, 14, 54, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 25, 54, 6, 54, 54, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 11, 12, 13, 14, 54, 
-	54, 54, 54, 54, 54, 20, 21, 22, 
-	54, 23, 24, 25, 54, 6, 54, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 54, 11, 12, 13, 14, 
-	54, 54, 54, 54, 54, 54, 54, 21, 
-	22, 54, 23, 24, 25, 54, 6, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 54, 54, 11, 12, 13, 
-	14, 54, 54, 54, 54, 54, 54, 54, 
-	54, 22, 54, 23, 24, 25, 54, 6, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 54, 54, 10, 11, 12, 
-	13, 14, 54, 16, 10, 54, 54, 54, 
-	20, 21, 22, 54, 23, 24, 25, 54, 
-	6, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 54, 54, 54, 10, 11, 
-	12, 13, 14, 54, 54, 10, 54, 54, 
-	54, 20, 21, 22, 54, 23, 24, 25, 
-	54, 6, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 54, 54, 54, 54, 10, 
-	11, 12, 13, 14, 15, 16, 10, 54, 
-	54, 54, 20, 21, 22, 54, 23, 24, 
-	25, 54, 6, 54, 54, 1, 55, 54, 
-	54, 54, 54, 54, 54, 54, 54, 54, 
-	10, 11, 12, 13, 14, 15, 16, 10, 
-	17, 54, 19, 20, 21, 22, 54, 23, 
-	24, 25, 54, 1, 57, 3, 54, 54, 
-	54, 3, 54, 54, 6, 54, 54, 1, 
-	55, 54, 54, 54, 54, 54, 54, 54, 
-	54, 54, 10, 11, 12, 13, 14, 15, 
-	16, 10, 17, 18, 19, 20, 21, 22, 
-	54, 23, 24, 25, 54, 6, 54, 54, 
-	1, 55, 54, 54, 54, 54, 54, 54, 
-	54, 54, 54, 10, 11, 12, 13, 14, 
-	15, 16, 10, 17, 18, 19, 20, 21, 
-	22, 54, 23, 24, 25, 54, 59, 58, 
-	58, 58, 58, 58, 58, 58, 58, 58, 
-	58, 58, 58, 58, 58, 58, 58, 58, 
-	58, 58, 59, 60, 58, 59, 60, 58, 
-	60, 58, 0
+	17, 19, 19, 20, 21, 22, 55, 23, 
+	24, 25, 55, 6, 55, 55, 1, 56, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 10, 11, 12, 13, 14, 15, 16, 
+	10, 17, 19, 19, 20, 21, 22, 55, 
+	23, 24, 25, 55, 6, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 10, 11, 12, 13, 14, 55, 
+	55, 55, 55, 55, 55, 20, 21, 22, 
+	55, 23, 24, 25, 55, 6, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 11, 12, 13, 14, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 23, 24, 25, 55, 6, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 12, 13, 
+	14, 55, 6, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 13, 14, 55, 6, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	14, 55, 6, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 12, 13, 14, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 23, 
+	24, 25, 55, 6, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 12, 13, 14, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 24, 25, 55, 6, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 12, 13, 14, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 25, 55, 6, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 11, 12, 13, 14, 
+	55, 55, 55, 55, 55, 55, 20, 21, 
+	22, 55, 23, 24, 25, 55, 6, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 11, 12, 13, 
+	14, 55, 55, 55, 55, 55, 55, 55, 
+	21, 22, 55, 23, 24, 25, 55, 6, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 11, 12, 
+	13, 14, 55, 55, 55, 55, 55, 55, 
+	55, 55, 22, 55, 23, 24, 25, 55, 
+	6, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 10, 11, 
+	12, 13, 14, 55, 16, 10, 55, 55, 
+	55, 20, 21, 22, 55, 23, 24, 25, 
+	55, 6, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 10, 
+	11, 12, 13, 14, 55, 58, 10, 55, 
+	55, 55, 20, 21, 22, 55, 23, 24, 
+	25, 55, 6, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	10, 11, 12, 13, 14, 55, 55, 10, 
+	55, 55, 55, 20, 21, 22, 55, 23, 
+	24, 25, 55, 6, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 10, 11, 12, 13, 14, 15, 16, 
+	10, 55, 55, 55, 20, 21, 22, 55, 
+	23, 24, 25, 55, 6, 55, 55, 1, 
+	56, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 10, 11, 12, 13, 14, 15, 
+	16, 10, 17, 55, 19, 20, 21, 22, 
+	55, 23, 24, 25, 55, 1, 59, 3, 
+	55, 55, 55, 3, 55, 55, 6, 55, 
+	55, 1, 56, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 10, 11, 12, 13, 
+	14, 15, 16, 10, 17, 18, 19, 20, 
+	21, 22, 55, 23, 24, 25, 55, 6, 
+	55, 55, 1, 56, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 10, 11, 12, 
+	13, 14, 15, 16, 10, 17, 18, 19, 
+	20, 21, 22, 55, 23, 24, 25, 55, 
+	61, 60, 60, 60, 60, 60, 60, 60, 
+	60, 60, 60, 60, 60, 60, 60, 60, 
+	60, 60, 60, 60, 61, 62, 60, 61, 
+	62, 60, 62, 60, 0
 };
 
 static const char _use_syllable_machine_trans_targs[] = {
-	1, 26, 2, 3, 1, 23, 1, 43, 
-	44, 46, 28, 29, 30, 31, 32, 39, 
-	40, 41, 45, 42, 36, 37, 38, 33, 
-	34, 35, 1, 1, 1, 1, 4, 5, 
-	22, 7, 8, 9, 10, 11, 18, 19, 
-	20, 21, 15, 16, 17, 12, 13, 14, 
-	6, 1, 1, 24, 25, 1, 1, 0, 
-	27, 1, 1, 47, 48
+	1, 27, 2, 3, 1, 24, 1, 45, 
+	46, 48, 29, 30, 31, 32, 33, 40, 
+	41, 43, 47, 44, 37, 38, 39, 34, 
+	35, 36, 1, 1, 1, 1, 4, 5, 
+	23, 7, 8, 9, 10, 11, 18, 19, 
+	21, 22, 15, 16, 17, 12, 13, 14, 
+	6, 1, 20, 1, 25, 26, 1, 1, 
+	0, 28, 42, 1, 1, 49, 50
 };
 
 static const char _use_syllable_machine_trans_actions[] = {
 	1, 2, 0, 0, 5, 0, 6, 0, 
 	2, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 2, 2, 0, 0, 0, 0, 
 	0, 0, 7, 8, 9, 10, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
-	0, 11, 12, 0, 0, 13, 14, 0, 
-	2, 15, 16, 0, 0
+	0, 11, 0, 12, 0, 0, 13, 14, 
+	0, 2, 0, 15, 16, 0, 0
 };
 
 static const char _use_syllable_machine_to_state_actions[] = {
 	0, 3, 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, 0, 0
 };
 
 static const char _use_syllable_machine_from_state_actions[] = {
 	0, 4, 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, 0, 0
 };
 
 static const short _use_syllable_machine_eof_trans[] = {
 	1, 0, 27, 29, 29, 50, 29, 29, 
 	29, 29, 29, 29, 29, 29, 29, 29, 
-	29, 29, 29, 29, 29, 29, 29, 51, 
-	54, 51, 55, 55, 55, 55, 55, 55, 
-	55, 55, 55, 55, 55, 55, 55, 55, 
-	55, 55, 55, 58, 55, 55, 59, 59, 
-	59
+	29, 29, 29, 29, 29, 29, 29, 29, 
+	52, 55, 52, 56, 56, 56, 56, 56, 
+	56, 56, 56, 56, 56, 56, 56, 56, 
+	56, 56, 56, 56, 56, 60, 56, 56, 
+	61, 61, 61
 };
 
 static const int use_syllable_machine_start = 1;
 static const int use_syllable_machine_first_final = 1;
 static const int use_syllable_machine_error = -1;
 
 static const int use_syllable_machine_en_main = 1;
 
 
 #line 38 "hb-ot-shape-complex-use-machine.rl"
 
 
 
-#line 138 "hb-ot-shape-complex-use-machine.rl"
+#line 139 "hb-ot-shape-complex-use-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; \
@@ -306,48 +314,48 @@ static const int use_syllable_machine_en
 
 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 315 "hb-ot-shape-complex-use-machine.hh"
+#line 323 "hb-ot-shape-complex-use-machine.hh"
 	{
 	cs = use_syllable_machine_start;
 	ts = 0;
 	te = 0;
 	act = 0;
 	}
 
-#line 159 "hb-ot-shape-complex-use-machine.rl"
+#line 160 "hb-ot-shape-complex-use-machine.rl"
 
 
   p = 0;
   pe = eof = buffer->len;
 
   unsigned int last = 0;
   unsigned int syllable_serial = 1;
   
-#line 332 "hb-ot-shape-complex-use-machine.hh"
+#line 340 "hb-ot-shape-complex-use-machine.hh"
 	{
 	int _slen;
 	int _trans;
 	const unsigned char *_keys;
 	const char *_inds;
 	if ( p == pe )
 		goto _test_eof;
 _resume:
 	switch ( _use_syllable_machine_from_state_actions[cs] ) {
 	case 4:
 #line 1 "NONE"
 	{ts = p;}
 	break;
-#line 346 "hb-ot-shape-complex-use-machine.hh"
+#line 354 "hb-ot-shape-complex-use-machine.hh"
 	}
 
 	_keys = _use_syllable_machine_trans_keys + (cs<<1);
 	_inds = _use_syllable_machine_indicies + _use_syllable_machine_index_offsets[cs];
 
 	_slen = _use_syllable_machine_key_spans[cs];
 	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].use_category()) &&
 		( info[p].use_category()) <= _keys[1] ?
@@ -360,91 +368,91 @@ find_syllables (hb_buffer_t *buffer)
 		goto _again;
 
 	switch ( _use_syllable_machine_trans_actions[_trans] ) {
 	case 2:
 #line 1 "NONE"
 	{te = p+1;}
 	break;
 	case 8:
-#line 127 "hb-ot-shape-complex-use-machine.rl"
+#line 128 "hb-ot-shape-complex-use-machine.rl"
 	{te = p+1;{ found_syllable (independent_cluster); }}
 	break;
 	case 10:
-#line 129 "hb-ot-shape-complex-use-machine.rl"
+#line 130 "hb-ot-shape-complex-use-machine.rl"
 	{te = p+1;{ found_syllable (standard_cluster); }}
 	break;
 	case 6:
-#line 133 "hb-ot-shape-complex-use-machine.rl"
+#line 134 "hb-ot-shape-complex-use-machine.rl"
 	{te = p+1;{ found_syllable (broken_cluster); }}
 	break;
 	case 5:
-#line 134 "hb-ot-shape-complex-use-machine.rl"
+#line 135 "hb-ot-shape-complex-use-machine.rl"
 	{te = p+1;{ found_syllable (non_cluster); }}
 	break;
 	case 7:
-#line 127 "hb-ot-shape-complex-use-machine.rl"
+#line 128 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (independent_cluster); }}
 	break;
 	case 11:
-#line 128 "hb-ot-shape-complex-use-machine.rl"
+#line 129 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (virama_terminated_cluster); }}
 	break;
 	case 9:
-#line 129 "hb-ot-shape-complex-use-machine.rl"
+#line 130 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (standard_cluster); }}
 	break;
 	case 13:
-#line 130 "hb-ot-shape-complex-use-machine.rl"
+#line 131 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (number_joiner_terminated_cluster); }}
 	break;
 	case 12:
-#line 131 "hb-ot-shape-complex-use-machine.rl"
+#line 132 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (numeral_cluster); }}
 	break;
 	case 16:
-#line 132 "hb-ot-shape-complex-use-machine.rl"
+#line 133 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (symbol_cluster); }}
 	break;
 	case 14:
-#line 133 "hb-ot-shape-complex-use-machine.rl"
+#line 134 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (broken_cluster); }}
 	break;
 	case 15:
-#line 134 "hb-ot-shape-complex-use-machine.rl"
+#line 135 "hb-ot-shape-complex-use-machine.rl"
 	{te = p;p--;{ found_syllable (non_cluster); }}
 	break;
 	case 1:
-#line 133 "hb-ot-shape-complex-use-machine.rl"
+#line 134 "hb-ot-shape-complex-use-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
 	break;
-#line 420 "hb-ot-shape-complex-use-machine.hh"
+#line 428 "hb-ot-shape-complex-use-machine.hh"
 	}
 
 _again:
 	switch ( _use_syllable_machine_to_state_actions[cs] ) {
 	case 3:
 #line 1 "NONE"
 	{ts = 0;}
 	break;
-#line 429 "hb-ot-shape-complex-use-machine.hh"
+#line 437 "hb-ot-shape-complex-use-machine.hh"
 	}
 
 	if ( ++p != pe )
 		goto _resume;
 	_test_eof: {}
 	if ( p == eof )
 	{
 	if ( _use_syllable_machine_eof_trans[cs] > 0 ) {
 		_trans = _use_syllable_machine_eof_trans[cs] - 1;
 		goto _eof_trans;
 	}
 	}
 
 	}
 
-#line 168 "hb-ot-shape-complex-use-machine.rl"
+#line 169 "hb-ot-shape-complex-use-machine.rl"
 
 }
 
 #undef found_syllable
 
 #endif /* HB_OT_SHAPE_COMPLEX_USE_MACHINE_HH */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.rl
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-machine.rl
@@ -84,17 +84,18 @@ VMAbv	= 37; # VOWEL_MOD_ABOVE
 VMBlw	= 38; # VOWEL_MOD_BELOW
 VMPst	= 39; # VOWEL_MOD_POST
 VMPre	= 23; # VOWEL_MOD_PRE
 SMAbv	= 41; # SYM_MOD_ABOVE
 SMBlw	= 42; # SYM_MOD_BELOW
 
 
 consonant_modifiers = CMAbv* CMBlw* ((H B | SUB) VS? CMAbv? CMBlw*)*;
-medial_consonants = MPre? MAbv? MBlw? MPst?;
+# Override: Allow two MBlw. https://github.com/behdad/harfbuzz/issues/376
+medial_consonants = MPre? MAbv? MBlw?.MBlw? MPst?;
 dependent_vowels = VPre* VAbv* VBlw* VPst*;
 vowel_modifiers = VMPre* VMAbv* VMBlw* VMPst*;
 final_consonants = FAbv* FBlw* FPst* FM?;
 
 virama_terminated_cluster =
 	R? (B | GB) VS?
 	consonant_modifiers
 	H
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-use-table.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-use-table.cc
@@ -4,17 +4,17 @@
  *
  *   ./gen-use-table.py IndicSyllabicCategory.txt IndicPositionalCategory.txt UnicodeData.txt Blocks.txt
  *
  * on files with these headers:
  *
  * # IndicSyllabicCategory-9.0.0.txt
  * # Date: 2016-05-21, 02:46:00 GMT [RP]
  * # IndicPositionalCategory-9.0.0.txt
- * # Date: 2016-02-25, 00:48:00 GMT [RP]
+ * # Date: 2016-06-09, 19:33:00 GMT [RP]
  * # Blocks-9.0.0.txt
  * # Date: 2016-02-05, 23:48:00 GMT [KW]
  * UnicodeData.txt does not have a header.
  */
 
 #include "hb-ot-shape-complex-use-private.hh"
 
 #define B	USE_B	/* BASE */
@@ -405,17 +405,17 @@ static const USE_TABLE_ELEMENT_TYPE use_
 
   /* A9E0 */     B,     B,     B,     B,     B,  VAbv,     O,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* A9F0 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,
 
   /* Cham */
 
   /* AA00 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* AA10 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
-  /* AA20 */     B,     B,     B,     B,     B,     B,     B,     B,     B,  VAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VAbv,  VPre,
+  /* AA20 */     B,     B,     B,     B,     B,     B,     B,     B,     B, VMAbv,  VAbv,  VAbv,  VAbv,  VBlw,  VAbv,  VPre,
   /* AA30 */  VPre,  VAbv,  VBlw,  MPst,  MPre,  MBlw,  MBlw,     O,     O,     O,     O,     O,     O,     O,     O,     O,
   /* AA40 */     B,     B,     B,  FAbv,     B,     B,     B,     B,     B,     B,     B,     B,  FAbv,  FPst,     O,     O,
   /* AA50 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     O,     O,     O,     O,     O,     O,
 
   /* Myanmar Extended-A */
 
   /* AA60 */     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,     B,
   /* AA70 */     O,     B,     B,     B,    GB,    GB,    GB,     O,     O,     O,     B, VMPst, VMAbv, VMPst,     B,     B,
@@ -639,57 +639,57 @@ static const USE_TABLE_ELEMENT_TYPE use_
 }; /* Table items: 5144; occupancy: 72% */
 
 USE_TABLE_ELEMENT_TYPE
 hb_use_get_categories (hb_codepoint_t u)
 {
   switch (u >> 12)
   {
     case 0x0u:
-      if (hb_in_range (u, 0x0028u, 0x003Fu)) return use_table[u - 0x0028u + use_offset_0x0028u];
-      if (hb_in_range (u, 0x00A0u, 0x00D7u)) return use_table[u - 0x00A0u + use_offset_0x00a0u];
-      if (hb_in_range (u, 0x0900u, 0x0DF7u)) return use_table[u - 0x0900u + use_offset_0x0900u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x0028u, 0x003Fu)) return use_table[u - 0x0028u + use_offset_0x0028u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x00A0u, 0x00D7u)) return use_table[u - 0x00A0u + use_offset_0x00a0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x0900u, 0x0DF7u)) return use_table[u - 0x0900u + use_offset_0x0900u];
       if (unlikely (u == 0x034Fu)) return CGJ;
       break;
 
     case 0x1u:
-      if (hb_in_range (u, 0x1000u, 0x109Fu)) return use_table[u - 0x1000u + use_offset_0x1000u];
-      if (hb_in_range (u, 0x1700u, 0x17EFu)) return use_table[u - 0x1700u + use_offset_0x1700u];
-      if (hb_in_range (u, 0x1900u, 0x1A9Fu)) return use_table[u - 0x1900u + use_offset_0x1900u];
-      if (hb_in_range (u, 0x1B00u, 0x1C4Fu)) return use_table[u - 0x1B00u + use_offset_0x1b00u];
-      if (hb_in_range (u, 0x1CD0u, 0x1CFFu)) return use_table[u - 0x1CD0u + use_offset_0x1cd0u];
-      if (hb_in_range (u, 0x1DF8u, 0x1DFFu)) return use_table[u - 0x1DF8u + use_offset_0x1df8u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1000u, 0x109Fu)) return use_table[u - 0x1000u + use_offset_0x1000u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1700u, 0x17EFu)) return use_table[u - 0x1700u + use_offset_0x1700u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1900u, 0x1A9Fu)) return use_table[u - 0x1900u + use_offset_0x1900u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1B00u, 0x1C4Fu)) return use_table[u - 0x1B00u + use_offset_0x1b00u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1CD0u, 0x1CFFu)) return use_table[u - 0x1CD0u + use_offset_0x1cd0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x1DF8u, 0x1DFFu)) return use_table[u - 0x1DF8u + use_offset_0x1df8u];
       break;
 
     case 0x2u:
-      if (hb_in_range (u, 0x2008u, 0x2017u)) return use_table[u - 0x2008u + use_offset_0x2008u];
-      if (hb_in_range (u, 0x2060u, 0x2087u)) return use_table[u - 0x2060u + use_offset_0x2060u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x2008u, 0x2017u)) return use_table[u - 0x2008u + use_offset_0x2008u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x2060u, 0x2087u)) return use_table[u - 0x2060u + use_offset_0x2060u];
       if (unlikely (u == 0x25CCu)) return GB;
       break;
 
     case 0xAu:
-      if (hb_in_range (u, 0xA800u, 0xAAF7u)) return use_table[u - 0xA800u + use_offset_0xa800u];
-      if (hb_in_range (u, 0xABC0u, 0xABFFu)) return use_table[u - 0xABC0u + use_offset_0xabc0u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xA800u, 0xAAF7u)) return use_table[u - 0xA800u + use_offset_0xa800u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xABC0u, 0xABFFu)) return use_table[u - 0xABC0u + use_offset_0xabc0u];
       break;
 
     case 0xFu:
-      if (hb_in_range (u, 0xFE00u, 0xFE0Fu)) return use_table[u - 0xFE00u + use_offset_0xfe00u];
+      if (hb_in_range<hb_codepoint_t> (u, 0xFE00u, 0xFE0Fu)) return use_table[u - 0xFE00u + use_offset_0xfe00u];
       break;
 
     case 0x10u:
-      if (hb_in_range (u, 0x10A00u, 0x10A47u)) return use_table[u - 0x10A00u + use_offset_0x10a00u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x10A00u, 0x10A47u)) return use_table[u - 0x10A00u + use_offset_0x10a00u];
       break;
 
     case 0x11u:
-      if (hb_in_range (u, 0x11000u, 0x110BFu)) return use_table[u - 0x11000u + use_offset_0x11000u];
-      if (hb_in_range (u, 0x11100u, 0x1123Fu)) return use_table[u - 0x11100u + use_offset_0x11100u];
-      if (hb_in_range (u, 0x11280u, 0x11377u)) return use_table[u - 0x11280u + use_offset_0x11280u];
-      if (hb_in_range (u, 0x11400u, 0x114DFu)) return use_table[u - 0x11400u + use_offset_0x11400u];
-      if (hb_in_range (u, 0x11580u, 0x1173Fu)) return use_table[u - 0x11580u + use_offset_0x11580u];
-      if (hb_in_range (u, 0x11C00u, 0x11CB7u)) return use_table[u - 0x11C00u + use_offset_0x11c00u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x11000u, 0x110BFu)) return use_table[u - 0x11000u + use_offset_0x11000u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x11100u, 0x1123Fu)) return use_table[u - 0x11100u + use_offset_0x11100u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x11280u, 0x11377u)) return use_table[u - 0x11280u + use_offset_0x11280u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x11400u, 0x114DFu)) return use_table[u - 0x11400u + use_offset_0x11400u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x11580u, 0x1173Fu)) return use_table[u - 0x11580u + use_offset_0x11580u];
+      if (hb_in_range<hb_codepoint_t> (u, 0x11C00u, 0x11CB7u)) return use_table[u - 0x11C00u + use_offset_0x11c00u];
       if (unlikely (u == 0x1107Fu)) return HN;
       break;
 
     default:
       break;
   }
   return USE_O;
 }
--- a/gfx/harfbuzz/src/hb-private.hh
+++ b/gfx/harfbuzz/src/hb-private.hh
@@ -69,20 +69,19 @@ extern "C" void  hb_free_impl(void *ptr)
 #define realloc hb_realloc_impl
 #define free hb_free_impl
 #endif
 
 
 /* Compiler attributes */
 
 
-#if defined(__GNUC__) && (__GNUC__ > 2) && defined(__OPTIMIZE__)
-#define _HB_BOOLEAN_EXPR(expr) ((expr) ? 1 : 0)
-#define likely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 1))
-#define unlikely(expr) (__builtin_expect (_HB_BOOLEAN_EXPR(expr), 0))
+#if (defined(__GNUC__) || defined(__clang__)) && defined(__OPTIMIZE__)
+#define likely(expr) (__builtin_expect (!!(expr), 1))
+#define unlikely(expr) (__builtin_expect (!!(expr), 0))
 #else
 #define likely(expr) (expr)
 #define unlikely(expr) (expr)
 #endif
 
 #if !defined(__GNUC__) && !defined(__clang__)
 #undef __attribute__
 #define __attribute__(x)
@@ -163,31 +162,27 @@ extern "C" void  hb_free_impl(void *ptr)
 #    define WIN32_LEAN_AND_MEAN 1
 #  endif
 #  ifndef STRICT
 #    define STRICT 1
 #  endif
 
 #  if defined(_WIN32_WCE)
      /* Some things not defined on Windows CE. */
-#    define strdup _strdup
 #    define vsnprintf _vsnprintf
 #    define getenv(Name) NULL
 #    if _WIN32_WCE < 0x800
 #      define setlocale(Category, Locale) "C"
 static int errno = 0; /* Use something better? */
 #    endif
 #  elif defined(WINAPI_FAMILY) && (WINAPI_FAMILY==WINAPI_FAMILY_PC_APP || WINAPI_FAMILY==WINAPI_FAMILY_PHONE_APP)
 #    define getenv(Name) NULL
 #  endif
 #  if defined(_MSC_VER) && _MSC_VER < 1900
 #    define snprintf _snprintf
-#  elif defined(_MSC_VER) && _MSC_VER >= 1900
-#    /* Covers VC++ Error for strdup being a deprecated POSIX name and to instead use _strdup instead */
-#    define strdup _strdup
 #  endif
 #endif
 
 #if HAVE_ATEXIT
 /* atexit() is only safe to be called from shared libraries on certain
  * platforms.  Whitelist.
  * https://bugs.freedesktop.org/show_bug.cgi?id=82246 */
 #  if defined(__linux) && defined(__GLIBC_PREREQ)
--- a/gfx/harfbuzz/src/hb-unicode-private.hh
+++ b/gfx/harfbuzz/src/hb-unicode-private.hh
@@ -121,17 +121,17 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIM
     return _hb_modified_combining_class[combining_class (unicode)];
   }
 
   static inline hb_bool_t
   is_variation_selector (hb_codepoint_t unicode)
   {
     /* U+180B..180D MONGOLIAN FREE VARIATION SELECTORs are handled in the
      * Arabic shaper.  No need to match them here. */
-    return unlikely (hb_in_ranges (unicode,
+    return unlikely (hb_in_ranges<hb_codepoint_t> (unicode,
 				   0xFE00u, 0xFE0Fu, /* VARIATION SELECTOR-1..16 */
 				   0xE0100u, 0xE01EFu));  /* VARIATION SELECTOR-17..256 */
   }
 
   /* Default_Ignorable codepoints:
    *
    * Note: While U+115F, U+1160, U+3164 and U+FFA0 are Default_Ignorable,
    * we do NOT want to hide them, as the way Uniscribe has implemented them
@@ -174,33 +174,33 @@ HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS_SIM
     if (likely (plane == 0))
     {
       /* BMP */
       hb_codepoint_t page = ch >> 8;
       switch (page) {
 	case 0x00: return unlikely (ch == 0x00ADu);
 	case 0x03: return unlikely (ch == 0x034Fu);
 	case 0x06: return unlikely (ch == 0x061Cu);
-	case 0x17: return hb_in_range (ch, 0x17B4u, 0x17B5u);
-	case 0x18: return hb_in_range (ch, 0x180Bu, 0x180Eu);
-	case 0x20: return hb_in_ranges (ch, 0x200Bu, 0x200Fu,
+	case 0x17: return hb_in_range<hb_codepoint_t> (ch, 0x17B4u, 0x17B5u);
+	case 0x18: return hb_in_range<hb_codepoint_t> (ch, 0x180Bu, 0x180Eu);
+	case 0x20: return hb_in_ranges<hb_codepoint_t> (ch, 0x200Bu, 0x200Fu,
 					    0x202Au, 0x202Eu,
 					    0x2060u, 0x206Fu);
-	case 0xFE: return hb_in_range (ch, 0xFE00u, 0xFE0Fu) || ch == 0xFEFFu;
-	case 0xFF: return hb_in_range (ch, 0xFFF0u, 0xFFF8u);
+	case 0xFE: return hb_in_range<hb_codepoint_t> (ch, 0xFE00u, 0xFE0Fu) || ch == 0xFEFFu;
+	case 0xFF: return hb_in_range<hb_codepoint_t> (ch, 0xFFF0u, 0xFFF8u);
 	default: return false;
       }
     }
     else
     {
       /* Other planes */
       switch (plane) {
-	case 0x01: return hb_in_ranges (ch, 0x1BCA0u, 0x1BCA3u,
+	case 0x01: return hb_in_ranges<hb_codepoint_t> (ch, 0x1BCA0u, 0x1BCA3u,
 					    0x1D173u, 0x1D17Au);
-	case 0x0E: return hb_in_range (ch, 0xE0000u, 0xE0FFFu);
+	case 0x0E: return hb_in_range<hb_codepoint_t> (ch, 0xE0000u, 0xE0FFFu);
 	default: return false;
       }
     }
   }
 
   /* Space estimates based on:
    * http://www.unicode.org/charts/PDF/U2000.pdf
    * https://www.microsoft.com/typography/developers/fdsspec/spaces.aspx
@@ -341,22 +341,23 @@ extern HB_INTERNAL const hb_unicode_func
 #define HB_MODIFIED_COMBINING_CLASS_CCC103 3 /* sara u / sara uu */
 #define HB_MODIFIED_COMBINING_CLASS_CCC107 107 /* mai * */
 
 /* Lao */
 #define HB_MODIFIED_COMBINING_CLASS_CCC118 118 /* sign u / sign uu */
 #define HB_MODIFIED_COMBINING_CLASS_CCC122 122 /* mai * */
 
 /* Tibetan
- * Modify U+0F74 (ccc=132) to reorder before ccc=130 marks.
+ * 
+ * In case of multiple vowel-signs, use u first (but after achung) 
+ * this allows Dzongkha multi-vowel shortcuts to render correctly 
  */
 #define HB_MODIFIED_COMBINING_CLASS_CCC129 129 /* sign aa */
-#define HB_MODIFIED_COMBINING_CLASS_CCC130 130 /* sign i */
-#define HB_MODIFIED_COMBINING_CLASS_CCC132 128 /* sign u */
-
+#define HB_MODIFIED_COMBINING_CLASS_CCC130 132 /* sign i */
+#define HB_MODIFIED_COMBINING_CLASS_CCC132 131 /* sign u */
 
 /* Misc */
 
 #define HB_UNICODE_GENERAL_CATEGORY_IS_MARK(gen_cat) \
 	(FLAG_SAFE (gen_cat) & \
 	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \
 	  FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \
 	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
--- a/gfx/harfbuzz/src/hb-utf-private.hh
+++ b/gfx/harfbuzz/src/hb-utf-private.hh
@@ -43,53 +43,53 @@ struct hb_utf8_t
     /* Written to only accept well-formed sequences.
      * Based on ideas from ICU's U8_NEXT.
      * Generates one "replacement" for each ill-formed byte. */
 
     hb_codepoint_t c = *text++;
 
     if (c > 0x7Fu)
     {
-      if (hb_in_range (c, 0xC2u, 0xDFu)) /* Two-byte */
+      if (hb_in_range<hb_codepoint_t> (c, 0xC2u, 0xDFu)) /* Two-byte */
       {
 	unsigned int t1;
 	if (likely (text < end &&
 		    (t1 = text[0] - 0x80u) <= 0x3Fu))
 	{
 	  c = ((c&0x1Fu)<<6) | t1;
 	  text++;
 	}
 	else
 	  goto error;
       }
-      else if (hb_in_range (c, 0xE0u, 0xEFu)) /* Three-byte */
+      else if (hb_in_range<hb_codepoint_t> (c, 0xE0u, 0xEFu)) /* Three-byte */
       {
 	unsigned int t1, t2;
 	if (likely (1 < end - text &&
 		    (t1 = text[0] - 0x80u) <= 0x3Fu &&
 		    (t2 = text[1] - 0x80u) <= 0x3Fu))
 	{
 	  c = ((c&0xFu)<<12) | (t1<<6) | t2;
-	  if (unlikely (c < 0x0800u || hb_in_range (c, 0xD800u, 0xDFFFu)))
+	  if (unlikely (c < 0x0800u || hb_in_range<hb_codepoint_t> (c, 0xD800u, 0xDFFFu)))
 	    goto error;
 	  text += 2;
 	}
 	else
 	  goto error;
       }
-      else if (hb_in_range (c, 0xF0u, 0xF4u)) /* Four-byte */
+      else if (hb_in_range<hb_codepoint_t> (c, 0xF0u, 0xF4u)) /* Four-byte */
       {
 	unsigned int t1, t2, t3;
 	if (likely (2 < end - text &&
 		    (t1 = text[0] - 0x80u) <= 0x3Fu &&
 		    (t2 = text[1] - 0x80u) <= 0x3Fu &&
 		    (t3 = text[2] - 0x80u) <= 0x3Fu))
 	{
 	  c = ((c&0x7u)<<18) | (t1<<12) | (t2<<6) | t3;
-	  if (unlikely (!hb_in_range (c, 0x10000u, 0x10FFFFu)))
+	  if (unlikely (!hb_in_range<hb_codepoint_t> (c, 0x10000u, 0x10FFFFu)))
 	    goto error;
 	  text += 3;
 	}
 	else
 	  goto error;
       }
       else
 	goto error;
@@ -135,27 +135,27 @@ struct hb_utf16_t
   static inline const uint16_t *
   next (const uint16_t *text,
 	const uint16_t *end,
 	hb_codepoint_t *unicode,
 	hb_codepoint_t replacement)
   {
     hb_codepoint_t c = *text++;
 
-    if (likely (!hb_in_range (c, 0xD800u, 0xDFFFu)))
+    if (likely (!hb_in_range<hb_codepoint_t> (c, 0xD800u, 0xDFFFu)))
     {
       *unicode = c;
       return text;
     }
 
     if (likely (c <= 0xDBFFu && text < end))
     {
       /* High-surrogate in c */
       hb_codepoint_t l = *text;
-      if (likely (hb_in_range (l, 0xDC00u, 0xDFFFu)))
+      if (likely (hb_in_range<hb_codepoint_t> (l, 0xDC00u, 0xDFFFu)))
       {
 	/* Low-surrogate in l */
 	*unicode = (c << 10) + l - ((0xD800u << 10) - 0x10000u + 0xDC00u);
 	 text++;
 	 return text;
       }
     }
 
@@ -167,27 +167,27 @@ struct hb_utf16_t
   static inline const uint16_t *
   prev (const uint16_t *text,
 	const uint16_t *start,
 	hb_codepoint_t *unicode,
 	hb_codepoint_t replacement)
   {
     hb_codepoint_t c = *--text;
 
-    if (likely (!hb_in_range (c, 0xD800u, 0xDFFFu)))
+    if (likely (!hb_in_range<hb_codepoint_t> (c, 0xD800u, 0xDFFFu)))
     {
       *unicode = c;
       return text;
     }
 
     if (likely (c >= 0xDC00u && start < text))
     {
       /* Low-surrogate in c */
       hb_codepoint_t h = text[-1];
-      if (likely (hb_in_range (h, 0xD800u, 0xDBFFu)))
+      if (likely (hb_in_range<hb_codepoint_t> (h, 0xD800u, 0xDBFFu)))
       {
         /* High-surrogate in h */
         *unicode = (h << 10) + c - ((0xD800u << 10) - 0x10000u + 0xDC00u);
         text--;
         return text;
       }
     }
 
--- 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 1
 #define HB_VERSION_MINOR 4
-#define HB_VERSION_MICRO 6
+#define HB_VERSION_MICRO 7
 
-#define HB_VERSION_STRING "1.4.6"
+#define HB_VERSION_STRING "1.4.7"
 
 #define HB_VERSION_ATLEAST(major,minor,micro) \
 	((major)*10000+(minor)*100+(micro) <= \
 	 HB_VERSION_MAJOR*10000+HB_VERSION_MINOR*100+HB_VERSION_MICRO)
 
 
 HB_EXTERN void
 hb_version (unsigned int *major,