bug 789687 - update harfbuzz to upstream commit fecdfa95daf4916695f23e7dab89ab363be11b89. r=jdaggett
authorJonathan Kew <jkew@mozilla.com>
Tue, 09 Oct 2012 13:11:24 +0100
changeset 109741 550038369ab1731d567f7a9f8e78cb036fe8a95f
parent 109740 757d21e66392156e973f18e52bb106ed157bfdfd
child 109742 afc8967a5cea136df44b739077e564c66d9aef10
push id23652
push userryanvm@gmail.com
push dateWed, 10 Oct 2012 01:10:20 +0000
treeherdermozilla-central@5cca0408a73f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjdaggett
bugs789687
milestone19.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 789687 - update harfbuzz to upstream commit fecdfa95daf4916695f23e7dab89ab363be11b89. r=jdaggett
gfx/harfbuzz/src/Makefile.am
gfx/harfbuzz/src/check-libstdc++.sh
gfx/harfbuzz/src/check-static-inits.sh
gfx/harfbuzz/src/gen-arabic-table.py
gfx/harfbuzz/src/hb-atomic-private.hh
gfx/harfbuzz/src/hb-buffer-private.hh
gfx/harfbuzz/src/hb-buffer.cc
gfx/harfbuzz/src/hb-buffer.h
gfx/harfbuzz/src/hb-common.cc
gfx/harfbuzz/src/hb-common.h
gfx/harfbuzz/src/hb-font.cc
gfx/harfbuzz/src/hb-graphite2.cc
gfx/harfbuzz/src/hb-icu-le.cc
gfx/harfbuzz/src/hb-mutex-private.hh
gfx/harfbuzz/src/hb-old.cc
gfx/harfbuzz/src/hb-open-file-private.hh
gfx/harfbuzz/src/hb-open-type-private.hh
gfx/harfbuzz/src/hb-ot-head-table.hh
gfx/harfbuzz/src/hb-ot-hhea-table.hh
gfx/harfbuzz/src/hb-ot-hmtx-table.hh
gfx/harfbuzz/src/hb-ot-layout-common-private.hh
gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
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-layout.h
gfx/harfbuzz/src/hb-ot-map-private.hh
gfx/harfbuzz/src/hb-ot-map.cc
gfx/harfbuzz/src/hb-ot-maxp-table.hh
gfx/harfbuzz/src/hb-ot-name-table.hh
gfx/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh
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-indic-machine.hh
gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl
gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
gfx/harfbuzz/src/hb-ot-shape-fallback-private.hh
gfx/harfbuzz/src/hb-ot-shape-fallback.cc
gfx/harfbuzz/src/hb-ot-shape-normalize-private.hh
gfx/harfbuzz/src/hb-ot-shape-normalize.cc
gfx/harfbuzz/src/hb-ot-shape-private.hh
gfx/harfbuzz/src/hb-ot-shape.cc
gfx/harfbuzz/src/hb-private.hh
gfx/harfbuzz/src/hb-shape.cc
gfx/harfbuzz/src/hb-shape.h
gfx/harfbuzz/src/hb-shaper-list.hh
gfx/harfbuzz/src/hb-ucdn.cc
gfx/harfbuzz/src/hb-unicode-private.hh
gfx/harfbuzz/src/hb-unicode.cc
gfx/harfbuzz/src/hb-uniscribe.cc
gfx/harfbuzz/src/hb-utf-private.hh
gfx/harfbuzz/src/hb-version.h
gfx/harfbuzz/src/main.cc
gfx/harfbuzz/src/test-would-substitute.cc
--- a/gfx/harfbuzz/src/Makefile.am
+++ b/gfx/harfbuzz/src/Makefile.am
@@ -46,16 +46,17 @@ HBSOURCES =  \
 	hb-shape-plan.h \
 	hb-shaper-list.hh \
 	hb-shaper-impl-private.hh \
 	hb-shaper-private.hh \
 	hb-shaper.cc \
 	hb-tt-font.cc \
 	hb-unicode-private.hh \
 	hb-unicode.cc \
+	hb-utf-private.hh \
 	hb-warning.cc \
 	$(NULL)
 HBHEADERS = \
 	hb.h \
 	hb-blob.h \
 	hb-buffer.h \
 	hb-common.h \
 	hb-font.h \
@@ -73,16 +74,17 @@ HBSOURCES += \
 	hb-ot-layout-gpos-table.hh \
 	hb-ot-layout-gsubgpos-private.hh \
 	hb-ot-layout-gsub-table.hh \
 	hb-ot-layout-private.hh \
 	hb-ot-map.cc \
 	hb-ot-map-private.hh \
 	hb-ot-shape.cc \
 	hb-ot-shape-complex-arabic.cc \
+	hb-ot-shape-complex-arabic-fallback.hh \
 	hb-ot-shape-complex-arabic-table.hh \
 	hb-ot-shape-complex-indic.cc \
 	hb-ot-shape-complex-indic-machine.hh \
 	hb-ot-shape-complex-indic-private.hh \
 	hb-ot-shape-complex-indic-table.hh \
 	hb-ot-shape-complex-misc.cc \
 	hb-ot-shape-complex-private.hh \
 	hb-ot-shape-normalize-private.hh \
@@ -93,16 +95,21 @@ HBSOURCES += \
 	$(NULL)
 HBHEADERS += \
 	hb-ot.h \
 	hb-ot-layout.h \
 	hb-ot-tag.h \
 	$(NULL)
 endif
 
+if HAVE_PTHREAD
+HBCFLAGS += $(PTHREAD_CFLAGS)
+HBLIBS   += $(PTHREAD_LIBS)
+endif
+
 if HAVE_GLIB
 HBCFLAGS += $(GLIB_CFLAGS)
 HBLIBS   += $(GLIB_LIBS)
 HBSOURCES += hb-glib.cc
 HBHEADERS += hb-glib.h
 endif
 
 if HAVE_GOBJECT
@@ -158,33 +165,50 @@ endif
 if HAVE_HB_OLD
 SUBDIRS += hb-old
 HBCFLAGS += -I$(srcdir)/hb-old
 HBLIBS   += hb-old/libhb-old.la
 HBSOURCES += hb-old.cc
 endif
 DIST_SUBDIRS += hb-old
 
+if HAVE_ICU_LE
+SUBDIRS += hb-icu-le
+HBCFLAGS += -I$(srcdir)/hb-icu-le
+HBLIBS   += hb-icu-le/libhb-icu-le.la
+HBSOURCES += hb-icu-le.cc
+endif
+DIST_SUBDIRS += hb-icu-le
+
+if HAVE_UCDN
+SUBDIRS += hb-ucdn
+HBCFLAGS += -I$(srcdir)/hb-ucdn
+HBLIBS   += hb-ucdn/libhb-ucdn.la
+HBSOURCES += hb-ucdn.cc
+endif
+DIST_SUBDIRS += hb-ucdn
 
 
 # Put the library together
 
 if OS_WIN32
 export_symbols = -export-symbols harfbuzz.def
 harfbuzz_def_dependency = harfbuzz.def
+libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS)
+else
+# Use a C linker, not C++; Don't link to libstdc++
+libharfbuzz_la_LINK = $(LINK) $(libharfbuzz_la_LDFLAGS)
 endif
 
-# Use a C linker, not C++; Don't link to libstdc++
-libharfbuzz_la_LINK = $(LINK) $(libharfbuzz_la_LDFLAGS)
 libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS)
 nodist_libharfbuzz_la_SOURCES = $(nodist_HBSOURCES)
 libharfbuzz_la_CPPFLAGS = $(HBCFLAGS)
 libharfbuzz_la_LDFLAGS = $(AM_LDFLAGS) -version-info $(HB_LIBTOOL_VERSION_INFO) $(export_symbols) -no-undefined
 libharfbuzz_la_LIBADD = $(HBLIBS)
-libharfbuzz_la_DEPENDENCIES = $(harfbuzz_def_dependency)
+EXTRA_libharfbuzz_la_DEPENDENCIES = $(harfbuzz_def_dependency)
 pkginclude_HEADERS = $(HBHEADERS)
 nodist_pkginclude_HEADERS = hb-version.h
 
 CLEANFILES += harfbuzz.def
 harfbuzz.def: $(HBHEADERS)
 	$(AM_V_GEN) (echo EXPORTS; \
 	(cat $^ || echo 'hb_ERROR ()' ) | \
 	$(EGREP) '^hb_.* \(' | \
@@ -212,51 +236,50 @@ indic-table: gen-indic-table.py IndicSyl
 arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt
 	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-arabic-table.hh.tmp && \
 	mv hb-ot-shape-complex-arabic-table.hh.tmp $(srcdir)/hb-ot-shape-complex-arabic-table.hh || \
 	($(RM) hb-ot-shape-complex-arabic-table.hh.tmp; false)
 
 
 .PHONY: unicode-tables arabic-table indic-table
 
-BUILT_SOURCES += hb-ot-shape-complex-indic-machine.hh
 EXTRA_DIST += hb-ot-shape-complex-indic-machine.rl
 hb-ot-shape-complex-indic-machine.hh: hb-ot-shape-complex-indic-machine.rl
 	$(AM_V_GEN)$(top_srcdir)/missing --run ragel -e -F1 -o "$@.tmp" "$<" && \
 	mv "$@.tmp" "$@" || ( $(RM) "$@.tmp" && false )
 
-noinst_PROGRAMS = main indic test-would-substitute
+noinst_PROGRAMS = main test-would-substitute
 bin_PROGRAMS =
 
 main_SOURCES = main.cc
 main_CPPFLAGS = $(HBCFLAGS)
 main_LDADD = libharfbuzz.la $(HBLIBS)
 
-indic_SOURCES = indic.cc
-indic_CPPFLAGS = $(HBCFLAGS)
-indic_LDADD = libharfbuzz.la $(HBLIBS)
-
 test_would_substitute_SOURCES = test-would-substitute.cc
 test_would_substitute_CPPFLAGS = $(HBCFLAGS) $(FREETYPE_CFLAGS)
 test_would_substitute_LDADD = libharfbuzz.la $(HBLIBS) $(FREETYPE_LIBS)
 
 dist_check_SCRIPTS = \
 	check-c-linkage-decls.sh \
 	check-header-guards.sh \
 	check-exported-symbols.sh \
 	check-includes.sh \
 	check-internal-symbols.sh \
-	check-static-inits.sh \
 	$(NULL)
 
 if HAVE_ICU
 else
 dist_check_SCRIPTS += check-libstdc++.sh
 endif
 
+if HAVE_ICU_LE
+else
+dist_check_SCRIPTS += check-static-inits.sh
+endif
+
 TESTS = $(dist_check_SCRIPTS)
 TESTS_ENVIRONMENT = \
 	srcdir="$(srcdir)" \
 	MAKE="$(MAKE) $(AM_MAKEFLAGS)" \
 	HBSOURCES="$(HBSOURCES)" \
 	HBHEADERS="$(HBHEADERS)" \
 	$(NULL)
 
--- a/gfx/harfbuzz/src/check-libstdc++.sh
+++ b/gfx/harfbuzz/src/check-libstdc++.sh
@@ -22,13 +22,13 @@ for suffix in so dylib; do
 		if ldd $so | grep 'libstdc[+][+]'; then
 			echo "Ouch, linked to libstdc++"
 			stat=1
 		fi
 		tested=true
 	fi
 done
 if ! $tested; then
-	echo "check-internal-symbols.sh: libharfbuzz shared library not found; skipping test"
+	echo "check-libstdc++.sh: libharfbuzz shared library not found; skipping test"
 	exit 77
 fi
 
 exit $stat
--- a/gfx/harfbuzz/src/check-static-inits.sh
+++ b/gfx/harfbuzz/src/check-static-inits.sh
@@ -23,17 +23,17 @@ fi
 echo "Checking that no object file has static initializers"
 for obj in $OBJS; do
 	if objdump -t "$obj" | grep '[.]ctors'; then
 		echo "Ouch, $obj has static initializers"
 		stat=1
 	fi
 done
 
-echo "Checking that no object file has lazy static C++ constructors/destructors"
+echo "Checking that no object file has lazy static C++ constructors/destructors or other such stuff"
 for obj in $OBJS; do
 	if objdump -t "$obj" | grep '__c'; then
-		echo "Ouch, $obj has lazy static C++ constructors/destructors"
+		echo "Ouch, $obj has lazy static C++ constructors/destructors or other such stuff"
 		stat=1
 	fi
 done
 
 exit $stat
--- a/gfx/harfbuzz/src/gen-arabic-table.py
+++ b/gfx/harfbuzz/src/gen-arabic-table.py
@@ -117,17 +117,17 @@ def print_shaping_table(f):
 
 	print
 	print "static const uint16_t shaping_table[][4] ="
 	print "{"
 
 	keys = shapes.keys ()
 	min_u, max_u = min (keys), max (keys)
 	for u in range (min_u, max_u + 1):
-		s = [shapes[u][shape] if u in shapes and shape in shapes[u] else u
+		s = [shapes[u][shape] if u in shapes and shape in shapes[u] else 0
 		     for shape in  ['initial', 'medial', 'final', 'isolated']]
 		value = ', '.join ("0x%04X" % c for c in s)
 		print "  {%s}, /* U+%04X %s */" % (value, u, names[u] if u in names else "")
 
 	print "};"
 	print
 	print "#define SHAPING_TABLE_FIRST	0x%04X" % min_u
 	print "#define SHAPING_TABLE_LAST	0x%04X" % max_u
@@ -143,19 +143,19 @@ def print_shaping_table(f):
 				liga = (shapes[pair[0]]['medial'], shapes[pair[1]]['final'])
 			else:
 				raise Exception ("Unexpected shape", shape)
 			if liga[0] not in ligas:
 				ligas[liga[0]] = []
 			ligas[liga[0]].append ((liga[1], c))
 	max_i = max (len (ligas[l]) for l in ligas)
 	print
-	print "static const struct {"
+	print "static const struct ligature_set_t {"
 	print " uint16_t first;"
-	print " struct {"
+	print " struct ligature_pairs_t {"
 	print "   uint16_t second;"
 	print "   uint16_t ligature;"
 	print " } ligatures[%d];" % max_i
 	print "} ligature_table[] ="
 	print "{"
 	keys = ligas.keys ()
 	keys.sort ()
 	for first in keys:
--- a/gfx/harfbuzz/src/hb-atomic-private.hh
+++ b/gfx/harfbuzz/src/hb-atomic-private.hh
@@ -37,27 +37,38 @@
 
 /* atomic_int */
 
 /* We need external help for these */
 
 #if 0
 
 
-#elif !defined(HB_NO_MT) && defined(_MSC_VER) && _MSC_VER >= 1600
+#elif !defined(HB_NO_MT) && defined(_MSC_VER) || defined(__MINGW32__)
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
 
-#include <intrin.h>
-/* On x86, _InterlockedCompareExchangePointer is a macro defined in concrt.h */
-#include <concrt.h>
+/* mingw32 does not have MemoryBarrier.
+ * MemoryBarrier may be defined as a macro or a function.
+ * Just make a failsafe version for ourselves. */
+#ifdef MemoryBarrier
+#define HBMemoryBarrier MemoryBarrier
+#else
+static inline void HBMemoryBarrier (void) {
+  long dummy = 0;
+  InterlockedExchange (&dummy, 1);
+}
+#endif
 
 typedef long hb_atomic_int_t;
-#define hb_atomic_int_add(AI, V)	_InterlockedExchangeAdd (&(AI), (V))
+#define hb_atomic_int_add(AI, V)	InterlockedExchangeAdd (&(AI), (V))
 
-#define hb_atomic_ptr_get(P)		(MemoryBarrier (), (void *) *(P))
-#define hb_atomic_ptr_cmpexch(P,O,N)	(_InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O))
+#define hb_atomic_ptr_get(P)		(HBMemoryBarrier (), (void *) *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N)	(InterlockedCompareExchangePointer ((void **) (P), (void *) (N), (void *) (O)) == (void *) (O))
 
 
 #elif !defined(HB_NO_MT) && defined(__APPLE__)
 
 #include <libkern/OSAtomic.h>
 
 typedef int32_t hb_atomic_int_t;
 #define hb_atomic_int_add(AI, V)	(OSAtomicAdd32Barrier ((V), &(AI)) - (V))
@@ -69,29 +80,16 @@ typedef int32_t hb_atomic_int_t;
 #elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
 
 typedef int hb_atomic_int_t;
 #define hb_atomic_int_add(AI, V)	__sync_fetch_and_add (&(AI), (V))
 
 #define hb_atomic_ptr_get(P)		(void *) (__sync_synchronize (), *(P))
 #define hb_atomic_ptr_cmpexch(P,O,N)	__sync_bool_compare_and_swap ((P), (O), (N))
 
-#elif !defined(HB_NO_MT) && defined(HAVE_GLIB)
-
-#include <glib.h>
-typedef int hb_atomic_int_t;
-#if GLIB_CHECK_VERSION(2,29,5)
-#define hb_atomic_int_add(AI, V)	g_atomic_int_add (&(AI), (V))
-#else
-#define hb_atomic_int_add(AI, V)	g_atomic_int_exchange_and_add (&(AI), (V))
-#endif
-
-#define hb_atomic_ptr_get(P)		g_atomic_pointer_get (P)
-#define hb_atomic_ptr_cmpexch(P,O,N)	g_atomic_pointer_compare_and_exchange ((void **) (P), (void *) (O), (void *) (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_t;
 #define hb_atomic_int_add(AI, V)	(((AI) += (V)) - (V))
 
 #define hb_atomic_ptr_get(P)		((void *) *(P))
--- a/gfx/harfbuzz/src/hb-buffer-private.hh
+++ b/gfx/harfbuzz/src/hb-buffer-private.hh
@@ -1,12 +1,12 @@
 /*
  * Copyright © 1998-2004  David Turner and Werner Lemberg
  * Copyright © 2004,2007,2009,2010  Red Hat, Inc.
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -87,16 +87,18 @@ struct hb_buffer_t {
 
   /* Information about how the text in the buffer should be treated */
 
   hb_unicode_funcs_t *unicode; /* Unicode functions */
   hb_segment_properties_t props; /* Script, language, direction */
 
   /* Buffer contents */
 
+  hb_buffer_content_type_t content_type;
+
   bool in_error; /* Allocation failed */
   bool have_output; /* Whether we have an output buffer going on */
   bool have_positions; /* Whether we have positions */
 
   unsigned int idx; /* Cursor into ->info and ->pos arrays */
   unsigned int len; /* Length of ->info and ->pos arrays */
   unsigned int out_len; /* Length of ->out array if have_output */
 
@@ -110,30 +112,40 @@ struct hb_buffer_t {
 
   inline hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; }
   inline hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; }
 
   inline hb_glyph_info_t &prev (void) { return out_info[out_len - 1]; }
   inline hb_glyph_info_t prev (void) const { return info[out_len - 1]; }
 
   unsigned int serial;
+
+  /* These reflect current allocations of the bytes in glyph_info_t's var1 and var2. */
   uint8_t allocated_var_bytes[8];
   const char *allocated_var_owner[8];
 
+  /* Text before / after the main buffer contents.
+   * Always in Unicode, and ordered outward.
+   * Index 0 is for "pre-context", 1 for "post-context". */
+  static const unsigned int CONTEXT_LENGTH = 5;
+  hb_codepoint_t context[2][CONTEXT_LENGTH];
+  unsigned int context_len[2];
+
 
   /* Methods */
 
   HB_INTERNAL void reset (void);
 
   inline unsigned int backtrack_len (void) const
   { return have_output? out_len : idx; }
   inline unsigned int next_serial (void) { return serial++; }
 
   HB_INTERNAL void allocate_var (unsigned int byte_i, unsigned int count, const char *owner);
   HB_INTERNAL void deallocate_var (unsigned int byte_i, unsigned int count, const char *owner);
+  HB_INTERNAL void assert_var (unsigned int byte_i, unsigned int count, const char *owner);
   HB_INTERNAL void deallocate_var_all (void);
 
   HB_INTERNAL void add (hb_codepoint_t  codepoint,
 			hb_mask_t       mask,
 			unsigned int    cluster);
 
   HB_INTERNAL void reverse_range (unsigned int start, unsigned int end);
   HB_INTERNAL void reverse (void);
@@ -146,21 +158,36 @@ struct hb_buffer_t {
 
   HB_INTERNAL void replace_glyphs (unsigned int num_in,
 				   unsigned int num_out,
 				   const hb_codepoint_t *glyph_data);
 
   HB_INTERNAL void replace_glyph (hb_codepoint_t glyph_index);
   /* Makes a copy of the glyph at idx to output and replace glyph_index */
   HB_INTERNAL void output_glyph (hb_codepoint_t glyph_index);
+  HB_INTERNAL void output_info (hb_glyph_info_t &glyph_info);
   /* Copies glyph at idx to output but doesn't advance idx */
   HB_INTERNAL void copy_glyph (void);
   /* Copies glyph at idx to output and advance idx.
    * If there's no output, just advance idx. */
-  HB_INTERNAL void next_glyph (void);
+  inline void
+  next_glyph (void)
+  {
+    if (have_output)
+    {
+      if (unlikely (out_info != info || out_len != idx)) {
+	if (unlikely (!make_room_for (1, 1))) return;
+	out_info[out_len] = info[idx];
+      }
+      out_len++;
+    }
+
+    idx++;
+  }
+
   /* Advance idx without copying to output. */
   inline void skip_glyph (void) { idx++; }
 
   inline void reset_masks (hb_mask_t mask)
   {
     for (unsigned int j = 0; j < len; j++)
       info[j].mask = mask;
   }
@@ -183,21 +210,25 @@ struct hb_buffer_t {
   HB_INTERNAL bool enlarge (unsigned int size);
 
   inline bool ensure (unsigned int size)
   { return likely (size < allocated) ? true : enlarge (size); }
 
   HB_INTERNAL bool make_room_for (unsigned int num_in, unsigned int num_out);
 
   HB_INTERNAL void *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)
 #define HB_BUFFER_ALLOCATE_VAR(b, var) \
 	HB_BUFFER_XALLOCATE_VAR (b, allocate_var, var (), #var)
 #define HB_BUFFER_DEALLOCATE_VAR(b, var) \
 	HB_BUFFER_XALLOCATE_VAR (b, deallocate_var, var (), #var)
+#define HB_BUFFER_ASSERT_VAR(b, var) \
+	HB_BUFFER_XALLOCATE_VAR (b, assert_var, var (), #var)
 
 
 #endif /* HB_BUFFER_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-buffer.cc
+++ b/gfx/harfbuzz/src/hb-buffer.cc
@@ -1,12 +1,12 @@
 /*
  * Copyright © 1998-2004  David Turner and Werner Lemberg
  * Copyright © 2004,2007,2009,2010  Red Hat, Inc.
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -23,19 +23,17 @@
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Owen Taylor, Behdad Esfahbod
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-buffer-private.hh"
-
-#include <string.h>
-
+#include "hb-utf-private.hh"
 
 
 #ifndef HB_DEBUG_BUFFER
 #define HB_DEBUG_BUFFER (HB_DEBUG+0)
 #endif
 
 /* Here is how the buffer works internally:
  *
@@ -142,28 +140,32 @@ hb_buffer_t::reset (void)
     return;
 
   hb_unicode_funcs_destroy (unicode);
   unicode = hb_unicode_funcs_get_default ();
 
   hb_segment_properties_t default_props = _HB_BUFFER_PROPS_DEFAULT;
   props = default_props;
 
+  content_type = HB_BUFFER_CONTENT_TYPE_INVALID;
   in_error = false;
   have_output = false;
   have_positions = false;
 
   idx = 0;
   len = 0;
   out_len = 0;
   out_info = info;
 
   serial = 0;
   memset (allocated_var_bytes, 0, sizeof allocated_var_bytes);
   memset (allocated_var_owner, 0, sizeof allocated_var_owner);
+
+  memset (context, 0, sizeof context);
+  memset (context_len, 0, sizeof context_len);
 }
 
 void
 hb_buffer_t::add (hb_codepoint_t  codepoint,
 		  hb_mask_t       mask,
 		  unsigned int    cluster)
 {
   hb_glyph_info_t *glyph;
@@ -263,16 +265,26 @@ hb_buffer_t::output_glyph (hb_codepoint_
 
   out_info[out_len] = info[idx];
   out_info[out_len].codepoint = glyph_index;
 
   out_len++;
 }
 
 void
+hb_buffer_t::output_info (hb_glyph_info_t &glyph_info)
+{
+  if (unlikely (!make_room_for (0, 1))) return;
+
+  out_info[out_len] = glyph_info;
+
+  out_len++;
+}
+
+void
 hb_buffer_t::copy_glyph (void)
 {
   if (unlikely (!make_room_for (0, 1))) return;
 
   out_info[out_len] = info[idx];
 
   out_len++;
 }
@@ -285,31 +297,16 @@ hb_buffer_t::replace_glyph (hb_codepoint
     out_info[out_len] = info[idx];
   }
   out_info[out_len].codepoint = glyph_index;
 
   idx++;
   out_len++;
 }
 
-void
-hb_buffer_t::next_glyph (void)
-{
-  if (have_output)
-  {
-    if (unlikely (out_info != info || out_len != idx)) {
-      if (unlikely (!make_room_for (1, 1))) return;
-      out_info[out_len] = info[idx];
-    }
-    out_len++;
-  }
-
-  idx++;
-}
-
 
 void
 hb_buffer_t::set_masks (hb_mask_t    value,
 			hb_mask_t    mask,
 			unsigned int cluster_start,
 			unsigned int cluster_end)
 {
   hb_mask_t not_mask = ~mask;
@@ -446,16 +443,19 @@ hb_buffer_t::merge_out_clusters (unsigne
 
   for (unsigned int i = start; i < end; i++)
     out_info[i].cluster = cluster;
 }
 
 void
 hb_buffer_t::guess_properties (void)
 {
+  if (unlikely (!len)) return;
+  assert (content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
+
   /* If script is set to INVALID, guess from buffer contents */
   if (props.script == HB_SCRIPT_INVALID) {
     for (unsigned int i = 0; i < len; i++) {
       hb_script_t script = unicode->script (info[i].codepoint);
       if (likely (script != HB_SCRIPT_COMMON &&
 		  script != HB_SCRIPT_INHERITED &&
 		  script != HB_SCRIPT_UNKNOWN)) {
         props.script = script;
@@ -518,16 +518,32 @@ void hb_buffer_t::deallocate_var (unsign
   assert (byte_i < 8 && byte_i + count <= 8);
   for (unsigned int i = byte_i; i < byte_i + count; i++) {
     assert (allocated_var_bytes[i]);
     assert (0 == strcmp (allocated_var_owner[i], owner));
     allocated_var_bytes[i]--;
   }
 }
 
+void hb_buffer_t::assert_var (unsigned int byte_i, unsigned int count, const char *owner)
+{
+  if (DEBUG (BUFFER))
+    dump_var_allocation (this);
+
+  DEBUG_MSG (BUFFER, this,
+	     "Asserting var bytes %d..%d for %s",
+	     byte_i, byte_i + count - 1, owner);
+
+  assert (byte_i < 8 && byte_i + count <= 8);
+  for (unsigned int i = byte_i; i < byte_i + count; i++) {
+    assert (allocated_var_bytes[i]);
+    assert (0 == strcmp (allocated_var_owner[i], owner));
+  }
+}
+
 void hb_buffer_t::deallocate_var_all (void)
 {
   memset (allocated_var_bytes, 0, sizeof (allocated_var_bytes));
   memset (allocated_var_owner, 0, sizeof (allocated_var_owner));
 }
 
 /* Public API */
 
@@ -548,19 +564,22 @@ hb_buffer_t *
 hb_buffer_get_empty (void)
 {
   static const hb_buffer_t _hb_buffer_nil = {
     HB_OBJECT_HEADER_STATIC,
 
     const_cast<hb_unicode_funcs_t *> (&_hb_unicode_funcs_nil),
     _HB_BUFFER_PROPS_DEFAULT,
 
+    HB_BUFFER_CONTENT_TYPE_INVALID,
     true, /* in_error */
     true, /* have_output */
     true  /* have_positions */
+
+    /* Zero is good enough for everything else. */
   };
 
   return const_cast<hb_buffer_t *> (&_hb_buffer_nil);
 }
 
 hb_buffer_t *
 hb_buffer_reference (hb_buffer_t *buffer)
 {
@@ -594,16 +613,30 @@ void *
 hb_buffer_get_user_data (hb_buffer_t        *buffer,
 			 hb_user_data_key_t *key)
 {
   return hb_object_get_user_data (buffer, key);
 }
 
 
 void
+hb_buffer_set_content_type (hb_buffer_t              *buffer,
+			    hb_buffer_content_type_t  content_type)
+{
+  buffer->content_type = content_type;
+}
+
+hb_buffer_content_type_t
+hb_buffer_get_content_type (hb_buffer_t *buffer)
+{
+  return buffer->content_type;
+}
+
+
+void
 hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
 			     hb_unicode_funcs_t *unicode)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return;
 
   if (!unicode)
     unicode = hb_unicode_funcs_get_default ();
@@ -690,16 +723,17 @@ hb_buffer_allocation_successful (hb_buff
 
 void
 hb_buffer_add (hb_buffer_t    *buffer,
 	       hb_codepoint_t  codepoint,
 	       hb_mask_t       mask,
 	       unsigned int    cluster)
 {
   buffer->add (codepoint, mask, cluster);
+  buffer->clear_context (1);
 }
 
 hb_bool_t
 hb_buffer_set_length (hb_buffer_t  *buffer,
 		      unsigned int  length)
 {
   if (unlikely (hb_object_is_inert (buffer)))
     return length == 0;
@@ -710,16 +744,21 @@ hb_buffer_set_length (hb_buffer_t  *buff
   /* Wipe the new space */
   if (length > buffer->len) {
     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->clear_context (0);
+  buffer->clear_context (1);
+
   return true;
 }
 
 unsigned int
 hb_buffer_get_length (hb_buffer_t *buffer)
 {
   return buffer->len;
 }
@@ -762,136 +801,103 @@ hb_buffer_reverse_clusters (hb_buffer_t 
 }
 
 void
 hb_buffer_guess_properties (hb_buffer_t *buffer)
 {
   buffer->guess_properties ();
 }
 
-#define ADD_UTF(T) \
-	HB_STMT_START { \
-	  if (text_length == -1) { \
-	    text_length = 0; \
-	    const T *p = (const T *) text; \
-	    while (*p) { \
-	      text_length++; \
-	      p++; \
-	    } \
-	  } \
-	  if (item_length == -1) \
-	    item_length = text_length - item_offset; \
-	  buffer->ensure (buffer->len + item_length * sizeof (T) / 4); \
-	  const T *next = (const T *) text + item_offset; \
-	  const T *end = next + item_length; \
-	  while (next < end) { \
-	    hb_codepoint_t u; \
-	    const T *old_next = next; \
-	    next = UTF_NEXT (next, end, u); \
-	    hb_buffer_add (buffer, u, 1,  old_next - (const T *) text); \
-	  } \
-	} HB_STMT_END
+template <typename T>
+static inline void
+hb_buffer_add_utf (hb_buffer_t  *buffer,
+		   const T      *text,
+		   int           text_length,
+		   unsigned int  item_offset,
+		   int           item_length)
+{
+  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE ||
+	  (!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID));
 
+  if (unlikely (hb_object_is_inert (buffer)))
+    return;
 
-#define UTF8_COMPUTE(Char, Mask, Len) \
-  if (Char < 128) { Len = 1; Mask = 0x7f; } \
-  else if ((Char & 0xe0) == 0xc0) { Len = 2; Mask = 0x1f; } \
-  else if ((Char & 0xf0) == 0xe0) { Len = 3; Mask = 0x0f; } \
-  else if ((Char & 0xf8) == 0xf0) { Len = 4; Mask = 0x07; } \
-  else Len = 0;
+  if (text_length == -1)
+    text_length = hb_utf_strlen (text);
+
+  if (item_length == -1)
+    item_length = text_length - item_offset;
+
+  buffer->ensure (buffer->len + item_length * sizeof (T) / 4);
 
-static inline const uint8_t *
-hb_utf8_next (const uint8_t *text,
-	      const uint8_t *end,
-	      hb_codepoint_t *unicode)
-{
-  uint8_t c = *text;
-  unsigned int mask, len;
-
-  /* TODO check for overlong sequences? */
+  if (!buffer->len)
+  {
+    /* Add pre-context */
+    buffer->clear_context (0);
+    const T *prev = text + item_offset;
+    const T *start = text;
+    while (start < prev && buffer->context_len[0] < buffer->CONTEXT_LENGTH)
+    {
+      hb_codepoint_t u;
+      prev = hb_utf_prev (prev, start, &u);
+      buffer->context[0][buffer->context_len[0]++] = u;
+    }
+  }
 
-  UTF8_COMPUTE (c, mask, len);
-  if (unlikely (!len || (unsigned int) (end - text) < len)) {
-    *unicode = -1;
-    return text + 1;
-  } else {
-    hb_codepoint_t result;
-    unsigned int i;
-    result = c & mask;
-    for (i = 1; i < len; i++)
-      {
-	if (unlikely ((text[i] & 0xc0) != 0x80))
-	  {
-	    *unicode = -1;
-	    return text + 1;
-	  }
-	result <<= 6;
-	result |= (text[i] & 0x3f);
-      }
-    *unicode = result;
-    return text + len;
+  const T *next = text + item_offset;
+  const T *end = next + item_length;
+  while (next < end)
+  {
+    hb_codepoint_t u;
+    const T *old_next = next;
+    next = hb_utf_next (next, end, &u);
+    buffer->add (u, 1,  old_next - (const T *) text);
   }
+
+  /* Add post-context */
+  buffer->clear_context (1);
+  end = text + text_length;
+  while (next < end && buffer->context_len[1] < buffer->CONTEXT_LENGTH)
+  {
+    hb_codepoint_t u;
+    next = hb_utf_next (next, end, &u);
+    buffer->context[1][buffer->context_len[1]++] = u;
+  }
+
+  buffer->content_type = HB_BUFFER_CONTENT_TYPE_UNICODE;
 }
 
 void
 hb_buffer_add_utf8 (hb_buffer_t  *buffer,
 		    const char   *text,
 		    int           text_length,
 		    unsigned int  item_offset,
 		    int           item_length)
 {
-#define UTF_NEXT(S, E, U)	hb_utf8_next (S, E, &(U))
-  ADD_UTF (uint8_t);
-#undef UTF_NEXT
-}
-
-static inline const uint16_t *
-hb_utf16_next (const uint16_t *text,
-	       const uint16_t *end,
-	       hb_codepoint_t *unicode)
-{
-  uint16_t c = *text++;
-
-  if (unlikely (c >= 0xd800 && c < 0xdc00)) {
-    /* high surrogate */
-    uint16_t l;
-    if (text < end && ((l = *text), likely (l >= 0xdc00 && l < 0xe000))) {
-      /* low surrogate */
-      *unicode = ((hb_codepoint_t) ((c) - 0xd800) * 0x400 + (l) - 0xdc00 + 0x10000);
-       text++;
-    } else
-      *unicode = -1;
-  } else
-    *unicode = c;
-
-  return text;
+  hb_buffer_add_utf (buffer, (const uint8_t *) text, text_length, item_offset, item_length);
 }
 
 void
 hb_buffer_add_utf16 (hb_buffer_t    *buffer,
 		     const uint16_t *text,
 		     int             text_length,
 		     unsigned int    item_offset,
 		     int            item_length)
 {
-#define UTF_NEXT(S, E, U)	hb_utf16_next (S, E, &(U))
-  ADD_UTF (uint16_t);
-#undef UTF_NEXT
+  hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length);
 }
 
 void
 hb_buffer_add_utf32 (hb_buffer_t    *buffer,
 		     const uint32_t *text,
 		     int             text_length,
 		     unsigned int    item_offset,
 		     int             item_length)
 {
-#define UTF_NEXT(S, E, U)	((U) = *(S), (S)+1)
-  ADD_UTF (uint32_t);
-#undef UTF_NEXT
+  hb_buffer_add_utf (buffer, text, text_length, item_offset, item_length);
 }
 
 
 static int
 compare_info_codepoint (const hb_glyph_info_t *pa,
 			const hb_glyph_info_t *pb)
 {
   return (int) pb->codepoint - (int) pa->codepoint;
@@ -944,17 +950,17 @@ normalize_glyphs_cluster (hb_buffer_t *b
     hb_bubble_sort (buffer->info + start + 1, end - start - 1, compare_info_codepoint, buffer->pos + start + 1);
   }
 }
 
 void
 hb_buffer_normalize_glyphs (hb_buffer_t *buffer)
 {
   assert (buffer->have_positions);
-  /* XXX assert (buffer->have_glyphs); */
+  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS);
 
   bool backward = HB_DIRECTION_IS_BACKWARD (buffer->props.direction);
 
   unsigned int count = buffer->len;
   if (unlikely (!count)) return;
   hb_glyph_info_t *info = buffer->info;
 
   unsigned int start = 0;
--- a/gfx/harfbuzz/src/hb-buffer.h
+++ b/gfx/harfbuzz/src/hb-buffer.h
@@ -1,12 +1,12 @@
 /*
  * Copyright © 1998-2004  David Turner and Werner Lemberg
  * Copyright © 2004,2007,2009  Red Hat, Inc.
- * Copyright © 2011  Google, Inc.
+ * Copyright © 2011,2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -57,16 +57,22 @@ typedef struct hb_glyph_position_t {
   hb_position_t  y_advance;
   hb_position_t  x_offset;
   hb_position_t  y_offset;
 
   /*< private >*/
   hb_var_int_t   var;
 } hb_glyph_position_t;
 
+typedef enum {
+  HB_BUFFER_CONTENT_TYPE_INVALID = 0,
+  HB_BUFFER_CONTENT_TYPE_UNICODE,
+  HB_BUFFER_CONTENT_TYPE_GLYPHS
+} hb_buffer_content_type_t;
+
 
 hb_buffer_t *
 hb_buffer_create (void);
 
 hb_buffer_t *
 hb_buffer_get_empty (void);
 
 hb_buffer_t *
@@ -83,16 +89,24 @@ hb_buffer_set_user_data (hb_buffer_t    
 			 hb_bool_t           replace);
 
 void *
 hb_buffer_get_user_data (hb_buffer_t        *buffer,
 			 hb_user_data_key_t *key);
 
 
 void
+hb_buffer_set_content_type (hb_buffer_t              *buffer,
+			    hb_buffer_content_type_t  content_type);
+
+hb_buffer_content_type_t
+hb_buffer_get_content_type (hb_buffer_t *buffer);
+
+
+void
 hb_buffer_set_unicode_funcs (hb_buffer_t        *buffer,
 			     hb_unicode_funcs_t *unicode_funcs);
 
 hb_unicode_funcs_t *
 hb_buffer_get_unicode_funcs (hb_buffer_t        *buffer);
 
 void
 hb_buffer_set_direction (hb_buffer_t    *buffer,
--- a/gfx/harfbuzz/src/hb-common.cc
+++ b/gfx/harfbuzz/src/hb-common.cc
@@ -53,16 +53,25 @@ hb_tag_from_string (const char *s, int l
   for (i = 0; i < (unsigned) len && s[i]; i++)
     tag[i] = s[i];
   for (; i < 4; i++)
     tag[i] = ' ';
 
   return HB_TAG_CHAR4 (tag);
 }
 
+void
+hb_tag_to_string (hb_tag_t tag, char *buf)
+{
+  buf[0] = (char) (uint8_t) (tag >> 24);
+  buf[1] = (char) (uint8_t) (tag >> 16);
+  buf[2] = (char) (uint8_t) (tag >>  8);
+  buf[3] = (char) (uint8_t) (tag >>  0);
+}
+
 
 /* hb_direction_t */
 
 const char direction_strings[][4] = {
   "ltr",
   "rtl",
   "ttb",
   "btt"
--- a/gfx/harfbuzz/src/hb-common.h
+++ b/gfx/harfbuzz/src/hb-common.h
@@ -90,20 +90,24 @@ typedef union _hb_var_int_t {
 
 typedef uint32_t hb_tag_t;
 
 #define HB_TAG(a,b,c,d) ((hb_tag_t)((((uint8_t)(a))<<24)|(((uint8_t)(b))<<16)|(((uint8_t)(c))<<8)|((uint8_t)(d))))
 #define HB_UNTAG(tag)   ((uint8_t)((tag)>>24)), ((uint8_t)((tag)>>16)), ((uint8_t)((tag)>>8)), ((uint8_t)(tag))
 
 #define HB_TAG_NONE HB_TAG(0,0,0,0)
 
-/* len=-1 means str is NUL-terminated */
+/* len=-1 means str is NUL-terminated. */
 hb_tag_t
 hb_tag_from_string (const char *str, int len);
 
+/* buf should have 4 bytes. */
+void
+hb_tag_to_string (hb_tag_t tag, char *buf);
+
 
 /* hb_direction_t */
 
 typedef enum {
   HB_DIRECTION_INVALID = 0,
   HB_DIRECTION_LTR = 4,
   HB_DIRECTION_RTL,
   HB_DIRECTION_TTB,
--- a/gfx/harfbuzz/src/hb-font.cc
+++ b/gfx/harfbuzz/src/hb-font.cc
@@ -584,36 +584,36 @@ static void
 static hb_blob_t *
 _hb_face_for_data_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *user_data)
 {
   hb_face_for_data_closure_t *data = (hb_face_for_data_closure_t *) user_data;
 
   if (tag == HB_TAG_NONE)
     return hb_blob_reference (data->blob);
 
-  const OpenTypeFontFile &ot_file = *Sanitizer<OpenTypeFontFile>::lock_instance (data->blob);
-  const OpenTypeFontFace &ot_face = ot_file.get_face (data->index);
+  const OT::OpenTypeFontFile &ot_file = *OT::Sanitizer<OT::OpenTypeFontFile>::lock_instance (data->blob);
+  const OT::OpenTypeFontFace &ot_face = ot_file.get_face (data->index);
 
-  const OpenTypeTable &table = ot_face.get_table_by_tag (tag);
+  const OT::OpenTypeTable &table = ot_face.get_table_by_tag (tag);
 
   hb_blob_t *blob = hb_blob_create_sub_blob (data->blob, table.offset, table.length);
 
   return blob;
 }
 
 hb_face_t *
 hb_face_create (hb_blob_t    *blob,
 		unsigned int  index)
 {
   hb_face_t *face;
 
   if (unlikely (!blob || !hb_blob_get_length (blob)))
     return hb_face_get_empty ();
 
-  hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (Sanitizer<OpenTypeFontFile>::sanitize (hb_blob_reference (blob)), index);
+  hb_face_for_data_closure_t *closure = _hb_face_for_data_closure_create (OT::Sanitizer<OT::OpenTypeFontFile>::sanitize (hb_blob_reference (blob)), index);
 
   if (unlikely (!closure))
     return hb_face_get_empty ();
 
   face = hb_face_create_for_tables (_hb_face_for_data_reference_table,
 				    closure,
 				    (hb_destroy_func_t) _hb_face_for_data_closure_destroy);
 
@@ -735,18 +735,18 @@ hb_face_get_upem (hb_face_t *face)
 {
   return face->get_upem ();
 }
 
 
 void
 hb_face_t::load_upem (void) const
 {
-  hb_blob_t *head_blob = Sanitizer<head>::sanitize (reference_table (HB_OT_TAG_head));
-  const head *head_table = Sanitizer<head>::lock_instance (head_blob);
+  hb_blob_t *head_blob = OT::Sanitizer<OT::head>::sanitize (reference_table (HB_OT_TAG_head));
+  const OT::head *head_table = OT::Sanitizer<OT::head>::lock_instance (head_blob);
   upem = head_table->get_upem ();
   hb_blob_destroy (head_blob);
 }
 
 
 /*
  * hb_font_t
  */
--- a/gfx/harfbuzz/src/hb-graphite2.cc
+++ b/gfx/harfbuzz/src/hb-graphite2.cc
@@ -206,71 +206,87 @@ hb_bool_t
 		     hb_buffer_t        *buffer,
 		     const hb_feature_t *features,
 		     unsigned int        num_features)
 {
   hb_face_t *face = font->face;
   gr_face *grface = HB_SHAPER_DATA_GET (face)->grface;
   gr_font *grfont = HB_SHAPER_DATA_GET (font);
 
-  unsigned int charlen;
-  hb_glyph_info_t *bufferi = hb_buffer_get_glyph_infos (buffer, &charlen);
-
-  int success = 0;
-
   const char *lang = hb_language_to_string (hb_buffer_get_language (buffer));
   const char *lang_end = strchr (lang, '-');
   int lang_len = lang_end ? lang_end - lang : -1;
   gr_feature_val *feats = gr_face_featureval_for_lang (grface, lang ? hb_tag_from_string (lang, lang_len) : 0);
 
   while (num_features--)
   {
     const gr_feature_ref *fref = gr_face_find_fref (grface, features->tag);
     if (fref)
       gr_fref_set_feature_value (fref, features->value, feats);
     features++;
   }
 
-  /* TODO Use scratch buffer for these. */
-  hb_codepoint_t *gids = NULL, *pg;
-  hb_graphite2_cluster_t *clusters = NULL;
   gr_segment *seg = NULL;
-  uint32_t *text = NULL;
   const gr_slot *is;
   unsigned int ci = 0, ic = 0;
   float curradvx = 0., curradvy = 0.;
-  unsigned int glyphlen = 0;
-  unsigned int *p;
+
+  unsigned int scratch_size;
+  char *scratch = (char *) buffer->get_scratch_buffer (&scratch_size);
 
-  text = (uint32_t *) malloc ((charlen + 1) * sizeof (uint32_t));
-  if (!text) goto dieout;
+#define ALLOCATE_ARRAY(Type, name, len) \
+  Type *name = (Type *) scratch; \
+  scratch += (len) * sizeof ((name)[0]); \
+  scratch_size -= (len) * sizeof ((name)[0]);
 
-  p = text;
-  for (unsigned int i = 0; i < charlen; ++i)
-    *p++ = bufferi++->codepoint;
-  *p = 0;
+  ALLOCATE_ARRAY (uint32_t, chars, buffer->len);
+
+  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,
 		     script_tag[1] == HB_TAG_NONE ? script_tag[0] : script_tag[1],
 		     feats,
-		     gr_utf32, text, charlen,
+		     gr_utf32, chars, buffer->len,
 		     2 | (hb_buffer_get_direction (buffer) == HB_DIRECTION_RTL ? 1 : 0));
-  if (!seg) goto dieout;
+
+  if (unlikely (!seg)) {
+    if (feats) gr_featureval_destroy (feats);
+    return false;
+  }
+
+  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;
+  }
 
-  glyphlen = gr_seg_n_slots (seg);
-  clusters = (hb_graphite2_cluster_t *) calloc (charlen, sizeof (hb_graphite2_cluster_t));
-  if (!glyphlen || !clusters) goto dieout;
+  scratch = (char *) buffer->get_scratch_buffer (&scratch_size);
+  while ((sizeof (hb_graphite2_cluster_t) * buffer->len +
+	  sizeof (hb_codepoint_t) * glyph_count) > 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);
+  }
 
-  gids = (hb_codepoint_t *) malloc (glyphlen * sizeof (hb_codepoint_t));
-  if (!gids) goto dieout;
+  ALLOCATE_ARRAY (hb_graphite2_cluster_t, clusters, buffer->len);
+  ALLOCATE_ARRAY (hb_codepoint_t, gids, glyph_count);
 
-  pg = gids;
+  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);
     pg++;
     while (clusters[ci].base_char > before && ci)
     {
@@ -320,18 +336,13 @@ hb_bool_t
     curradvy += pPos->y_advance;
   }
   if (!HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
     pPos[-1].x_advance += gr_seg_advance_X(seg) - curradvx;
 
   if (HB_DIRECTION_IS_BACKWARD (buffer->props.direction))
     hb_buffer_reverse_clusters (buffer);
 
-  success = 1;
+  if (feats) gr_featureval_destroy (feats);
+  gr_seg_destroy (seg);
 
-dieout:
-  if (feats) gr_featureval_destroy (feats);
-  if (gids) free (gids);
-  if (clusters) free (clusters);
-  if (seg) gr_seg_destroy (seg);
-  if (text) free (text);
-  return success;
+  return true;
 }
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-icu-le.cc
@@ -0,0 +1,213 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#define HB_SHAPER icu_le
+#define hb_icu_le_shaper_font_data_t PortableFontInstance
+#include "hb-shaper-impl-private.hh"
+
+#include "hb-icu-le/PortableFontInstance.h"
+
+#include "layout/LayoutEngine.h"
+#include "unicode/unistr.h"
+
+#include "hb-icu.h"
+
+
+/*
+ * shaper face data
+ */
+
+struct hb_icu_le_shaper_face_data_t {};
+
+hb_icu_le_shaper_face_data_t *
+_hb_icu_le_shaper_face_data_create (hb_face_t *face)
+{
+  return (hb_icu_le_shaper_face_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+}
+
+void
+_hb_icu_le_shaper_face_data_destroy (hb_icu_le_shaper_face_data_t *data)
+{
+}
+
+
+/*
+ * shaper font data
+ */
+
+hb_icu_le_shaper_font_data_t *
+_hb_icu_le_shaper_font_data_create (hb_font_t *font)
+{
+  LEErrorCode status = LE_NO_ERROR;
+  hb_icu_le_shaper_font_data_t *data = new PortableFontInstance (font->face,
+								 font->x_scale,
+								 font->y_scale,
+								 status);
+  if (status != LE_NO_ERROR) {
+    delete (data);
+    return NULL;
+  }
+
+  return data;
+}
+
+void
+_hb_icu_le_shaper_font_data_destroy (hb_icu_le_shaper_font_data_t *data)
+{
+  delete (data);
+}
+
+
+/*
+ * shaper shape_plan data
+ */
+
+struct hb_icu_le_shaper_shape_plan_data_t {};
+
+hb_icu_le_shaper_shape_plan_data_t *
+_hb_icu_le_shaper_shape_plan_data_create (hb_shape_plan_t    *shape_plan,
+					  const hb_feature_t *user_features,
+					  unsigned int        num_user_features)
+{
+  return (hb_icu_le_shaper_shape_plan_data_t *) HB_SHAPER_DATA_SUCCEEDED;
+}
+
+void
+_hb_icu_le_shaper_shape_plan_data_destroy (hb_icu_le_shaper_shape_plan_data_t *data)
+{
+}
+
+
+/*
+ * shaper
+ */
+
+hb_bool_t
+_hb_icu_le_shape (hb_shape_plan_t    *shape_plan,
+		  hb_font_t          *font,
+		  hb_buffer_t        *buffer,
+		  const hb_feature_t *features,
+		  unsigned int        num_features)
+{
+  LEFontInstance *font_instance = HB_SHAPER_DATA_GET (font);
+  le_int32 script_code = hb_icu_script_from_script (shape_plan->props.script);
+  le_int32 language_code = -1 /* TODO */;
+  le_int32 typography_flags = 3; // essential for ligatures and kerning
+  LEErrorCode status = LE_NO_ERROR;
+  LayoutEngine *le = LayoutEngine::layoutEngineFactory (font_instance,
+							script_code,
+							language_code,
+							typography_flags,
+							status);
+  if (status != LE_NO_ERROR)
+  { delete (le); return false; }
+
+retry:
+
+  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]);
+
+  ALLOCATE_ARRAY (LEUnicode, chars, buffer->len);
+  ALLOCATE_ARRAY (unsigned int, clusters, buffer->len);
+
+  for (unsigned int i = 0; i < buffer->len; i++) {
+    chars[i] = buffer->info[i].codepoint;
+    clusters[i] = buffer->info[i].cluster;
+  }
+
+  unsigned int glyph_count = le->layoutChars(chars,
+					     0,
+					     buffer->len,
+					     buffer->len,
+					     HB_DIRECTION_IS_BACKWARD (buffer->props.direction),
+					     0., 0.,
+					     status);
+  if (status != LE_NO_ERROR)
+  { delete (le); return false; }
+
+  unsigned int num_glyphs = scratch_size / (sizeof (LEGlyphID) +
+					    sizeof (le_int32) +
+					    sizeof (float) * 2);
+
+  if (unlikely (glyph_count >= num_glyphs || glyph_count > buffer->allocated)) {
+    buffer->ensure (buffer->allocated * 2);
+    if (buffer->in_error)
+    { delete (le); return false; }
+    goto retry;
+  }
+
+  ALLOCATE_ARRAY (LEGlyphID, glyphs, glyph_count);
+  ALLOCATE_ARRAY (le_int32, indices, glyph_count);
+  ALLOCATE_ARRAY (float, positions, glyph_count * 2 + 2);
+
+  le->getGlyphs(glyphs, status);
+  le->getCharIndices(indices, status);
+  le->getGlyphPositions(positions, status);
+
+#undef ALLOCATE_ARRAY
+
+  /* Ok, we've got everything we need, now compose output buffer,
+   * very, *very*, carefully! */
+
+  unsigned int j = 0;
+  hb_glyph_info_t *info = buffer->info;
+  for (unsigned int i = 0; i < glyph_count; i++)
+  {
+    if (glyphs[i] >= 0xFFFE)
+	continue;
+
+    info[j].codepoint = glyphs[i];
+    info[j].cluster = clusters[indices[i]];
+
+    /* icu-le doesn't seem to have separapte advance values. */
+    info[j].mask = positions[2 * i + 2] - positions[2 * i];
+    info[j].var1.u32 = 0;
+    info[j].var2.u32 = -positions[2 * i + 1];
+
+    j++;
+  }
+  buffer->len = j;
+
+  buffer->clear_positions ();
+
+  for (unsigned int i = 0; i < buffer->len; i++) {
+    hb_glyph_info_t *info = &buffer->info[i];
+    hb_glyph_position_t *pos = &buffer->pos[i];
+
+    /* TODO vertical */
+    pos->x_advance = info->mask;
+    pos->x_offset = info->var1.u32;
+    pos->y_offset = info->var2.u32;
+  }
+
+  delete (le);
+  return true;
+}
--- a/gfx/harfbuzz/src/hb-mutex-private.hh
+++ b/gfx/harfbuzz/src/hb-mutex-private.hh
@@ -60,36 +60,16 @@ typedef CRITICAL_SECTION hb_mutex_impl_t
 typedef pthread_mutex_t hb_mutex_impl_t;
 #define HB_MUTEX_IMPL_INIT	PTHREAD_MUTEX_INITIALIZER
 #define hb_mutex_impl_init(M)	pthread_mutex_init (M, NULL)
 #define hb_mutex_impl_lock(M)	pthread_mutex_lock (M)
 #define hb_mutex_impl_unlock(M)	pthread_mutex_unlock (M)
 #define hb_mutex_impl_finish(M)	pthread_mutex_destroy (M)
 
 
-#elif !defined(HB_NO_MT) && defined(HAVE_GLIB)
-
-#include <glib.h>
-#if !GLIB_CHECK_VERSION(2,32,0)
-typedef GStaticMutex hb_mutex_impl_t;
-#define HB_MUTEX_IMPL_INIT	G_STATIC_MUTEX_INIT
-#define hb_mutex_impl_init(M)	g_static_mutex_init (M)
-#define hb_mutex_impl_lock(M)	g_static_mutex_lock (M)
-#define hb_mutex_impl_unlock(M)	g_static_mutex_unlock (M)
-#define hb_mutex_impl_finish(M)	g_static_mutex_free (M)
-#else
-typedef GMutex hb_mutex_impl_t;
-#define HB_MUTEX_IMPL_INIT	{0}
-#define hb_mutex_impl_init(M)	g_mutex_init (M)
-#define hb_mutex_impl_lock(M)	g_mutex_lock (M)
-#define hb_mutex_impl_unlock(M)	g_mutex_unlock (M)
-#define hb_mutex_impl_finish(M)	g_mutex_clear (M)
-#endif
-
-
 #elif !defined(HB_NO_MT) && defined(HAVE_INTEL_ATOMIC_PRIMITIVES)
 
 #if defined(HAVE_SCHED_H) && defined(HAVE_SCHED_YIELD)
 # include <sched.h>
 # define HB_SCHED_YIELD() sched_yield ()
 #else
 # define HB_SCHED_YIELD() HB_STMT_START {} HB_STMT_END
 #endif
--- a/gfx/harfbuzz/src/hb-old.cc
+++ b/gfx/harfbuzz/src/hb-old.cc
@@ -332,16 +332,19 @@ retry:
 					    sizeof (HB_FixedPoint) +
 					    sizeof (uint32_t));
 
   item.num_glyphs = num_glyphs;
   ALLOCATE_ARRAY (HB_Glyph, item.glyphs, num_glyphs);
   ALLOCATE_ARRAY (HB_GlyphAttributes, item.attributes, num_glyphs);
   ALLOCATE_ARRAY (HB_Fixed, item.advances, num_glyphs);
   ALLOCATE_ARRAY (HB_FixedPoint, item.offsets, num_glyphs);
+  /* Apparently in some cases the offsets array will not be fully assigned to.
+   * Clear it. */
+  memset (item.offsets, 0, num_glyphs * sizeof (item.offsets[0]));
   uint32_t *vis_clusters;
   ALLOCATE_ARRAY (uint32_t, vis_clusters, num_glyphs);
 
 #undef ALLOCATE_ARRAY
 
   if (!HB_ShapeItem (&item))
   {
     if (unlikely (item.num_glyphs > num_glyphs))
--- a/gfx/harfbuzz/src/hb-open-file-private.hh
+++ b/gfx/harfbuzz/src/hb-open-file-private.hh
@@ -27,16 +27,18 @@
  */
 
 #ifndef HB_OPEN_FILE_PRIVATE_HH
 #define HB_OPEN_FILE_PRIVATE_HH
 
 #include "hb-open-type-private.hh"
 
 
+namespace OT {
+
 
 /*
  *
  * The OpenType Font File
  *
  */
 
 
@@ -248,10 +250,12 @@ struct OpenTypeFontFile
   OpenTypeFontFace	fontFace;
   TTCHeader		ttcHeader;
   } u;
   public:
   DEFINE_SIZE_UNION (4, tag);
 };
 
 
+} // namespace OT
+
 
 #endif /* HB_OPEN_FILE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-open-type-private.hh
+++ b/gfx/harfbuzz/src/hb-open-type-private.hh
@@ -29,16 +29,18 @@
 #ifndef HB_OPEN_TYPE_PRIVATE_HH
 #define HB_OPEN_TYPE_PRIVATE_HH
 
 #include "hb-private.hh"
 
 #include "hb-blob.h"
 
 
+namespace OT {
+
 
 /*
  * Casts
  */
 
 /* Cast to struct T, reference to reference */
 template<typename Type, typename TObject>
 inline const Type& CastR(const TObject &X)
@@ -320,16 +322,170 @@ struct Sanitizer
     hb_blob_make_immutable (blob);
     const char *base = hb_blob_get_data (blob, NULL);
     return unlikely (!base) ? &Null(Type) : CastP<Type> (base);
   }
 };
 
 
 
+/*
+ * Serialize
+ */
+
+#ifndef HB_DEBUG_SERIALIZE
+#define HB_DEBUG_SERIALIZE (HB_DEBUG+0)
+#endif
+
+
+#define TRACE_SERIALIZE() \
+	hb_auto_trace_t<HB_DEBUG_SERIALIZE> trace (&c->debug_depth, "SERIALIZE", c, HB_FUNC, "");
+
+
+struct hb_serialize_context_t
+{
+  inline hb_serialize_context_t (void *start, unsigned int size)
+  {
+    this->start = (char *) start;
+    this->end = this->start + size;
+
+    this->ran_out_of_room = false;
+    this->head = this->start;
+    this->debug_depth = 0;
+  }
+
+  template <typename Type>
+  inline Type *start_serialize (void)
+  {
+    DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1,
+		     "start [%p..%p] (%lu bytes)",
+		     this->start, this->end,
+		     (unsigned long) (this->end - this->start));
+
+    return start_embed<Type> ();
+  }
+
+  inline void end_serialize (void)
+  {
+    DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1,
+		     "end [%p..%p] serialized %d bytes; %s",
+		     this->start, this->end,
+		     (int) (this->head - this->start),
+		     this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room");
+
+  }
+
+  template <typename Type>
+  inline Type *copy (void)
+  {
+    assert (!this->ran_out_of_room);
+    unsigned int len = this->head - this->start;
+    void *p = malloc (len);
+    if (p)
+      memcpy (p, this->start, len);
+    return reinterpret_cast<Type *> (p);
+  }
+
+  template <typename Type>
+  inline Type *allocate_size (unsigned int size)
+  {
+    if (unlikely (this->ran_out_of_room || this->end - this->head < size)) {
+      this->ran_out_of_room = true;
+      return NULL;
+    }
+    memset (this->head, 0, size);
+    char *ret = this->head;
+    this->head += size;
+    return reinterpret_cast<Type *> (ret);
+  }
+
+  template <typename Type>
+  inline Type *allocate_min (void)
+  {
+    return this->allocate_size<Type> (Type::min_size);
+  }
+
+  template <typename Type>
+  inline Type *start_embed (void)
+  {
+    Type *ret = reinterpret_cast<Type *> (this->head);
+    return ret;
+  }
+
+  template <typename Type>
+  inline Type *embed (const Type &obj)
+  {
+    unsigned int size = obj.get_size ();
+    Type *ret = this->allocate_size<Type> (size);
+    if (unlikely (!ret)) return NULL;
+    memcpy (ret, obj, size);
+    return ret;
+  }
+
+  template <typename Type>
+  inline Type *extend_min (Type &obj)
+  {
+    unsigned int size = obj.min_size;
+    assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
+    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return NULL;
+    return reinterpret_cast<Type *> (&obj);
+  }
+
+  template <typename Type>
+  inline Type *extend (Type &obj)
+  {
+    unsigned int size = obj.get_size ();
+    assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head);
+    if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return NULL;
+    return reinterpret_cast<Type *> (&obj);
+  }
+
+  inline void truncate (void *head)
+  {
+    assert (this->start < head && head <= this->head);
+    this->head = (char *) head;
+  }
+
+  unsigned int debug_depth;
+  char *start, *end, *head;
+  bool ran_out_of_room;
+};
+
+template <typename Type>
+struct Supplier
+{
+  inline Supplier (const Type *array, unsigned int len_)
+  {
+    head = array;
+    len = len_;
+  }
+  inline const Type operator [] (unsigned int i) const
+  {
+    if (unlikely (i >= len)) return Type ();
+    return head[i];
+  }
+
+  inline void advance (unsigned int count)
+  {
+    if (unlikely (count > len))
+      count = len;
+    len -= count;
+    head += count;
+  }
+
+  private:
+  inline Supplier (const Supplier<Type> &); /* Disallow copy */
+  inline Supplier<Type>& operator= (const Supplier<Type> &); /* Disallow copy */
+
+  unsigned int len;
+  const Type *head;
+};
+
+
+
 
 /*
  *
  * The OpenType Font File: Data Types
  */
 
 
 /* "The following data types are used in the OpenType font file.
@@ -366,32 +522,29 @@ struct BEInt<Type, 4>
 /* Integer types in big-endian order and no alignment requirement */
 template <typename Type>
 struct IntType
 {
   inline void set (Type i) { v.set (i); }
   inline operator Type(void) const { return v; }
   inline bool operator == (const IntType<Type> &o) const { return v == o.v; }
   inline bool operator != (const IntType<Type> &o) const { return v != o.v; }
+  static inline int cmp (const IntType<Type> *a, const IntType<Type> *b) { return b->cmp (*a); }
+  inline int cmp (IntType<Type> va) const { Type a = va; Type b = v; return a < b ? -1 : a == b ? 0 : +1; }
   inline int cmp (Type a) const { Type b = v; return a < b ? -1 : a == b ? 0 : +1; }
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (likely (c->check_struct (this)));
   }
   protected:
   BEInt<Type, sizeof (Type)> v;
   public:
   DEFINE_SIZE_STATIC (sizeof (Type));
 };
 
-/* Typedef these to avoid clash with windows.h */
-#define USHORT	HB_USHORT
-#define SHORT	HB_SHORT
-#define ULONG	HB_ULONG
-#define LONG	HB_LONG
 typedef IntType<uint16_t> USHORT;	/* 16-bit unsigned integer. */
 typedef IntType<int16_t>  SHORT;	/* 16-bit signed integer. */
 typedef IntType<uint32_t> ULONG;	/* 32-bit unsigned integer. */
 typedef IntType<int32_t>  LONG;		/* 32-bit signed integer. */
 
 /* 16-bit signed integer (SHORT) that describes a quantity in FUnits. */
 typedef SHORT FWORD;
 
@@ -488,16 +641,28 @@ template <typename OffsetType, typename 
 struct GenericOffsetTo : OffsetType
 {
   inline const Type& operator () (const void *base) const
   {
     unsigned int offset = *this;
     if (unlikely (!offset)) return Null(Type);
     return StructAtOffset<Type> (base, offset);
   }
+  inline Type& operator () (void *base)
+  {
+    unsigned int offset = *this;
+    return StructAtOffset<Type> (base, offset);
+  }
+
+  inline Type& serialize (hb_serialize_context_t *c, void *base)
+  {
+    Type *t = c->start_embed<Type> ();
+    this->set ((char *) t - (char *) base); /* TODO(serialize) Overflow? */
+    return *t;
+  }
 
   inline bool sanitize (hb_sanitize_context_t *c, void *base) {
     TRACE_SANITIZE ();
     if (unlikely (!c->check_struct (this))) return TRACE_RETURN (false);
     unsigned int offset = *this;
     if (unlikely (!offset)) return TRACE_RETURN (true);
     Type &obj = StructAtOffset<Type> (base, offset);
     return TRACE_RETURN (likely (obj.sanitize (c)) || neuter (c));
@@ -518,17 +683,19 @@ struct GenericOffsetTo : OffsetType
     if (c->may_edit (this, this->static_size)) {
       this->set (0); /* 0 is Null offset */
       return true;
     }
     return false;
   }
 };
 template <typename Base, typename OffsetType, typename Type>
-inline const Type& operator + (const Base &base, GenericOffsetTo<OffsetType, Type> offset) { return offset (base); }
+inline const Type& operator + (const Base &base, const GenericOffsetTo<OffsetType, Type> &offset) { return offset (base); }
+template <typename Base, typename OffsetType, typename Type>
+inline Type& operator + (Base &base, GenericOffsetTo<OffsetType, Type> &offset) { return offset (base); }
 
 template <typename Type>
 struct OffsetTo : GenericOffsetTo<Offset, Type> {};
 
 template <typename Type>
 struct LongOffsetTo : GenericOffsetTo<LongOffset, Type> {};
 
 
@@ -551,19 +718,45 @@ struct GenericArrayOf
     return array + start_offset;
   }
 
   inline const Type& operator [] (unsigned int i) const
   {
     if (unlikely (i >= len)) return Null(Type);
     return array[i];
   }
+  inline Type& operator [] (unsigned int i)
+  {
+    return array[i];
+  }
   inline unsigned int get_size (void) const
   { return len.static_size + len * Type::static_size; }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 unsigned int items_len)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    len.set (items_len); /* TODO(serialize) Overflow? */
+    if (unlikely (!c->extend (*this))) return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
+  }
+
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<Type> &items,
+			 unsigned int items_len)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!serialize (c, items_len))) return TRACE_RETURN (false);
+    for (unsigned int i = 0; i < items_len; i++)
+      array[i] = items[i];
+    items.advance (items_len);
+    return TRACE_RETURN (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
 
     /* Note: for structs that do not reference other structs,
      * we do not need to call their sanitize() as we already did
      * a bound check on the aggregate array size.  We just include
      * a small unreachable expression to make sure the structs
@@ -657,16 +850,31 @@ struct HeadlessArrayOf
   inline const Type& operator [] (unsigned int i) const
   {
     if (unlikely (i >= len || !i)) return Null(Type);
     return array[i-1];
   }
   inline unsigned int get_size (void) const
   { return len.static_size + (len ? len - 1 : 0) * Type::static_size; }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<Type> &items,
+			 unsigned int items_len)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    len.set (items_len); /* TODO(serialize) Overflow? */
+    if (unlikely (!items_len)) return TRACE_RETURN (true);
+    if (unlikely (!c->extend (*this))) return TRACE_RETURN (false);
+    for (unsigned int i = 0; i < items_len - 1; i++)
+      array[i] = items[i];
+    items.advance (items_len - 1);
+    return TRACE_RETURN (true);
+  }
+
   inline bool sanitize_shallow (hb_sanitize_context_t *c) {
     return c->check_struct (this)
 	&& c->check_array (this, Type::static_size, len);
   }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (unlikely (!sanitize_shallow (c))) return TRACE_RETURN (false);
@@ -709,10 +917,12 @@ struct SortedArrayOf : ArrayOf<Type> {
       };
       const Type *p = (const Type *) bsearch (&x, this->array, this->len, sizeof (this->array[0]), (hb_compare_func_t) Cmp::cmp);
       return p ? p - this->array : -1;
     }
   }
 };
 
 
+} // namespace OT
+
 
 #endif /* HB_OPEN_TYPE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-ot-head-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-head-table.hh
@@ -27,16 +27,18 @@
  */
 
 #ifndef HB_OT_HEAD_TABLE_HH
 #define HB_OT_HEAD_TABLE_HH
 
 #include "hb-open-type-private.hh"
 
 
+namespace OT {
+
 
 /*
  * head -- Font Header
  */
 
 #define HB_OT_TAG_head HB_TAG('h','e','a','d')
 
 struct head
@@ -136,10 +138,12 @@ struct head
 					 * -2: Like -1 but also contains neutrals. */
   SHORT		indexToLocFormat;	/* 0 for short offsets, 1 for long. */
   SHORT		glyphDataFormat;	/* 0 for current format. */
   public:
   DEFINE_SIZE_STATIC (54);
 };
 
 
+} // namespace OT
+
 
 #endif /* HB_OT_HEAD_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-hhea-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hhea-table.hh
@@ -25,16 +25,18 @@
  */
 
 #ifndef HB_OT_HHEA_TABLE_HH
 #define HB_OT_HHEA_TABLE_HH
 
 #include "hb-open-type-private.hh"
 
 
+namespace OT {
+
 
 /*
  * hhea -- The Horizontal Header Table
  */
 
 #define HB_OT_TAG_hhea HB_TAG('h','h','e','a')
 
 
@@ -84,9 +86,12 @@ struct hhea
   SHORT		metricDataFormat;	/* 0 for current format. */
   USHORT	numberOfHMetrics;	/* Number of hMetric entries in 'hmtx'
 					 * table */
   public:
   DEFINE_SIZE_STATIC (36);
 };
 
 
+} // namespace OT
+
+
 #endif /* HB_OT_HHEA_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-hmtx-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-hmtx-table.hh
@@ -25,16 +25,18 @@
  */
 
 #ifndef HB_OT_HMTX_TABLE_HH
 #define HB_OT_HMTX_TABLE_HH
 
 #include "hb-open-type-private.hh"
 
 
+namespace OT {
+
 
 /*
  * hmtx -- The Horizontal Metrics Table
  */
 
 #define HB_OT_TAG_hmtx HB_TAG('h','m','t','x')
 
 
@@ -78,9 +80,13 @@ struct hmtx
 					 * run is allowed and it must be at
 					 * the end. This allows a monospaced
 					 * font to vary the left side bearing
 					 * values for each glyph. */
   public:
   DEFINE_SIZE_ARRAY2 (0, longHorMetric, leftSideBearingX);
 };
 
+
+} // namespace OT
+
+
 #endif /* HB_OT_HMTX_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-common-private.hh
@@ -29,16 +29,19 @@
 #ifndef HB_OT_LAYOUT_COMMON_PRIVATE_HH
 #define HB_OT_LAYOUT_COMMON_PRIVATE_HH
 
 #include "hb-ot-layout-private.hh"
 #include "hb-open-type-private.hh"
 #include "hb-set-private.hh"
 
 
+namespace OT {
+
+
 #define NOT_COVERED		((unsigned int) -1)
 #define MAX_NESTING_LEVEL	8
 
 
 
 /*
  *
  * OpenType Layout Common Table Formats
@@ -305,21 +308,39 @@ struct Lookup
     if (unlikely (flag & LookupFlag::UseMarkFilteringSet))
     {
       const USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
       flag += (markFilteringSet << 16);
     }
     return flag;
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 unsigned int lookup_type,
+			 uint32_t lookup_props,
+			 unsigned int num_subtables)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    lookupType.set (lookup_type);
+    lookupFlag.set (lookup_props & 0xFFFF);
+    if (unlikely (!subTable.serialize (c, num_subtables))) return TRACE_RETURN (false);
+    if (lookupFlag & LookupFlag::UseMarkFilteringSet)
+    {
+      USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
+      markFilteringSet.set (lookup_props >> 16);
+    }
+    return TRACE_RETURN (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     /* Real sanitize of the subtables is done by GSUB/GPOS/... */
     if (!(c->check_struct (this) && subTable.sanitize (c))) return TRACE_RETURN (false);
-    if (unlikely (lookupFlag & LookupFlag::UseMarkFilteringSet))
+    if (lookupFlag & LookupFlag::UseMarkFilteringSet)
     {
       USHORT &markFilteringSet = StructAfter<USHORT> (subTable);
       if (!markFilteringSet.sanitize (c)) return TRACE_RETURN (false);
     }
     return TRACE_RETURN (true);
   }
 
   USHORT	lookupType;		/* Different enumerations for GSUB and GPOS */
@@ -347,16 +368,30 @@ struct CoverageFormat1
   private:
   inline unsigned int get_coverage (hb_codepoint_t glyph_id) const
   {
     int i = glyphArray.search (glyph_id);
     ASSERT_STATIC (((unsigned int) -1) == NOT_COVERED);
     return i;
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &glyphs,
+			 unsigned int num_glyphs)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    glyphArray.len.set (num_glyphs);
+    if (unlikely (!c->extend (glyphArray))) return TRACE_RETURN (false);
+    for (unsigned int i = 0; i < num_glyphs; i++)
+      glyphArray[i] = glyphs[i];
+    glyphs.advance (num_glyphs);
+    return TRACE_RETURN (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (glyphArray.sanitize (c));
   }
 
   inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
     return glyphs->has (glyphArray[index]);
   }
@@ -398,16 +433,48 @@ struct CoverageFormat2
     int i = rangeRecord.search (glyph_id);
     if (i != -1) {
       const RangeRecord &range = rangeRecord[i];
       return (unsigned int) range.value + (glyph_id - range.start);
     }
     return NOT_COVERED;
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &glyphs,
+			 unsigned int num_glyphs)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+
+    if (unlikely (!num_glyphs)) return TRACE_RETURN (true);
+
+    unsigned int num_ranges = 1;
+    for (unsigned int i = 1; i < num_glyphs; i++)
+      if (glyphs[i - 1] + 1 != glyphs[i])
+        num_ranges++;
+    rangeRecord.len.set (num_ranges);
+    if (unlikely (!c->extend (rangeRecord))) return TRACE_RETURN (false);
+
+    unsigned int range = 0;
+    rangeRecord[range].start = glyphs[0];
+    rangeRecord[range].value.set (0);
+    for (unsigned int i = 1; i < num_glyphs; i++)
+      if (glyphs[i - 1] + 1 != glyphs[i]) {
+	range++;
+	rangeRecord[range].start = glyphs[i];
+	rangeRecord[range].value.set (i);
+        rangeRecord[range].end = glyphs[i];
+      } else {
+        rangeRecord[range].end = glyphs[i];
+      }
+    glyphs.advance (num_glyphs);
+    return TRACE_RETURN (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (rangeRecord.sanitize (c));
   }
 
   inline bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const {
     unsigned int i;
     unsigned int count = rangeRecord.len;
@@ -474,16 +541,34 @@ struct Coverage
   {
     switch (u.format) {
     case 1: return u.format1.get_coverage(glyph_id);
     case 2: return u.format2.get_coverage(glyph_id);
     default:return NOT_COVERED;
     }
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &glyphs,
+			 unsigned int num_glyphs)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    unsigned int num_ranges = 1;
+    for (unsigned int i = 1; i < num_glyphs; i++)
+      if (glyphs[i - 1] + 1 != glyphs[i])
+        num_ranges++;
+    u.format.set (num_glyphs * 2 < num_ranges * 3 ? 1 : 2);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, num_glyphs));
+    case 2: return TRACE_RETURN (u.format2.serialize (c, glyphs, num_glyphs));
+    default:return TRACE_RETURN (false);
+    }
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     case 2: return TRACE_RETURN (u.format2.sanitize (c));
     default:return TRACE_RETURN (true);
     }
@@ -758,10 +843,12 @@ struct Device
 					 * 3	Signed 8-bit value, 2 values per uint16
 					 */
   USHORT	deltaValue[VAR];	/* Array of compressed data */
   public:
   DEFINE_SIZE_ARRAY (6, deltaValue);
 };
 
 
+} // namespace OT
+
 
 #endif /* HB_OT_LAYOUT_COMMON_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gdef-table.hh
@@ -29,16 +29,18 @@
 #ifndef HB_OT_LAYOUT_GDEF_TABLE_HH
 #define HB_OT_LAYOUT_GDEF_TABLE_HH
 
 #include "hb-ot-layout-common-private.hh"
 
 #include "hb-font-private.hh"
 
 
+namespace OT {
+
 
 /*
  * Attachment List Table
  */
 
 typedef ArrayOf<USHORT> AttachPoint;	/* Array of contour point indices--in
 					 * increasing numerical order */
 
@@ -416,10 +418,12 @@ struct GDEF
 					 * definitions--from beginning of GDEF
 					 * header (may be NULL).  Introduced
 					 * in version 00010002. */
   public:
   DEFINE_SIZE_ARRAY (12, markGlyphSetsDef);
 };
 
 
+} // namespace OT
+
 
 #endif /* HB_OT_LAYOUT_GDEF_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gpos-table.hh
@@ -27,16 +27,18 @@
  */
 
 #ifndef HB_OT_LAYOUT_GPOS_TABLE_HH
 #define HB_OT_LAYOUT_GPOS_TABLE_HH
 
 #include "hb-ot-layout-gsubgpos-private.hh"
 
 
+namespace OT {
+
 
 /* buffer **position** var allocations */
 #define attach_lookback() var.u16[0] /* number of glyphs to go back to attach this glyph to its base */
 #define cursive_chain() var.i16[1] /* character to which this connects, may be positive or negative */
 
 
 /* Shared Tables: ValueRecord, Anchor Table, and MarkArray */
 
@@ -1524,31 +1526,31 @@ struct PosLookup : Lookup
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++)
       if (get_subtable (i).apply (c, lookup_type))
 	return true;
 
     return false;
   }
 
-  inline bool apply_string (hb_apply_context_t *c) const
+  inline bool apply_string (hb_apply_context_t *c, const hb_set_digest_t *digest) const
   {
     bool ret = false;
 
-    if (unlikely (!c->buffer->len))
+    if (unlikely (!c->buffer->len || !c->lookup_mask))
       return false;
 
     c->set_lookup (*this);
 
     c->buffer->idx = 0;
 
     while (c->buffer->idx < c->buffer->len)
     {
       if ((c->buffer->cur().mask & c->lookup_mask) &&
-	  c->digest.may_have (c->buffer->cur().codepoint) &&
+	  digest->may_have (c->buffer->cur().codepoint) &&
 	  apply_once (c))
 	ret = true;
       else
 	c->buffer->idx++;
     }
 
     return ret;
   }
@@ -1573,19 +1575,16 @@ struct GPOS : GSUBGPOS
 
   inline const PosLookup& get_lookup (unsigned int i) const
   { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
 
   template <typename set_t>
   inline void add_coverage (set_t *glyphs, unsigned int lookup_index) const
   { get_lookup (lookup_index).add_coverage (glyphs); }
 
-  inline bool position_lookup (hb_apply_context_t *c, unsigned int lookup_index) const
-  { return get_lookup (lookup_index).apply_string (c); }
-
   static inline void position_start (hb_font_t *font, hb_buffer_t *buffer);
   static inline void position_finish (hb_font_t *font, hb_buffer_t *buffer, hb_bool_t zero_width_attahced_marks);
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (unlikely (!GSUBGPOS::sanitize (c))) return TRACE_RETURN (false);
     OffsetTo<PosLookupList> &list = CastR<OffsetTo<PosLookupList> > (lookupList);
     return TRACE_RETURN (list.sanitize (c, this));
@@ -1593,30 +1592,30 @@ struct GPOS : GSUBGPOS
   public:
   DEFINE_SIZE_STATIC (10);
 };
 
 
 static void
 fix_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction)
 {
-    unsigned int j = pos[i].cursive_chain();
-    if (likely (!j))
-      return;
+  unsigned int j = pos[i].cursive_chain();
+  if (likely (!j))
+    return;
 
-    j += i;
+  j += i;
 
-    pos[i].cursive_chain() = 0;
+  pos[i].cursive_chain() = 0;
 
-    fix_cursive_minor_offset (pos, j, direction);
+  fix_cursive_minor_offset (pos, j, direction);
 
-    if (HB_DIRECTION_IS_HORIZONTAL (direction))
-      pos[i].y_offset += pos[j].y_offset;
-    else
-      pos[i].x_offset += pos[j].x_offset;
+  if (HB_DIRECTION_IS_HORIZONTAL (direction))
+    pos[i].y_offset += pos[j].y_offset;
+  else
+    pos[i].x_offset += pos[j].x_offset;
 }
 
 static void
 fix_mark_attachment (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, hb_bool_t zero_width_attached_marks)
 {
   if (likely (!(pos[i].attach_lookback())))
     return;
 
@@ -1708,10 +1707,12 @@ static inline bool position_lookup (hb_a
   return l.apply_once (&new_c);
 }
 
 
 #undef attach_lookback
 #undef cursive_chain
 
 
+} // namespace OT
+
 
 #endif /* HB_OT_LAYOUT_GPOS_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsub-table.hh
@@ -27,16 +27,18 @@
  */
 
 #ifndef HB_OT_LAYOUT_GSUB_TABLE_HH
 #define HB_OT_LAYOUT_GSUB_TABLE_HH
 
 #include "hb-ot-layout-gsubgpos-private.hh"
 
 
+namespace OT {
+
 
 struct SingleSubstFormat1
 {
   friend struct SingleSubst;
 
   private:
 
   inline void closure (hb_closure_context_t *c) const
@@ -65,16 +67,28 @@ struct SingleSubstFormat1
     /* According to the Adobe Annotated OpenType Suite, result is always
      * limited to 16bit. */
     glyph_id = (glyph_id + deltaGlyphID) & 0xFFFF;
     c->replace_glyph (glyph_id);
 
     return TRACE_RETURN (true);
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &glyphs,
+			 unsigned int num_glyphs,
+			 int delta)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
+    deltaGlyphID.set (delta); /* TODO(serilaize) overflow? */
+    return TRACE_RETURN (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && deltaGlyphID.sanitize (c));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
@@ -117,16 +131,28 @@ struct SingleSubstFormat2
     if (unlikely (index >= substitute.len)) return TRACE_RETURN (false);
 
     glyph_id = substitute[index];
     c->replace_glyph (glyph_id);
 
     return TRACE_RETURN (true);
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &glyphs,
+			 Supplier<GlyphID> &substitutes,
+			 unsigned int num_glyphs)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    if (unlikely (!substitute.serialize (c, substitutes, num_glyphs))) return TRACE_RETURN (false);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && substitute.sanitize (c));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 2 */
   OffsetTo<Coverage>
@@ -137,16 +163,17 @@ struct SingleSubstFormat2
 					 * GlyphIDs--ordered by Coverage Index */
   public:
   DEFINE_SIZE_ARRAY (6, substitute);
 };
 
 struct SingleSubst
 {
   friend struct SubstLookupSubTable;
+  friend struct SubstLookup;
 
   private:
 
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE ();
     switch (u.format) {
     case 1: u.format1.closure (c); break;
@@ -169,16 +196,43 @@ struct SingleSubst
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c));
     case 2: return TRACE_RETURN (u.format2.apply (c));
     default:return TRACE_RETURN (false);
     }
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &glyphs,
+			 Supplier<GlyphID> &substitutes,
+			 unsigned int num_glyphs)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
+    unsigned int format = 2;
+    int delta;
+    if (num_glyphs) {
+      format = 1;
+      /* TODO(serialize) check for wrap-around */
+      delta = substitutes[0] - glyphs[0];
+      for (unsigned int i = 1; i < num_glyphs; i++)
+	if (delta != substitutes[i] - glyphs[i]) {
+	  format = 2;
+	  break;
+	}
+    }
+    u.format.set (format);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, num_glyphs, delta));
+    case 2: return TRACE_RETURN (u.format2.serialize (c, glyphs, substitutes, num_glyphs));
+    default:return TRACE_RETURN (false);
+    }
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     case 2: return TRACE_RETURN (u.format2.sanitize (c));
     default:return TRACE_RETURN (true);
     }
@@ -218,16 +272,26 @@ struct Sequence
       set_lig_props_for_component (c->buffer->cur(), i);
       c->output_glyph (substitute.array[i], klass);
     }
     c->buffer->skip_glyph ();
 
     return TRACE_RETURN (true);
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &glyphs,
+			 unsigned int num_glyphs)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    if (unlikely (!substitute.serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
+  }
+
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (substitute.sanitize (c));
   }
 
   protected:
   ArrayOf<GlyphID>
@@ -262,16 +326,34 @@ struct MultipleSubstFormat1
     TRACE_APPLY ();
 
     unsigned int index = (this+coverage) (c->buffer->cur().codepoint);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     return TRACE_RETURN ((this+sequence[index]).apply (c));
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &glyphs,
+			 Supplier<unsigned int> &substitute_len_list,
+			 unsigned int num_glyphs,
+			 Supplier<GlyphID> &substitute_glyphs_list)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    if (unlikely (!sequence.serialize (c, num_glyphs))) return TRACE_RETURN (false);
+    for (unsigned int i = 0; i < num_glyphs; i++)
+      if (unlikely (!sequence[i].serialize (c, this).serialize (c,
+								substitute_glyphs_list,
+								substitute_len_list[i]))) return TRACE_RETURN (false);
+    substitute_len_list.advance (num_glyphs);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && sequence.sanitize (c, this));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
@@ -282,16 +364,17 @@ struct MultipleSubstFormat1
 					 * ordered by Coverage Index */
   public:
   DEFINE_SIZE_ARRAY (6, sequence);
 };
 
 struct MultipleSubst
 {
   friend struct SubstLookupSubTable;
+  friend struct SubstLookup;
 
   private:
 
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE ();
     switch (u.format) {
     case 1: u.format1.closure (c); break;
@@ -311,16 +394,32 @@ struct MultipleSubst
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c));
     default:return TRACE_RETURN (false);
     }
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &glyphs,
+			 Supplier<unsigned int> &substitute_len_list,
+			 unsigned int num_glyphs,
+			 Supplier<GlyphID> &substitute_glyphs_list)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
+    unsigned int format = 1;
+    u.format.set (format);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, substitute_len_list, num_glyphs, substitute_glyphs_list));
+    default:return TRACE_RETURN (false);
+    }
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
@@ -384,16 +483,34 @@ struct AlternateSubstFormat1
 
     glyph_id = alt_set[alt_index - 1];
 
     c->replace_glyph (glyph_id);
 
     return TRACE_RETURN (true);
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &glyphs,
+			 Supplier<unsigned int> &alternate_len_list,
+			 unsigned int num_glyphs,
+			 Supplier<GlyphID> &alternate_glyphs_list)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    if (unlikely (!alternateSet.serialize (c, num_glyphs))) return TRACE_RETURN (false);
+    for (unsigned int i = 0; i < num_glyphs; i++)
+      if (unlikely (!alternateSet[i].serialize (c, this).serialize (c,
+								    alternate_glyphs_list,
+								    alternate_len_list[i]))) return TRACE_RETURN (false);
+    alternate_len_list.advance (num_glyphs);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, glyphs, num_glyphs))) return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && alternateSet.sanitize (c, this));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
@@ -404,16 +521,17 @@ struct AlternateSubstFormat1
 					 * ordered by Coverage Index */
   public:
   DEFINE_SIZE_ARRAY (6, alternateSet);
 };
 
 struct AlternateSubst
 {
   friend struct SubstLookupSubTable;
+  friend struct SubstLookup;
 
   private:
 
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE ();
     switch (u.format) {
     case 1: u.format1.closure (c); break;
@@ -433,16 +551,32 @@ struct AlternateSubst
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c));
     default:return TRACE_RETURN (false);
     }
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &glyphs,
+			 Supplier<unsigned int> &alternate_len_list,
+			 unsigned int num_glyphs,
+			 Supplier<GlyphID> &alternate_glyphs_list)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
+    unsigned int format = 1;
+    u.format.set (format);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.serialize (c, glyphs, alternate_len_list, num_glyphs, alternate_glyphs_list));
+    default:return TRACE_RETURN (false);
+    }
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
@@ -484,145 +618,53 @@ struct Ligature
   }
 
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY ();
     unsigned int count = component.len;
     if (unlikely (count < 1)) return TRACE_RETURN (false);
 
-    hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
-    if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
+    unsigned int end_offset;
+    bool is_mark_ligature;
+    unsigned int total_component_count;
 
-    /*
-     * This is perhaps the trickiest part of GSUB...  Remarks:
-     *
-     * - If all components of the ligature were marks, we call this a mark ligature.
-     *
-     * - If there is no GDEF, and the ligature is NOT a mark ligature, we categorize
-     *   it as a ligature glyph.  Though, really, this will not really be used...
-     *
-     * - If it *is* a mark ligature, we don't allocate a new ligature id, and leave
-     *   the ligature to keep its old ligature id.  This will allow it to attach to
-     *   a base ligature in GPOS.  Eg. if the sequence is: LAM,LAM,SHADDA,FATHA,HEH,
-     *   and LAM,LAM,HEH for a ligature, they will leave SHADDA and FATHA wit a
-     *   ligature id and component value of 2.  Then if SHADDA,FATHA form a ligature
-     *   later, we don't want them to lose their ligature id/component, otherwise
-     *   GPOS will fail to correctly position the mark ligature on top of the
-     *   LAM,LAM,HEH ligature.  See:
-     *     https://bugzilla.gnome.org/show_bug.cgi?id=676343
-     *
-     * - If a ligature is formed of components that some of which are also ligatures
-     *   themselves, and those ligature components had marks attached to *their*
-     *   components, we have to attach the marks to the new ligature component
-     *   positions!  Now *that*'s tricky!  And these marks may be following the
-     *   last component of the whole sequence, so we should loop forward looking
-     *   for them and update them.
-     *
-     *   Eg. the sequence is LAM,LAM,SHADDA,FATHA,HEH, and the font first forms a
-     *   'calt' ligature of LAM,HEH, leaving the SHADDA and FATHA with a ligature
-     *   id and component == 1.  Now, during 'liga', the LAM and the LAM-HEH ligature
-     *   form a LAM-LAM-HEH ligature.  We need to reassign the SHADDA and FATHA to
-     *   the new ligature with a component value of 2.
-     *
-     *   This in fact happened to a font...  See:
-     *   https://bugzilla.gnome.org/show_bug.cgi?id=437633
-     *
-     * - Ligatures cannot be formed across glyphs attached to different components
-     *   of previous ligatures.  Eg. the sequence is LAM,SHADDA,LAM,FATHA,HEH, and
-     *   LAM,LAM,HEH form a ligature, leaving SHADDA,FATHA next to eachother.
-     *   However, it would be wrong to ligate that SHADDA,FATHA sequence.o
-     *   There is an exception to this: If a ligature tries ligating with marks that
-     *   belong to it itself, go ahead, assuming that the font designer knows what
-     *   they are doing (otherwise it can break Indic stuff when a matra wants to
-     *   ligate with a conjunct...)
-     */
-
-    bool is_mark_ligature = !!(c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
-
-    unsigned int total_component_count = 0;
-    total_component_count += get_lig_num_comps (c->buffer->cur());
-
-    unsigned int first_lig_id = get_lig_id (c->buffer->cur());
-    unsigned int first_lig_comp = get_lig_comp (c->buffer->cur());
-
-    for (unsigned int i = 1; i < count; i++)
-    {
-      unsigned int property;
-
-      if (!skippy_iter.next (&property)) return TRACE_RETURN (false);
-
-      if (likely (c->buffer->info[skippy_iter.idx].codepoint != component[i])) return TRACE_RETURN (false);
-
-      unsigned int this_lig_id = get_lig_id (c->buffer->info[skippy_iter.idx]);
-      unsigned int this_lig_comp = get_lig_comp (c->buffer->info[skippy_iter.idx]);
-
-      if (first_lig_id && first_lig_comp) {
-        /* If first component was attached to a previous ligature component,
-	 * all subsequent components should be attached to the same ligature
-	 * component, otherwise we shouldn't ligate them. */
-        if (first_lig_id != this_lig_id || first_lig_comp != this_lig_comp)
-	  return TRACE_RETURN (false);
-      } else {
-        /* If first component was NOT attached to a previous ligature component,
-	 * all subsequent components should also NOT be attached to any ligature
-	 * component, unless they are attached to the first component itself! */
-        if (this_lig_id && this_lig_comp && (this_lig_id != first_lig_id))
-	  return TRACE_RETURN (false);
-      }
-
-      is_mark_ligature = is_mark_ligature && (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
-      total_component_count += get_lig_num_comps (c->buffer->info[skippy_iter.idx]);
-    }
+    if (likely (!match_input (c, count,
+			      &component[1],
+			      match_glyph,
+			      NULL,
+			      &end_offset,
+			      &is_mark_ligature,
+			      &total_component_count)))
+      return TRACE_RETURN (false);
 
     /* Deal, we are forming the ligature. */
-    c->buffer->merge_clusters (c->buffer->idx, skippy_iter.idx + 1);
+    c->buffer->merge_clusters (c->buffer->idx, c->buffer->idx + end_offset);
 
-    unsigned int klass = is_mark_ligature ? 0 : HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
-    unsigned int lig_id = is_mark_ligature ? 0 : allocate_lig_id (c->buffer);
-    unsigned int last_lig_id = get_lig_id (c->buffer->cur());
-    unsigned int last_num_components = get_lig_num_comps (c->buffer->cur());
-    unsigned int components_so_far = last_num_components;
-
-    if (!is_mark_ligature)
-      set_lig_props_for_ligature (c->buffer->cur(), lig_id, total_component_count);
-    c->replace_glyph (ligGlyph, klass);
+    ligate_input (c,
+		  count,
+		  &component[1],
+		  ligGlyph,
+		  match_glyph,
+		  NULL,
+		  is_mark_ligature,
+		  total_component_count);
 
-    for (unsigned int i = 1; i < count; i++)
-    {
-      while (c->should_mark_skip_current_glyph ())
-      {
-	if (!is_mark_ligature) {
-	  unsigned int new_lig_comp = components_so_far - last_num_components +
-				      MIN (MAX (get_lig_comp (c->buffer->cur()), 1u), last_num_components);
-	  set_lig_props_for_mark (c->buffer->cur(), lig_id, new_lig_comp);
-	}
-	c->buffer->next_glyph ();
-      }
-
-      last_lig_id = get_lig_id (c->buffer->cur());
-      last_num_components = get_lig_num_comps (c->buffer->cur());
-      components_so_far += last_num_components;
+    return TRACE_RETURN (true);
+  }
 
-      /* Skip the base glyph */
-      c->buffer->idx++;
-    }
-
-    if (!is_mark_ligature && last_lig_id) {
-      /* Re-adjust components for any marks following. */
-      for (unsigned int i = c->buffer->idx; i < c->buffer->len; i++) {
-	if (last_lig_id == get_lig_id (c->buffer->info[i])) {
-	  unsigned int new_lig_comp = components_so_far - last_num_components +
-				      MIN (MAX (get_lig_comp (c->buffer->info[i]), 1u), last_num_components);
-	  set_lig_props_for_mark (c->buffer->info[i], lig_id, new_lig_comp);
-	} else
-	  break;
-      }
-    }
-
+  inline bool serialize (hb_serialize_context_t *c,
+			 GlyphID ligature,
+			 Supplier<GlyphID> &components, /* Starting from second */
+			 unsigned int num_components /* Including first component */)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    ligGlyph = ligature;
+    if (unlikely (!component.serialize (c, components, num_components))) return TRACE_RETURN (false);
     return TRACE_RETURN (true);
   }
 
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (ligGlyph.sanitize (c) && component.sanitize (c));
   }
@@ -671,16 +713,35 @@ struct LigatureSet
     {
       const Ligature &lig = this+ligature[i];
       if (lig.apply (c)) return TRACE_RETURN (true);
     }
 
     return TRACE_RETURN (false);
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &ligatures,
+			 Supplier<unsigned int> &component_count_list,
+			 unsigned int num_ligatures,
+			 Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    if (unlikely (!ligature.serialize (c, num_ligatures))) return TRACE_RETURN (false);
+    for (unsigned int i = 0; i < num_ligatures; i++)
+      if (unlikely (!ligature[i].serialize (c, this).serialize (c,
+								ligatures[i],
+								component_list,
+								component_count_list[i]))) return TRACE_RETURN (false);
+    ligatures.advance (num_ligatures);
+    component_count_list.advance (num_ligatures);
+    return TRACE_RETURN (true);
+  }
+
   public:
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (ligature.sanitize (c, this));
   }
 
   protected:
   OffsetArrayOf<Ligature>
@@ -723,16 +784,38 @@ struct LigatureSubstFormat1
 
     unsigned int index = (this+coverage) (glyph_id);
     if (likely (index == NOT_COVERED)) return TRACE_RETURN (false);
 
     const LigatureSet &lig_set = this+ligatureSet[index];
     return TRACE_RETURN (lig_set.apply (c));
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &first_glyphs,
+			 Supplier<unsigned int> &ligature_per_first_glyph_count_list,
+			 unsigned int num_first_glyphs,
+			 Supplier<GlyphID> &ligatures_list,
+			 Supplier<unsigned int> &component_count_list,
+			 Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (*this))) return TRACE_RETURN (false);
+    if (unlikely (!ligatureSet.serialize (c, num_first_glyphs))) return TRACE_RETURN (false);
+    for (unsigned int i = 0; i < num_first_glyphs; i++)
+      if (unlikely (!ligatureSet[i].serialize (c, this).serialize (c,
+								   ligatures_list,
+								   component_count_list,
+								   ligature_per_first_glyph_count_list[i],
+								   component_list))) return TRACE_RETURN (false);
+    ligature_per_first_glyph_count_list.advance (num_first_glyphs);
+    if (unlikely (!coverage.serialize (c, this).serialize (c, first_glyphs, num_first_glyphs))) return TRACE_RETURN (false);
+    return TRACE_RETURN (true);
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     return TRACE_RETURN (coverage.sanitize (c, this) && ligatureSet.sanitize (c, this));
   }
 
   protected:
   USHORT	format;			/* Format identifier--format = 1 */
   OffsetTo<Coverage>
@@ -743,16 +826,17 @@ struct LigatureSubstFormat1
 					 * ordered by Coverage Index */
   public:
   DEFINE_SIZE_ARRAY (6, ligatureSet);
 };
 
 struct LigatureSubst
 {
   friend struct SubstLookupSubTable;
+  friend struct SubstLookup;
 
   private:
 
   inline void closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE ();
     switch (u.format) {
     case 1: u.format1.closure (c); break;
@@ -780,16 +864,35 @@ struct LigatureSubst
   {
     TRACE_APPLY ();
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.apply (c));
     default:return TRACE_RETURN (false);
     }
   }
 
+  inline bool serialize (hb_serialize_context_t *c,
+			 Supplier<GlyphID> &first_glyphs,
+			 Supplier<unsigned int> &ligature_per_first_glyph_count_list,
+			 unsigned int num_first_glyphs,
+			 Supplier<GlyphID> &ligatures_list,
+			 Supplier<unsigned int> &component_count_list,
+			 Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!c->extend_min (u.format))) return TRACE_RETURN (false);
+    unsigned int format = 1;
+    u.format.set (format);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (u.format1.serialize (c, first_glyphs, ligature_per_first_glyph_count_list, num_first_glyphs,
+						      ligatures_list, component_count_list, component_list));
+    default:return TRACE_RETURN (false);
+    }
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
     switch (u.format) {
     case 1: return TRACE_RETURN (u.format1.sanitize (c));
     default:return TRACE_RETURN (true);
     }
   }
@@ -1178,20 +1281,20 @@ struct SubstLookup : Lookup
       const Coverage *c = &get_subtable (i).get_coverage (get_type ());
       if (c != last) {
         c->add_coverage (glyphs);
         last = c;
       }
     }
   }
 
-  inline bool would_apply (hb_would_apply_context_t *c) const
+  inline bool would_apply (hb_would_apply_context_t *c, const hb_set_digest_t *digest) const
   {
     if (unlikely (!c->len)) return false;
-    if (!c->digest.may_have (c->glyphs[0])) return false;
+    if (!digest->may_have (c->glyphs[0])) return false;
     unsigned int lookup_type = get_type ();
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++)
       if (get_subtable (i).would_apply (c, lookup_type))
 	return true;
     return false;
   }
 
@@ -1205,64 +1308,123 @@ struct SubstLookup : Lookup
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++)
       if (get_subtable (i).apply (c, lookup_type))
 	return true;
 
     return false;
   }
 
-  inline bool apply_string (hb_apply_context_t *c) const
+  inline bool apply_string (hb_apply_context_t *c, const hb_set_digest_t *digest) const
   {
     bool ret = false;
 
-    if (unlikely (!c->buffer->len))
+    if (unlikely (!c->buffer->len || !c->lookup_mask))
       return false;
 
     c->set_lookup (*this);
 
     if (likely (!is_reverse ()))
     {
 	/* in/out forward substitution */
 	c->buffer->clear_output ();
 	c->buffer->idx = 0;
 
 	while (c->buffer->idx < c->buffer->len)
 	{
 	  if ((c->buffer->cur().mask & c->lookup_mask) &&
-	      c->digest.may_have (c->buffer->cur().codepoint) &&
+	      digest->may_have (c->buffer->cur().codepoint) &&
 	      apply_once (c))
 	    ret = true;
 	  else
 	    c->buffer->next_glyph ();
 	}
 	if (ret)
 	  c->buffer->swap_buffers ();
     }
     else
     {
 	/* in-place backward substitution */
 	c->buffer->idx = c->buffer->len - 1;
 	do
 	{
 	  if ((c->buffer->cur().mask & c->lookup_mask) &&
-	      c->digest.may_have (c->buffer->cur().codepoint) &&
+	      digest->may_have (c->buffer->cur().codepoint) &&
 	      apply_once (c))
 	    ret = true;
 	  else
 	    c->buffer->idx--;
 
 	}
 	while ((int) c->buffer->idx >= 0);
     }
 
     return ret;
   }
 
-  inline bool sanitize (hb_sanitize_context_t *c) {
+  private:
+  inline SubstLookupSubTable& serialize_subtable (hb_serialize_context_t *c,
+						  unsigned int i)
+  { return CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i].serialize (c, this); }
+  public:
+
+  inline bool serialize_single (hb_serialize_context_t *c,
+				uint32_t lookup_props,
+			        Supplier<GlyphID> &glyphs,
+			        Supplier<GlyphID> &substitutes,
+			        unsigned int num_glyphs)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Single, lookup_props, 1))) return TRACE_RETURN (false);
+    return TRACE_RETURN (serialize_subtable (c, 0).u.single.serialize (c, glyphs, substitutes, num_glyphs));
+  }
+
+  inline bool serialize_multiple (hb_serialize_context_t *c,
+				  uint32_t lookup_props,
+				  Supplier<GlyphID> &glyphs,
+				  Supplier<unsigned int> &substitute_len_list,
+				  unsigned int num_glyphs,
+				  Supplier<GlyphID> &substitute_glyphs_list)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Multiple, lookup_props, 1))) return TRACE_RETURN (false);
+    return TRACE_RETURN (serialize_subtable (c, 0).u.multiple.serialize (c, glyphs, substitute_len_list, num_glyphs,
+									 substitute_glyphs_list));
+  }
+
+  inline bool serialize_alternate (hb_serialize_context_t *c,
+				   uint32_t lookup_props,
+				   Supplier<GlyphID> &glyphs,
+				   Supplier<unsigned int> &alternate_len_list,
+				   unsigned int num_glyphs,
+				   Supplier<GlyphID> &alternate_glyphs_list)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Alternate, lookup_props, 1))) return TRACE_RETURN (false);
+    return TRACE_RETURN (serialize_subtable (c, 0).u.alternate.serialize (c, glyphs, alternate_len_list, num_glyphs,
+									  alternate_glyphs_list));
+  }
+
+  inline bool serialize_ligature (hb_serialize_context_t *c,
+				  uint32_t lookup_props,
+				  Supplier<GlyphID> &first_glyphs,
+				  Supplier<unsigned int> &ligature_per_first_glyph_count_list,
+				  unsigned int num_first_glyphs,
+				  Supplier<GlyphID> &ligatures_list,
+				  Supplier<unsigned int> &component_count_list,
+				  Supplier<GlyphID> &component_list /* Starting from second for each ligature */)
+  {
+    TRACE_SERIALIZE ();
+    if (unlikely (!Lookup::serialize (c, SubstLookupSubTable::Ligature, lookup_props, 1))) return TRACE_RETURN (false);
+    return TRACE_RETURN (serialize_subtable (c, 0).u.ligature.serialize (c, first_glyphs, ligature_per_first_glyph_count_list, num_first_glyphs,
+									 ligatures_list, component_count_list, component_list));
+  }
+
+  inline bool sanitize (hb_sanitize_context_t *c)
+  {
     TRACE_SANITIZE ();
     if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
     OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
     if (unlikely (!list.sanitize (c, this, get_type ()))) return TRACE_RETURN (false);
 
     if (unlikely (get_type () == SubstLookupSubTable::Extension))
     {
       /* The spec says all subtables of an Extension lookup should
@@ -1292,22 +1454,16 @@ struct GSUB : GSUBGPOS
 
   inline const SubstLookup& get_lookup (unsigned int i) const
   { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
 
   template <typename set_t>
   inline void add_coverage (set_t *glyphs, unsigned int lookup_index) const
   { get_lookup (lookup_index).add_coverage (glyphs); }
 
-  inline bool would_substitute_lookup (hb_would_apply_context_t *c, unsigned int lookup_index) const
-  { return get_lookup (lookup_index).would_apply (c); }
-
-  inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index) const
-  { return get_lookup (lookup_index).apply_string (c); }
-
   static inline void substitute_start (hb_font_t *font, hb_buffer_t *buffer);
   static inline void substitute_finish (hb_font_t *font, hb_buffer_t *buffer);
 
   inline void closure_lookup (hb_closure_context_t *c,
 			      unsigned int          lookup_index) const
   { return get_lookup (lookup_index).closure (c); }
 
   inline bool sanitize (hb_sanitize_context_t *c) {
@@ -1405,10 +1561,12 @@ static inline bool substitute_lookup (hb
 
   hb_apply_context_t new_c (*c);
   new_c.nesting_level_left--;
   new_c.set_lookup (l);
   return l.apply_once (&new_c);
 }
 
 
+} // namespace OT
+
 
 #endif /* HB_OT_LAYOUT_GSUB_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos-private.hh
@@ -29,16 +29,18 @@
 #ifndef HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
 #define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
 
 #include "hb-buffer-private.hh"
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-set-private.hh"
 
 
+namespace OT {
+
 
 #ifndef HB_DEBUG_CLOSURE
 #define HB_DEBUG_CLOSURE (HB_DEBUG+0)
 #endif
 
 #define TRACE_CLOSURE() \
 	hb_auto_trace_t<HB_DEBUG_CLOSURE> trace (&c->debug_depth, "CLOSURE", this, HB_FUNC, "");
 
@@ -71,28 +73,27 @@ struct hb_closure_context_t
 	hb_auto_trace_t<HB_DEBUG_WOULD_APPLY> trace (&c->debug_depth, "WOULD_APPLY", this, HB_FUNC, "%d glyphs", c->len);
 
 
 struct hb_would_apply_context_t
 {
   hb_face_t *face;
   const hb_codepoint_t *glyphs;
   unsigned int len;
-  const hb_set_digest_t digest;
+  bool zero_context;
   unsigned int debug_depth;
 
   hb_would_apply_context_t (hb_face_t *face_,
 			    const hb_codepoint_t *glyphs_,
 			    unsigned int len_,
-			    const hb_set_digest_t *digest_
-			    ) :
+			    bool zero_context_) :
 			      face (face_),
 			      glyphs (glyphs_),
 			      len (len_),
-			      digest (*digest_),
+			      zero_context (zero_context_),
 			      debug_depth (0) {};
 };
 
 
 #ifndef HB_DEBUG_APPLY
 #define HB_DEBUG_APPLY (HB_DEBUG+0)
 #endif
 
@@ -108,31 +109,32 @@ struct hb_apply_context_t
   hb_direction_t direction;
   hb_mask_t lookup_mask;
   unsigned int nesting_level_left;
   unsigned int lookup_props;
   unsigned int property; /* propety of first glyph */
   unsigned int debug_depth;
   const GDEF &gdef;
   bool has_glyph_classes;
-  const hb_set_digest_t digest;
 
 
   hb_apply_context_t (hb_font_t *font_,
 		      hb_buffer_t *buffer_,
-		      hb_mask_t lookup_mask_,
-		      const hb_set_digest_t *digest_) :
+		      hb_mask_t lookup_mask_) :
 			font (font_), face (font->face), buffer (buffer_),
 			direction (buffer_->props.direction),
 			lookup_mask (lookup_mask_),
 			nesting_level_left (MAX_NESTING_LEVEL),
 			lookup_props (0), property (0), debug_depth (0),
 			gdef (*hb_ot_layout_from_face (face)->gdef),
-			has_glyph_classes (gdef.has_glyph_classes ()),
-			digest (*digest_) {}
+			has_glyph_classes (gdef.has_glyph_classes ()) {}
+
+  void set_lookup_props (unsigned int lookup_props_) {
+    lookup_props = lookup_props_;
+  }
 
   void set_lookup (const Lookup &l) {
     lookup_props = l.get_props ();
   }
 
   struct mark_skipping_forward_iterator_t
   {
     inline mark_skipping_forward_iterator_t (hb_apply_context_t *c_,
@@ -411,80 +413,218 @@ static inline bool would_match_input (hb
 
   return true;
 }
 static inline bool match_input (hb_apply_context_t *c,
 				unsigned int count, /* Including the first glyph (not matched) */
 				const USHORT input[], /* Array of input values--start with second glyph */
 				match_func_t match_func,
 				const void *match_data,
-				unsigned int *end_offset = NULL)
+				unsigned int *end_offset = NULL,
+				bool *p_is_mark_ligature = NULL,
+				unsigned int *p_total_component_count = NULL)
 {
+  hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", NULL, HB_FUNC, "idx %d codepoint %u", c->buffer->idx, c->buffer->cur().codepoint);
+
   hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
-  if (skippy_iter.has_no_chance ())
-    return false;
+  if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
+
+  /*
+   * This is perhaps the trickiest part of OpenType...  Remarks:
+   *
+   * - If all components of the ligature were marks, we call this a mark ligature.
+   *
+   * - If there is no GDEF, and the ligature is NOT a mark ligature, we categorize
+   *   it as a ligature glyph.
+   *
+   * - Ligatures cannot be formed across glyphs attached to different components
+   *   of previous ligatures.  Eg. the sequence is LAM,SHADDA,LAM,FATHA,HEH, and
+   *   LAM,LAM,HEH form a ligature, leaving SHADDA,FATHA next to eachother.
+   *   However, it would be wrong to ligate that SHADDA,FATHA sequence.o
+   *   There is an exception to this: If a ligature tries ligating with marks that
+   *   belong to it itself, go ahead, assuming that the font designer knows what
+   *   they are doing (otherwise it can break Indic stuff when a matra wants to
+   *   ligate with a conjunct...)
+   */
+
+  bool is_mark_ligature = !!(c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
+
+  unsigned int total_component_count = 0;
+  total_component_count += get_lig_num_comps (c->buffer->cur());
+
+  unsigned int first_lig_id = get_lig_id (c->buffer->cur());
+  unsigned int first_lig_comp = get_lig_comp (c->buffer->cur());
 
   for (unsigned int i = 1; i < count; i++)
   {
-    if (!skippy_iter.next ())
-      return false;
+    unsigned int property;
+
+    if (!skippy_iter.next (&property)) return TRACE_RETURN (false);
+
+    if (likely (!match_func (c->buffer->info[skippy_iter.idx].codepoint, input[i - 1], match_data))) return false;
+
+    unsigned int this_lig_id = get_lig_id (c->buffer->info[skippy_iter.idx]);
+    unsigned int this_lig_comp = get_lig_comp (c->buffer->info[skippy_iter.idx]);
 
-    if (likely (!match_func (c->buffer->info[skippy_iter.idx].codepoint, input[i - 1], match_data)))
-      return false;
+    if (first_lig_id && first_lig_comp) {
+      /* If first component was attached to a previous ligature component,
+       * all subsequent components should be attached to the same ligature
+       * component, otherwise we shouldn't ligate them. */
+      if (first_lig_id != this_lig_id || first_lig_comp != this_lig_comp)
+	return TRACE_RETURN (false);
+    } else {
+      /* If first component was NOT attached to a previous ligature component,
+       * all subsequent components should also NOT be attached to any ligature
+       * component, unless they are attached to the first component itself! */
+      if (this_lig_id && this_lig_comp && (this_lig_id != first_lig_id))
+	return TRACE_RETURN (false);
+    }
+
+    is_mark_ligature = is_mark_ligature && (property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
+    total_component_count += get_lig_num_comps (c->buffer->info[skippy_iter.idx]);
   }
 
   if (end_offset)
     *end_offset = skippy_iter.idx - c->buffer->idx + 1;
 
+  if (p_is_mark_ligature)
+    *p_is_mark_ligature = is_mark_ligature;
+
+  if (p_total_component_count)
+    *p_total_component_count = total_component_count;
+
   return true;
 }
+static inline void ligate_input (hb_apply_context_t *c,
+				 unsigned int count, /* Including the first glyph (not matched) */
+				 const USHORT input[], /* Array of input values--start with second glyph */
+				 hb_codepoint_t lig_glyph,
+				 match_func_t match_func,
+				 const void *match_data,
+				 bool is_mark_ligature,
+				 unsigned int total_component_count)
+{
+  /*
+   * - If it *is* a mark ligature, we don't allocate a new ligature id, and leave
+   *   the ligature to keep its old ligature id.  This will allow it to attach to
+   *   a base ligature in GPOS.  Eg. if the sequence is: LAM,LAM,SHADDA,FATHA,HEH,
+   *   and LAM,LAM,HEH for a ligature, they will leave SHADDA and FATHA wit a
+   *   ligature id and component value of 2.  Then if SHADDA,FATHA form a ligature
+   *   later, we don't want them to lose their ligature id/component, otherwise
+   *   GPOS will fail to correctly position the mark ligature on top of the
+   *   LAM,LAM,HEH ligature.  See:
+   *     https://bugzilla.gnome.org/show_bug.cgi?id=676343
+   *
+   * - If a ligature is formed of components that some of which are also ligatures
+   *   themselves, and those ligature components had marks attached to *their*
+   *   components, we have to attach the marks to the new ligature component
+   *   positions!  Now *that*'s tricky!  And these marks may be following the
+   *   last component of the whole sequence, so we should loop forward looking
+   *   for them and update them.
+   *
+   *   Eg. the sequence is LAM,LAM,SHADDA,FATHA,HEH, and the font first forms a
+   *   'calt' ligature of LAM,HEH, leaving the SHADDA and FATHA with a ligature
+   *   id and component == 1.  Now, during 'liga', the LAM and the LAM-HEH ligature
+   *   form a LAM-LAM-HEH ligature.  We need to reassign the SHADDA and FATHA to
+   *   the new ligature with a component value of 2.
+   *
+   *   This in fact happened to a font...  See:
+   *   https://bugzilla.gnome.org/show_bug.cgi?id=437633
+   */
+
+  unsigned int klass = is_mark_ligature ? 0 : HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE;
+  unsigned int lig_id = is_mark_ligature ? 0 : allocate_lig_id (c->buffer);
+  unsigned int last_lig_id = get_lig_id (c->buffer->cur());
+  unsigned int last_num_components = get_lig_num_comps (c->buffer->cur());
+  unsigned int components_so_far = last_num_components;
+
+  if (!is_mark_ligature)
+    set_lig_props_for_ligature (c->buffer->cur(), lig_id, total_component_count);
+  c->replace_glyph (lig_glyph, klass);
+
+  for (unsigned int i = 1; i < count; i++)
+  {
+    while (c->should_mark_skip_current_glyph ())
+    {
+      if (!is_mark_ligature) {
+	unsigned int new_lig_comp = components_so_far - last_num_components +
+				    MIN (MAX (get_lig_comp (c->buffer->cur()), 1u), last_num_components);
+	set_lig_props_for_mark (c->buffer->cur(), lig_id, new_lig_comp);
+      }
+      c->buffer->next_glyph ();
+    }
+
+    last_lig_id = get_lig_id (c->buffer->cur());
+    last_num_components = get_lig_num_comps (c->buffer->cur());
+    components_so_far += last_num_components;
+
+    /* Skip the base glyph */
+    c->buffer->idx++;
+  }
+
+  if (!is_mark_ligature && last_lig_id) {
+    /* Re-adjust components for any marks following. */
+    for (unsigned int i = c->buffer->idx; i < c->buffer->len; i++) {
+      if (last_lig_id == get_lig_id (c->buffer->info[i])) {
+	unsigned int new_lig_comp = components_so_far - last_num_components +
+				    MIN (MAX (get_lig_comp (c->buffer->info[i]), 1u), last_num_components);
+	set_lig_props_for_mark (c->buffer->info[i], lig_id, new_lig_comp);
+      } else
+	break;
+    }
+  }
+}
 
 static inline bool match_backtrack (hb_apply_context_t *c,
 				    unsigned int count,
 				    const USHORT backtrack[],
 				    match_func_t match_func,
 				    const void *match_data)
 {
+  hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", NULL, HB_FUNC, "idx %d codepoint %u", c->buffer->idx, c->buffer->cur().codepoint);
+
   hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count, true);
   if (skippy_iter.has_no_chance ())
-    return false;
+    return TRACE_RETURN (false);
 
   for (unsigned int i = 0; i < count; i++)
   {
     if (!skippy_iter.prev ())
-      return false;
+      return TRACE_RETURN (false);
 
     if (likely (!match_func (c->buffer->out_info[skippy_iter.idx].codepoint, backtrack[i], match_data)))
-      return false;
+      return TRACE_RETURN (false);
   }
 
-  return true;
+  return TRACE_RETURN (true);
 }
 
 static inline bool match_lookahead (hb_apply_context_t *c,
 				    unsigned int count,
 				    const USHORT lookahead[],
 				    match_func_t match_func,
 				    const void *match_data,
 				    unsigned int offset)
 {
+  hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", NULL, HB_FUNC, "idx %d codepoint %u", c->buffer->idx, c->buffer->cur().codepoint);
+
   hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count, true);
   if (skippy_iter.has_no_chance ())
-    return false;
+    return TRACE_RETURN (false);
 
   for (unsigned int i = 0; i < count; i++)
   {
     if (!skippy_iter.next ())
-      return false;
+      return TRACE_RETURN (false);
 
     if (likely (!match_func (c->buffer->info[skippy_iter.idx].codepoint, lookahead[i], match_data)))
-      return false;
+      return TRACE_RETURN (false);
   }
 
-  return true;
+  return TRACE_RETURN (true);
 }
 
 
 
 struct LookupRecord
 {
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE ();
@@ -1061,18 +1201,17 @@ static inline bool chain_context_would_a
 						     unsigned int inputCount, /* Including the first glyph (not matched) */
 						     const USHORT input[], /* Array of input values--start with second glyph */
 						     unsigned int lookaheadCount,
 						     const USHORT lookahead[],
 						     unsigned int lookupCount,
 						     const LookupRecord lookupRecord[],
 						     ChainContextApplyLookupContext &lookup_context)
 {
-  return !backtrackCount
-      && !lookaheadCount
+  return (c->zero_context ? !backtrackCount && !lookaheadCount : true)
       && would_match_input (c,
 			    inputCount, input,
 			    lookup_context.funcs.match, lookup_context.match_data[1]);
 }
 
 static inline bool chain_context_apply_lookup (hb_apply_context_t *c,
 					       unsigned int backtrackCount,
 					       const USHORT backtrack[],
@@ -1675,10 +1814,12 @@ struct GSUBGPOS
 		featureList; 	/* FeatureList table */
   OffsetTo<LookupList>
 		lookupList; 	/* LookupList table */
   public:
   DEFINE_SIZE_STATIC (10);
 };
 
 
+} // namespace OT
+
 
 #endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-ot-layout-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-layout-private.hh
@@ -134,19 +134,20 @@ static inline uint8_t allocate_lig_id (h
   if (unlikely (!lig_id))
     lig_id = allocate_lig_id (buffer); /* in case of overflow */
   return lig_id;
 }
 
 
 HB_INTERNAL hb_bool_t
 hb_ot_layout_would_substitute_lookup_fast (hb_face_t            *face,
+					   unsigned int          lookup_index,
 					   const hb_codepoint_t *glyphs,
 					   unsigned int          glyphs_length,
-					   unsigned int          lookup_index);
+					   hb_bool_t             zero_context);
 
 
 /* Should be called before all the substitute_lookup's are done. */
 HB_INTERNAL void
 hb_ot_layout_substitute_start (hb_font_t    *font,
 			       hb_buffer_t  *buffer);
 
 HB_INTERNAL hb_bool_t
@@ -179,25 +180,31 @@ hb_ot_layout_position_finish (hb_font_t 
 			      hb_bool_t     zero_width_attached_marks);
 
 
 
 /*
  * hb_ot_layout_t
  */
 
+namespace OT {
+  struct GDEF;
+  struct GSUB;
+  struct GPOS;
+}
+
 struct hb_ot_layout_t
 {
   hb_blob_t *gdef_blob;
   hb_blob_t *gsub_blob;
   hb_blob_t *gpos_blob;
 
-  const struct GDEF *gdef;
-  const struct GSUB *gsub;
-  const struct GPOS *gpos;
+  const struct OT::GDEF *gdef;
+  const struct OT::GSUB *gsub;
+  const struct OT::GPOS *gpos;
 
   unsigned int gsub_lookup_count;
   unsigned int gpos_lookup_count;
 
   hb_set_digest_t *gsub_digests;
   hb_set_digest_t *gpos_digests;
 };
 
--- a/gfx/harfbuzz/src/hb-ot-layout.cc
+++ b/gfx/harfbuzz/src/hb-ot-layout.cc
@@ -30,38 +30,37 @@
 
 #include "hb-ot-layout-private.hh"
 
 #include "hb-ot-layout-gdef-table.hh"
 #include "hb-ot-layout-gsub-table.hh"
 #include "hb-ot-layout-gpos-table.hh"
 #include "hb-ot-maxp-table.hh"
 
-
 #include <stdlib.h>
 #include <string.h>
 
 
 HB_SHAPER_DATA_ENSURE_DECLARE(ot, face)
 
 hb_ot_layout_t *
 _hb_ot_layout_create (hb_face_t *face)
 {
   hb_ot_layout_t *layout = (hb_ot_layout_t *) calloc (1, sizeof (hb_ot_layout_t));
   if (unlikely (!layout))
     return NULL;
 
-  layout->gdef_blob = Sanitizer<GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF));
-  layout->gdef = Sanitizer<GDEF>::lock_instance (layout->gdef_blob);
+  layout->gdef_blob = OT::Sanitizer<OT::GDEF>::sanitize (face->reference_table (HB_OT_TAG_GDEF));
+  layout->gdef = OT::Sanitizer<OT::GDEF>::lock_instance (layout->gdef_blob);
 
-  layout->gsub_blob = Sanitizer<GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB));
-  layout->gsub = Sanitizer<GSUB>::lock_instance (layout->gsub_blob);
+  layout->gsub_blob = OT::Sanitizer<OT::GSUB>::sanitize (face->reference_table (HB_OT_TAG_GSUB));
+  layout->gsub = OT::Sanitizer<OT::GSUB>::lock_instance (layout->gsub_blob);
 
-  layout->gpos_blob = Sanitizer<GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
-  layout->gpos = Sanitizer<GPOS>::lock_instance (layout->gpos_blob);
+  layout->gpos_blob = OT::Sanitizer<OT::GPOS>::sanitize (face->reference_table (HB_OT_TAG_GPOS));
+  layout->gpos = OT::Sanitizer<OT::GPOS>::lock_instance (layout->gpos_blob);
 
   layout->gsub_lookup_count = layout->gsub->get_lookup_count ();
   layout->gpos_lookup_count = layout->gpos->get_lookup_count ();
 
   layout->gsub_digests = (hb_set_digest_t *) calloc (layout->gsub->get_lookup_count (), sizeof (hb_set_digest_t));
   layout->gpos_digests = (hb_set_digest_t *) calloc (layout->gpos->get_lookup_count (), sizeof (hb_set_digest_t));
 
   if (unlikely ((layout->gsub_lookup_count && !layout->gsub_digests) ||
@@ -87,32 +86,32 @@ void
   hb_blob_destroy (layout->gpos_blob);
 
   free (layout->gsub_digests);
   free (layout->gpos_digests);
 
   free (layout);
 }
 
-static inline const GDEF&
+static inline const OT::GDEF&
 _get_gdef (hb_face_t *face)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(GDEF);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GDEF);
   return *hb_ot_layout_from_face (face)->gdef;
 }
-static inline const GSUB&
+static inline const OT::GSUB&
 _get_gsub (hb_face_t *face)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(GSUB);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GSUB);
   return *hb_ot_layout_from_face (face)->gsub;
 }
-static inline const GPOS&
+static inline const OT::GPOS&
 _get_gpos (hb_face_t *face)
 {
-  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return Null(GPOS);
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return OT::Null(OT::GPOS);
   return *hb_ot_layout_from_face (face)->gpos;
 }
 
 
 /*
  * GDEF
  */
 
@@ -144,48 +143,48 @@ hb_ot_layout_get_ligature_carets (hb_fon
   return _get_gdef (font->face).get_lig_carets (font, direction, glyph, start_offset, caret_count, caret_array);
 }
 
 
 /*
  * GSUB/GPOS
  */
 
-static const GSUBGPOS&
+static const OT::GSUBGPOS&
 get_gsubgpos_table (hb_face_t *face,
 		    hb_tag_t   table_tag)
 {
   switch (table_tag) {
     case HB_OT_TAG_GSUB: return _get_gsub (face);
     case HB_OT_TAG_GPOS: return _get_gpos (face);
-    default:             return Null(GSUBGPOS);
+    default:             return OT::Null(OT::GSUBGPOS);
   }
 }
 
 
 unsigned int
 hb_ot_layout_table_get_script_tags (hb_face_t    *face,
 				    hb_tag_t      table_tag,
 				    unsigned int  start_offset,
 				    unsigned int *script_count /* IN/OUT */,
 				    hb_tag_t     *script_tags /* OUT */)
 {
-  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
   return g.get_script_tags (start_offset, script_count, script_tags);
 }
 
 hb_bool_t
 hb_ot_layout_table_find_script (hb_face_t    *face,
 				hb_tag_t      table_tag,
 				hb_tag_t      script_tag,
 				unsigned int *script_index)
 {
-  ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
-  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
   if (g.find_script_index (script_tag, script_index))
     return true;
 
   /* try finding 'DFLT' */
   if (g.find_script_index (HB_OT_TAG_DEFAULT_SCRIPT, script_index))
     return false;
 
@@ -200,18 +199,18 @@ hb_ot_layout_table_find_script (hb_face_
 
 hb_bool_t
 hb_ot_layout_table_choose_script (hb_face_t      *face,
 				  hb_tag_t        table_tag,
 				  const hb_tag_t *script_tags,
 				  unsigned int   *script_index,
 				  hb_tag_t       *chosen_script)
 {
-  ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
-  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_SCRIPT_INDEX);
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
   while (*script_tags)
   {
     if (g.find_script_index (*script_tags, script_index)) {
       if (chosen_script)
         *chosen_script = *script_tags;
       return true;
     }
@@ -249,44 +248,44 @@ hb_ot_layout_table_choose_script (hb_fac
 
 unsigned int
 hb_ot_layout_table_get_feature_tags (hb_face_t    *face,
 				     hb_tag_t      table_tag,
 				     unsigned int  start_offset,
 				     unsigned int *feature_count /* IN/OUT */,
 				     hb_tag_t     *feature_tags /* OUT */)
 {
-  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
 
   return g.get_feature_tags (start_offset, feature_count, feature_tags);
 }
 
 
 unsigned int
 hb_ot_layout_script_get_language_tags (hb_face_t    *face,
 				       hb_tag_t      table_tag,
 				       unsigned int  script_index,
 				       unsigned int  start_offset,
 				       unsigned int *language_count /* IN/OUT */,
 				       hb_tag_t     *language_tags /* OUT */)
 {
-  const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
+  const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
 
   return s.get_lang_sys_tags (start_offset, language_count, language_tags);
 }
 
 hb_bool_t
 hb_ot_layout_script_find_language (hb_face_t    *face,
 				   hb_tag_t      table_tag,
 				   unsigned int  script_index,
 				   hb_tag_t      language_tag,
 				   unsigned int *language_index)
 {
-  ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
-  const Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
+  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_DEFAULT_LANGUAGE_INDEX);
+  const OT::Script &s = get_gsubgpos_table (face, table_tag).get_script (script_index);
 
   if (s.find_lang_sys_index (language_tag, language_index))
     return true;
 
   /* try with 'dflt'; MS site has had typos and many fonts use it now :( */
   if (s.find_lang_sys_index (HB_OT_TAG_DEFAULT_LANGUAGE, language_index))
     return false;
 
@@ -296,49 +295,49 @@ hb_ot_layout_script_find_language (hb_fa
 
 hb_bool_t
 hb_ot_layout_language_get_required_feature_index (hb_face_t    *face,
 						  hb_tag_t      table_tag,
 						  unsigned int  script_index,
 						  unsigned int  language_index,
 						  unsigned int *feature_index)
 {
-  const LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index);
+  const OT::LangSys &l = get_gsubgpos_table (face, table_tag).get_script (script_index).get_lang_sys (language_index);
 
   if (feature_index) *feature_index = l.get_required_feature_index ();
 
   return l.has_required_feature ();
 }
 
 unsigned int
 hb_ot_layout_language_get_feature_indexes (hb_face_t    *face,
 					   hb_tag_t      table_tag,
 					   unsigned int  script_index,
 					   unsigned int  language_index,
 					   unsigned int  start_offset,
 					   unsigned int *feature_count /* IN/OUT */,
 					   unsigned int *feature_indexes /* OUT */)
 {
-  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
-  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
 
   return l.get_feature_indexes (start_offset, feature_count, feature_indexes);
 }
 
 unsigned int
 hb_ot_layout_language_get_feature_tags (hb_face_t    *face,
 					hb_tag_t      table_tag,
 					unsigned int  script_index,
 					unsigned int  language_index,
 					unsigned int  start_offset,
 					unsigned int *feature_count /* IN/OUT */,
 					hb_tag_t     *feature_tags /* OUT */)
 {
-  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
-  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
 
   ASSERT_STATIC (sizeof (unsigned int) == sizeof (hb_tag_t));
   unsigned int ret = l.get_feature_indexes (start_offset, feature_count, (unsigned int *) feature_tags);
 
   if (feature_tags) {
     unsigned int count = *feature_count;
     for (unsigned int i = 0; i < count; i++)
       feature_tags[i] = g.get_feature_tag ((unsigned int) feature_tags[i]);
@@ -351,19 +350,19 @@ hb_ot_layout_language_get_feature_tags (
 hb_bool_t
 hb_ot_layout_language_find_feature (hb_face_t    *face,
 				    hb_tag_t      table_tag,
 				    unsigned int  script_index,
 				    unsigned int  language_index,
 				    hb_tag_t      feature_tag,
 				    unsigned int *feature_index)
 {
-  ASSERT_STATIC (Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
-  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
-  const LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
+  ASSERT_STATIC (OT::Index::NOT_FOUND_INDEX == HB_OT_LAYOUT_NO_FEATURE_INDEX);
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+  const OT::LangSys &l = g.get_script (script_index).get_lang_sys (language_index);
 
   unsigned int num_features = l.get_feature_count ();
   for (unsigned int i = 0; i < num_features; i++) {
     unsigned int f_index = l.get_feature_index (i);
 
     if (feature_tag == g.get_feature_tag (f_index)) {
       if (feature_index) *feature_index = f_index;
       return true;
@@ -377,110 +376,123 @@ hb_ot_layout_language_find_feature (hb_f
 unsigned int
 hb_ot_layout_feature_get_lookup_indexes (hb_face_t    *face,
 					 hb_tag_t      table_tag,
 					 unsigned int  feature_index,
 					 unsigned int  start_offset,
 					 unsigned int *lookup_count /* IN/OUT */,
 					 unsigned int *lookup_indexes /* OUT */)
 {
-  const GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
-  const Feature &f = g.get_feature (feature_index);
+  const OT::GSUBGPOS &g = get_gsubgpos_table (face, table_tag);
+  const OT::Feature &f = g.get_feature (feature_index);
 
   return f.get_lookup_indexes (start_offset, lookup_count, lookup_indexes);
 }
 
 
 /*
- * GSUB
+ * OT::GSUB
  */
 
 hb_bool_t
 hb_ot_layout_has_substitution (hb_face_t *face)
 {
-  return &_get_gsub (face) != &Null(GSUB);
+  return &_get_gsub (face) != &OT::Null(OT::GSUB);
 }
 
 hb_bool_t
 hb_ot_layout_would_substitute_lookup (hb_face_t            *face,
+				      unsigned int          lookup_index,
 				      const hb_codepoint_t *glyphs,
 				      unsigned int          glyphs_length,
-				      unsigned int          lookup_index)
+				      hb_bool_t             zero_context)
 {
   if (unlikely (!hb_ot_shaper_face_data_ensure (face))) return false;
-  return hb_ot_layout_would_substitute_lookup_fast (face, glyphs, glyphs_length, lookup_index);
+  return hb_ot_layout_would_substitute_lookup_fast (face, lookup_index, glyphs, glyphs_length, zero_context);
 }
 
 hb_bool_t
 hb_ot_layout_would_substitute_lookup_fast (hb_face_t            *face,
+					   unsigned int          lookup_index,
 					   const hb_codepoint_t *glyphs,
 					   unsigned int          glyphs_length,
-					   unsigned int          lookup_index)
+					   hb_bool_t             zero_context)
 {
   if (unlikely (lookup_index >= hb_ot_layout_from_face (face)->gsub_lookup_count)) return false;
-  hb_would_apply_context_t c (face, glyphs, glyphs_length, &hb_ot_layout_from_face (face)->gsub_digests[lookup_index]);
-  return hb_ot_layout_from_face (face)->gsub->would_substitute_lookup (&c, lookup_index);
+  OT::hb_would_apply_context_t c (face, glyphs, glyphs_length, zero_context);
+
+  const OT::SubstLookup& l = hb_ot_layout_from_face (face)->gsub->get_lookup (lookup_index);
+
+  return l.would_apply (&c, &hb_ot_layout_from_face (face)->gsub_digests[lookup_index]);
 }
 
 void
 hb_ot_layout_substitute_start (hb_font_t *font, hb_buffer_t *buffer)
 {
-  GSUB::substitute_start (font, buffer);
+  OT::GSUB::substitute_start (font, buffer);
 }
 
 hb_bool_t
 hb_ot_layout_substitute_lookup (hb_font_t    *font,
 				hb_buffer_t  *buffer,
 				unsigned int  lookup_index,
 				hb_mask_t     mask)
 {
   if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gsub_lookup_count)) return false;
-  hb_apply_context_t c (font, buffer, mask, &hb_ot_layout_from_face (font->face)->gsub_digests[lookup_index]);
-  return hb_ot_layout_from_face (font->face)->gsub->substitute_lookup (&c, lookup_index);
+
+  OT::hb_apply_context_t c (font, buffer, mask);
+
+  const OT::SubstLookup& l = hb_ot_layout_from_face (font->face)->gsub->get_lookup (lookup_index);
+
+  return l.apply_string (&c, &hb_ot_layout_from_face (font->face)->gsub_digests[lookup_index]);
 }
 
 void
 hb_ot_layout_substitute_finish (hb_font_t *font, hb_buffer_t *buffer)
 {
-  GSUB::substitute_finish (font, buffer);
+  OT::GSUB::substitute_finish (font, buffer);
 }
 
 void
 hb_ot_layout_substitute_closure_lookup (hb_face_t    *face,
-				        hb_set_t     *glyphs,
-				        unsigned int  lookup_index)
+				        unsigned int  lookup_index,
+				        hb_set_t     *glyphs)
 {
-  hb_closure_context_t c (face, glyphs);
+  OT::hb_closure_context_t c (face, glyphs);
   _get_gsub (face).closure_lookup (&c, lookup_index);
 }
 
 /*
- * GPOS
+ * OT::GPOS
  */
 
 hb_bool_t
 hb_ot_layout_has_positioning (hb_face_t *face)
 {
-  return &_get_gpos (face) != &Null(GPOS);
+  return &_get_gpos (face) != &OT::Null(OT::GPOS);
 }
 
 void
 hb_ot_layout_position_start (hb_font_t *font, hb_buffer_t *buffer)
 {
-  GPOS::position_start (font, buffer);
+  OT::GPOS::position_start (font, buffer);
 }
 
 hb_bool_t
 hb_ot_layout_position_lookup (hb_font_t    *font,
 			      hb_buffer_t  *buffer,
 			      unsigned int  lookup_index,
 			      hb_mask_t     mask)
 {
   if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gpos_lookup_count)) return false;
-  hb_apply_context_t c (font, buffer, mask, &hb_ot_layout_from_face (font->face)->gpos_digests[lookup_index]);
-  return hb_ot_layout_from_face (font->face)->gpos->position_lookup (&c, lookup_index);
+
+  OT::hb_apply_context_t c (font, buffer, mask);
+
+  const OT::PosLookup& l = hb_ot_layout_from_face (font->face)->gpos->get_lookup (lookup_index);
+
+  return l.apply_string (&c, &hb_ot_layout_from_face (font->face)->gpos_digests[lookup_index]);
 }
 
 void
 hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer, hb_bool_t zero_width_attached_marks)
 {
-  GPOS::position_finish (font, buffer, zero_width_attached_marks);
+  OT::GPOS::position_finish (font, buffer, zero_width_attached_marks);
 }
--- a/gfx/harfbuzz/src/hb-ot-layout.h
+++ b/gfx/harfbuzz/src/hb-ot-layout.h
@@ -166,24 +166,25 @@ hb_ot_layout_feature_get_lookup_indexes 
  * GSUB
  */
 
 hb_bool_t
 hb_ot_layout_has_substitution (hb_face_t *face);
 
 hb_bool_t
 hb_ot_layout_would_substitute_lookup (hb_face_t            *face,
+				      unsigned int          lookup_index,
 				      const hb_codepoint_t *glyphs,
 				      unsigned int          glyphs_length,
-				      unsigned int          lookup_index);
+				      hb_bool_t             zero_context);
 
 void
 hb_ot_layout_substitute_closure_lookup (hb_face_t    *face,
-				        hb_set_t     *glyphs,
-				        unsigned int  lookup_index);
+				        unsigned int  lookup_index,
+				        hb_set_t     *glyphs);
 
 /*
  * GPOS
  */
 
 hb_bool_t
 hb_ot_layout_has_positioning (hb_face_t *face);
 
--- a/gfx/harfbuzz/src/hb-ot-map-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-map-private.hh
@@ -44,16 +44,17 @@ 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 */
+    hb_bool_t needs_fallback;
 
     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 int index;
     hb_mask_t mask;
@@ -75,16 +76,21 @@ struct hb_ot_map_t
   inline hb_mask_t get_global_mask (void) const { return global_mask; }
 
   inline hb_mask_t get_mask (hb_tag_t feature_tag, unsigned int *shift = NULL) const {
     const feature_map_t *map = features.bsearch (&feature_tag);
     if (shift) *shift = map ? map->shift : 0;
     return map ? map->mask : 0;
   }
 
+  inline bool needs_fallback (hb_tag_t feature_tag) const {
+    const feature_map_t *map = features.bsearch (&feature_tag);
+    return map ? map->needs_fallback : false;
+  }
+
   inline hb_mask_t get_1_mask (hb_tag_t feature_tag) const {
     const feature_map_t *map = features.bsearch (&feature_tag);
     return map ? map->_1_mask : 0;
   }
 
   inline unsigned int get_feature_index (unsigned int table_index, hb_tag_t feature_tag) const {
     const feature_map_t *map = features.bsearch (&feature_tag);
     return map ? map->index[table_index] : HB_OT_LAYOUT_NO_FEATURE_INDEX;
@@ -142,20 +148,20 @@ struct hb_ot_map_t
 
 
 struct hb_ot_map_builder_t
 {
   public:
 
   hb_ot_map_builder_t (void) { memset (this, 0, sizeof (*this)); }
 
-  HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value, bool global);
+  HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value, bool global, bool has_fallback = false);
 
-  inline void add_bool_feature (hb_tag_t tag, bool global = true)
-  { add_feature (tag, 1, global); }
+  inline void add_bool_feature (hb_tag_t tag, bool global = true, bool has_fallback = false)
+  { add_feature (tag, 1, global, has_fallback); }
 
   inline void add_gsub_pause (hb_ot_map_t::pause_func_t pause_func)
   { add_pause (0, pause_func); }
   inline void add_gpos_pause (hb_ot_map_t::pause_func_t pause_func)
   { add_pause (1, pause_func); }
 
   HB_INTERNAL void compile (hb_face_t *face,
 			    const hb_segment_properties_t *props,
@@ -169,16 +175,17 @@ struct hb_ot_map_builder_t
 
   private:
 
   struct feature_info_t {
     hb_tag_t tag;
     unsigned int seq; /* sequence#, used for stable sorting only */
     unsigned int max_value;
     bool global; /* whether the feature applies value to every glyph in the buffer */
+    bool has_fallback; /* whether to allocate bits even if feature not found */
     unsigned int default_value; /* for non-global features, what should the unset glyphs take */
     unsigned int stage[2]; /* GSUB/GPOS */
 
     static int cmp (const feature_info_t *a, const feature_info_t *b)
     { return (a->tag != b->tag) ?  (a->tag < b->tag ? -1 : 1) : (a->seq < b->seq ? -1 : 1); }
   };
 
   struct pause_info_t {
--- a/gfx/harfbuzz/src/hb-ot-map.cc
+++ b/gfx/harfbuzz/src/hb-ot-map.cc
@@ -55,24 +55,25 @@ hb_ot_map_t::add_lookups (hb_face_t    *
       lookup->index = lookup_indices[i];
     }
 
     offset += len;
   } while (len == ARRAY_LENGTH (lookup_indices));
 }
 
 
-void hb_ot_map_builder_t::add_feature (hb_tag_t tag, unsigned int value, bool global)
+void hb_ot_map_builder_t::add_feature (hb_tag_t tag, unsigned int value, bool global, bool has_fallback)
 {
   feature_info_t *info = feature_infos.push();
   if (unlikely (!info)) return;
   info->tag = tag;
   info->seq = feature_infos.len;
   info->max_value = value;
   info->global = global;
+  info->has_fallback = has_fallback;
   info->default_value = global ? value : 0;
   info->stage[0] = current_stage[0];
   info->stage[1] = current_stage[1];
 }
 
 /* Keep the next two functions in sync. */
 
 void hb_ot_map_t::substitute (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
@@ -111,26 +112,18 @@ void hb_ot_map_t::position (const hb_ot_
 
   for (; i < lookups[table_index].len; i++)
     hb_ot_layout_position_lookup (font, buffer, lookups[table_index][i].index, lookups[table_index][i].mask);
 }
 
 void hb_ot_map_t::substitute_closure (const hb_ot_shape_plan_t *plan, hb_face_t *face, hb_set_t *glyphs) const
 {
   unsigned int table_index = 0;
-  unsigned int i = 0;
-
-  for (unsigned int pause_index = 0; pause_index < pauses[table_index].len; pause_index++) {
-    const pause_map_t *pause = &pauses[table_index][pause_index];
-    for (; i < pause->num_lookups; i++)
-      hb_ot_layout_substitute_closure_lookup (face, glyphs, lookups[table_index][i].index);
-  }
-
-  for (; i < lookups[table_index].len; i++)
-    hb_ot_layout_substitute_closure_lookup (face, glyphs, lookups[table_index][i].index);
+  for (unsigned int i = 0; i < lookups[table_index].len; i++)
+    hb_ot_layout_substitute_closure_lookup (face, lookups[table_index][i].index, glyphs);
 }
 
 void hb_ot_map_builder_t::add_pause (unsigned int table_index, hb_ot_map_t::pause_func_t pause_func)
 {
   pause_info_t *p = pauses[table_index].push ();
   if (likely (p)) {
     p->stage = current_stage[table_index];
     p->callback = pause_func;
@@ -178,16 +171,17 @@ hb_ot_map_builder_t::compile (hb_face_t 
 	if (feature_infos[i].global) {
 	  feature_infos[j].global = true;
 	  feature_infos[j].max_value = feature_infos[i].max_value;
 	  feature_infos[j].default_value = feature_infos[i].default_value;
 	} else {
 	  feature_infos[j].global = false;
 	  feature_infos[j].max_value = MAX (feature_infos[j].max_value, feature_infos[i].max_value);
 	}
+	feature_infos[j].has_fallback = feature_infos[j].has_fallback || feature_infos[i].has_fallback;
 	feature_infos[j].stage[0] = MIN (feature_infos[j].stage[0], feature_infos[i].stage[0]);
 	feature_infos[j].stage[1] = MIN (feature_infos[j].stage[1], feature_infos[i].stage[1]);
 	/* Inherit default_value from j */
       }
     feature_infos.shrink (j + 1);
   }
 
 
@@ -212,17 +206,17 @@ hb_ot_map_builder_t::compile (hb_face_t 
     unsigned int feature_index[2];
     for (unsigned int table_index = 0; table_index < 2; table_index++)
       found |= hb_ot_layout_language_find_feature (face,
 						   table_tags[table_index],
 						   script_index[table_index],
 						   language_index[table_index],
 						   info->tag,
 						   &feature_index[table_index]);
-    if (!found)
+    if (!found && !info->has_fallback)
       continue;
 
 
     hb_ot_map_t::feature_map_t *map = m.features.push ();
     if (unlikely (!map))
       break;
 
     map->tag = info->tag;
@@ -237,16 +231,17 @@ hb_ot_map_builder_t::compile (hb_face_t 
     } else {
       map->shift = next_bit;
       map->mask = (1 << (next_bit + bits_needed)) - (1 << next_bit);
       next_bit += bits_needed;
       if (info->global)
 	m.global_mask |= (info->default_value << map->shift) & map->mask;
     }
     map->_1_mask = (1 << map->shift) & map->mask;
+    map->needs_fallback = !found;
 
   }
   feature_infos.shrink (0); /* Done with these */
 
 
   add_gsub_pause (NULL);
   add_gpos_pause (NULL);
 
--- a/gfx/harfbuzz/src/hb-ot-maxp-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-maxp-table.hh
@@ -25,16 +25,18 @@
  */
 
 #ifndef HB_OT_MAXP_TABLE_HH
 #define HB_OT_MAXP_TABLE_HH
 
 #include "hb-open-type-private.hh"
 
 
+namespace OT {
+
 
 /*
  * maxp -- The Maximum Profile Table
  */
 
 #define HB_OT_TAG_maxp HB_TAG('m','a','x','p')
 
 struct maxp
@@ -56,10 +58,12 @@ struct maxp
   FixedVersion	version;		/* Version of the maxp table (0.5 or 1.0),
 					 * 0x00005000 or 0x00010000. */
   USHORT	numGlyphs;		/* The number of glyphs in the font. */
   public:
   DEFINE_SIZE_STATIC (6);
 };
 
 
+} // namespace OT
+
 
 #endif /* HB_OT_MAXP_TABLE_HH */
--- a/gfx/harfbuzz/src/hb-ot-name-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-name-table.hh
@@ -25,16 +25,18 @@
  */
 
 #ifndef HB_OT_NAME_TABLE_HH
 #define HB_OT_NAME_TABLE_HH
 
 #include "hb-open-type-private.hh"
 
 
+namespace OT {
+
 
 /*
  * name -- The Naming Table
  */
 
 #define HB_OT_TAG_name HB_TAG('n','a','m','e')
 
 
@@ -119,10 +121,12 @@ struct name
   USHORT	count;			/* Number of name records. */
   Offset	stringOffset;		/* Offset to start of string storage (from start of table). */
   NameRecord	nameRecord[VAR];	/* The name records where count is the number of records. */
   public:
   DEFINE_SIZE_ARRAY (6, nameRecord);
 };
 
 
+} // namespace OT
+
 
 #endif /* HB_OT_NAME_TABLE_HH */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-fallback.hh
@@ -0,0 +1,253 @@
+/*
+ * Copyright © 2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH
+#define HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH
+
+#include "hb-private.hh"
+
+#include "hb-ot-shape-private.hh"
+#include "hb-ot-layout-gsub-table.hh"
+
+
+static const hb_tag_t arabic_fallback_features[] =
+{
+  HB_TAG('i','n','i','t'),
+  HB_TAG('m','e','d','i'),
+  HB_TAG('f','i','n','a'),
+  HB_TAG('i','s','o','l'),
+  HB_TAG('r','l','i','g'),
+};
+
+/* Same order as the fallback feature array */
+enum {
+  FALLBACK_INIT,
+  FALLBACK_MEDI,
+  FALLBACK_FINA,
+  FALLBACK_ISOL,
+  FALLBACK_RLIG,
+  ARABIC_NUM_FALLBACK_FEATURES
+};
+
+static OT::SubstLookup *
+arabic_fallback_synthesize_lookup_single (const hb_ot_shape_plan_t *plan,
+					  hb_font_t *font,
+					  unsigned int feature_index)
+{
+  OT::GlyphID glyphs[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1];
+  OT::GlyphID substitutes[SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1];
+  unsigned int num_glyphs = 0;
+
+  /* Populate arrays */
+  for (hb_codepoint_t u = SHAPING_TABLE_FIRST; u < SHAPING_TABLE_LAST + 1; u++)
+  {
+    hb_codepoint_t s = shaping_table[u - SHAPING_TABLE_FIRST][feature_index];
+    hb_codepoint_t u_glyph, s_glyph;
+
+    if (!s ||
+	!hb_font_get_glyph (font, u, 0, &u_glyph) ||
+	!hb_font_get_glyph (font, s, 0, &s_glyph) ||
+	u_glyph == s_glyph ||
+	u_glyph > 0xFFFF || s_glyph > 0xFFFF)
+      continue;
+
+    glyphs[num_glyphs].set (u_glyph);
+    substitutes[num_glyphs].set (s_glyph);
+
+    num_glyphs++;
+  }
+
+  /* Bubble-sort!
+   * May not be good-enough for presidential candidate interviews, but good-enough for us... */
+  hb_bubble_sort (&glyphs[0], num_glyphs, OT::GlyphID::cmp, &substitutes[0]);
+
+  OT::Supplier<OT::GlyphID> glyphs_supplier      (glyphs, num_glyphs);
+  OT::Supplier<OT::GlyphID> substitutes_supplier (substitutes, num_glyphs);
+
+  /* Each glyph takes four bytes max, and there's some overhead. */
+  char buf[(SHAPING_TABLE_LAST - SHAPING_TABLE_FIRST + 1) * 4 + 128];
+  OT::hb_serialize_context_t c (buf, sizeof (buf));
+  OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> ();
+  bool ret = lookup->serialize_single (&c,
+				       OT::LookupFlag::IgnoreMarks,
+				       glyphs_supplier,
+				       substitutes_supplier,
+				       num_glyphs);
+  c.end_serialize ();
+  /* TODO sanitize the results? */
+
+  return ret ? c.copy<OT::SubstLookup> () : NULL;
+}
+
+static OT::SubstLookup *
+arabic_fallback_synthesize_lookup_ligature (const hb_ot_shape_plan_t *plan,
+					    hb_font_t *font)
+{
+  OT::GlyphID first_glyphs[ARRAY_LENGTH_CONST (ligature_table)];
+  unsigned int first_glyphs_indirection[ARRAY_LENGTH_CONST (ligature_table)];
+  unsigned int ligature_per_first_glyph_count_list[ARRAY_LENGTH_CONST (first_glyphs)];
+  unsigned int num_first_glyphs = 0;
+
+  /* We know that all our ligatures are 2-component */
+  OT::GlyphID ligature_list[ARRAY_LENGTH_CONST (first_glyphs) * ARRAY_LENGTH_CONST(ligature_table[0].ligatures)];
+  unsigned int component_count_list[ARRAY_LENGTH_CONST (ligature_list)];
+  OT::GlyphID component_list[ARRAY_LENGTH_CONST (ligature_list) * 1/* One extra component per ligature */];
+  unsigned int num_ligatures = 0;
+
+  /* Populate arrays */
+
+  /* Sort out the first-glyphs */
+  for (unsigned int first_glyph_idx = 0; first_glyph_idx < ARRAY_LENGTH (first_glyphs); first_glyph_idx++)
+  {
+    hb_codepoint_t first_u = ligature_table[first_glyph_idx].first;
+    hb_codepoint_t first_glyph;
+    if (!hb_font_get_glyph (font, first_u, 0, &first_glyph))
+      continue;
+    first_glyphs[num_first_glyphs].set (first_glyph);
+    ligature_per_first_glyph_count_list[num_first_glyphs] = 0;
+    first_glyphs_indirection[num_first_glyphs] = first_glyph_idx;
+    num_first_glyphs++;
+  }
+  hb_bubble_sort (&first_glyphs[0], num_first_glyphs, OT::GlyphID::cmp, &first_glyphs_indirection[0]);
+
+  /* Now that the first-glyphs are sorted, walk again, populate ligatures. */
+  for (unsigned int i = 0; i < num_first_glyphs; i++)
+  {
+    unsigned int first_glyph_idx = first_glyphs_indirection[i];
+
+    for (unsigned int second_glyph_idx = 0; second_glyph_idx < ARRAY_LENGTH (ligature_table[0].ligatures); second_glyph_idx++)
+    {
+      hb_codepoint_t second_u   = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].second;
+      hb_codepoint_t ligature_u = ligature_table[first_glyph_idx].ligatures[second_glyph_idx].ligature;
+      hb_codepoint_t second_glyph, ligature_glyph;
+      if (!second_u ||
+	  !hb_font_get_glyph (font, second_u,   0, &second_glyph) ||
+	  !hb_font_get_glyph (font, ligature_u, 0, &ligature_glyph))
+	continue;
+
+      ligature_per_first_glyph_count_list[i]++;
+
+      ligature_list[num_ligatures].set (ligature_glyph);
+      component_count_list[num_ligatures] = 2;
+      component_list[num_ligatures].set (second_glyph);
+      num_ligatures++;
+    }
+  }
+
+  OT::Supplier<OT::GlyphID>   first_glyphs_supplier                      (first_glyphs, num_first_glyphs);
+  OT::Supplier<unsigned int > ligature_per_first_glyph_count_supplier    (ligature_per_first_glyph_count_list, num_first_glyphs);
+  OT::Supplier<OT::GlyphID>   ligatures_supplier                         (ligature_list, num_ligatures);
+  OT::Supplier<unsigned int > component_count_supplier                   (component_count_list, num_ligatures);
+  OT::Supplier<OT::GlyphID>   component_supplier                         (component_list, num_ligatures);
+
+  /* 16 bytes per ligature ought to be enough... */
+  char buf[ARRAY_LENGTH_CONST (ligature_list) * 16 + 128];
+  OT::hb_serialize_context_t c (buf, sizeof (buf));
+  OT::SubstLookup *lookup = c.start_serialize<OT::SubstLookup> ();
+  bool ret = lookup->serialize_ligature (&c,
+					 OT::LookupFlag::IgnoreMarks,
+					 first_glyphs_supplier,
+					 ligature_per_first_glyph_count_supplier,
+					 num_first_glyphs,
+					 ligatures_supplier,
+					 component_count_supplier,
+					 component_supplier);
+
+  c.end_serialize ();
+  /* TODO sanitize the results? */
+
+  return ret ? c.copy<OT::SubstLookup> () : NULL;
+}
+
+static OT::SubstLookup *
+arabic_fallback_synthesize_lookup (const hb_ot_shape_plan_t *plan,
+				   hb_font_t *font,
+				   unsigned int feature_index)
+{
+  if (feature_index < 4)
+    return arabic_fallback_synthesize_lookup_single (plan, font, feature_index);
+  else
+    return arabic_fallback_synthesize_lookup_ligature (plan, font);
+}
+
+struct arabic_fallback_plan_t
+{
+  ASSERT_POD ();
+
+  hb_mask_t mask_array[ARABIC_NUM_FALLBACK_FEATURES];
+  OT::SubstLookup *lookup_array[ARABIC_NUM_FALLBACK_FEATURES];
+  hb_set_digest_t digest_array[ARABIC_NUM_FALLBACK_FEATURES];
+};
+
+static const arabic_fallback_plan_t arabic_fallback_plan_nil = {};
+
+static arabic_fallback_plan_t *
+arabic_fallback_plan_create (const hb_ot_shape_plan_t *plan,
+			     hb_font_t *font)
+{
+  arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) calloc (1, sizeof (arabic_fallback_plan_t));
+  if (unlikely (!fallback_plan))
+    return const_cast<arabic_fallback_plan_t *> (&arabic_fallback_plan_nil);
+
+  for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++) {
+    fallback_plan->mask_array[i] = plan->map.get_1_mask (arabic_fallback_features[i]);
+    if (fallback_plan->mask_array[i]) {
+      fallback_plan->lookup_array[i] = arabic_fallback_synthesize_lookup (plan, font, i);
+      if (fallback_plan->lookup_array[i])
+	fallback_plan->lookup_array[i]->add_coverage (&fallback_plan->digest_array[i]);
+    }
+  }
+
+  return fallback_plan;
+}
+
+static void
+arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan)
+{
+  if (!fallback_plan || fallback_plan == &arabic_fallback_plan_nil)
+    return;
+
+  for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++)
+    if (fallback_plan->lookup_array[i])
+      free (fallback_plan->lookup_array[i]);
+
+  free (fallback_plan);
+}
+
+static void
+arabic_fallback_plan_shape (arabic_fallback_plan_t *fallback_plan,
+			    hb_font_t *font,
+			    hb_buffer_t *buffer)
+{
+  for (unsigned int i = 0; i < ARABIC_NUM_FALLBACK_FEATURES; i++)
+    if (fallback_plan->lookup_array[i]) {
+      OT::hb_apply_context_t c (font, buffer, fallback_plan->mask_array[i]);
+      fallback_plan->lookup_array[i]->apply_string (&c, &fallback_plan->digest_array[i]);
+    }
+}
+
+
+#endif /* HB_OT_SHAPE_COMPLEX_ARABIC_FALLBACK_HH */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic-table.hh
@@ -724,195 +724,195 @@ static const uint8_t joining_table[] =
 };
 
 #define JOINING_TABLE_FIRST	0x0600
 #define JOINING_TABLE_LAST	0x08AC
 
 
 static const uint16_t shaping_table[][4] =
 {
-  {0x0621, 0x0621, 0x0621, 0xFE80}, /* U+0621 ARABIC LETTER HAMZA ISOLATED FORM */
-  {0x0622, 0x0622, 0xFE82, 0xFE81}, /* U+0622 ARABIC LETTER ALEF WITH MADDA ABOVE */
-  {0x0623, 0x0623, 0xFE84, 0xFE83}, /* U+0623 ARABIC LETTER ALEF WITH HAMZA ABOVE */
-  {0x0624, 0x0624, 0xFE86, 0xFE85}, /* U+0624 ARABIC LETTER WAW WITH HAMZA ABOVE */
-  {0x0625, 0x0625, 0xFE88, 0xFE87}, /* U+0625 ARABIC LETTER ALEF WITH HAMZA BELOW */
+  {0x0000, 0x0000, 0x0000, 0xFE80}, /* U+0621 ARABIC LETTER HAMZA ISOLATED FORM */
+  {0x0000, 0x0000, 0xFE82, 0xFE81}, /* U+0622 ARABIC LETTER ALEF WITH MADDA ABOVE */
+  {0x0000, 0x0000, 0xFE84, 0xFE83}, /* U+0623 ARABIC LETTER ALEF WITH HAMZA ABOVE */
+  {0x0000, 0x0000, 0xFE86, 0xFE85}, /* U+0624 ARABIC LETTER WAW WITH HAMZA ABOVE */
+  {0x0000, 0x0000, 0xFE88, 0xFE87}, /* U+0625 ARABIC LETTER ALEF WITH HAMZA BELOW */
   {0xFE8B, 0xFE8C, 0xFE8A, 0xFE89}, /* U+0626 ARABIC LETTER YEH WITH HAMZA ABOVE */
-  {0x0627, 0x0627, 0xFE8E, 0xFE8D}, /* U+0627 ARABIC LETTER ALEF */
+  {0x0000, 0x0000, 0xFE8E, 0xFE8D}, /* U+0627 ARABIC LETTER ALEF */
   {0xFE91, 0xFE92, 0xFE90, 0xFE8F}, /* U+0628 ARABIC LETTER BEH */
-  {0x0629, 0x0629, 0xFE94, 0xFE93}, /* U+0629 ARABIC LETTER TEH MARBUTA */
+  {0x0000, 0x0000, 0xFE94, 0xFE93}, /* U+0629 ARABIC LETTER TEH MARBUTA */
   {0xFE97, 0xFE98, 0xFE96, 0xFE95}, /* U+062A ARABIC LETTER TEH */
   {0xFE9B, 0xFE9C, 0xFE9A, 0xFE99}, /* U+062B ARABIC LETTER THEH */
   {0xFE9F, 0xFEA0, 0xFE9E, 0xFE9D}, /* U+062C ARABIC LETTER JEEM */
   {0xFEA3, 0xFEA4, 0xFEA2, 0xFEA1}, /* U+062D ARABIC LETTER HAH */
   {0xFEA7, 0xFEA8, 0xFEA6, 0xFEA5}, /* U+062E ARABIC LETTER KHAH */
-  {0x062F, 0x062F, 0xFEAA, 0xFEA9}, /* U+062F ARABIC LETTER DAL */
-  {0x0630, 0x0630, 0xFEAC, 0xFEAB}, /* U+0630 ARABIC LETTER THAL */
-  {0x0631, 0x0631, 0xFEAE, 0xFEAD}, /* U+0631 ARABIC LETTER REH */
-  {0x0632, 0x0632, 0xFEB0, 0xFEAF}, /* U+0632 ARABIC LETTER ZAIN */
+  {0x0000, 0x0000, 0xFEAA, 0xFEA9}, /* U+062F ARABIC LETTER DAL */
+  {0x0000, 0x0000, 0xFEAC, 0xFEAB}, /* U+0630 ARABIC LETTER THAL */
+  {0x0000, 0x0000, 0xFEAE, 0xFEAD}, /* U+0631 ARABIC LETTER REH */
+  {0x0000, 0x0000, 0xFEB0, 0xFEAF}, /* U+0632 ARABIC LETTER ZAIN */
   {0xFEB3, 0xFEB4, 0xFEB2, 0xFEB1}, /* U+0633 ARABIC LETTER SEEN */
   {0xFEB7, 0xFEB8, 0xFEB6, 0xFEB5}, /* U+0634 ARABIC LETTER SHEEN */
   {0xFEBB, 0xFEBC, 0xFEBA, 0xFEB9}, /* U+0635 ARABIC LETTER SAD */
   {0xFEBF, 0xFEC0, 0xFEBE, 0xFEBD}, /* U+0636 ARABIC LETTER DAD */
   {0xFEC3, 0xFEC4, 0xFEC2, 0xFEC1}, /* U+0637 ARABIC LETTER TAH */
   {0xFEC7, 0xFEC8, 0xFEC6, 0xFEC5}, /* U+0638 ARABIC LETTER ZAH */
   {0xFECB, 0xFECC, 0xFECA, 0xFEC9}, /* U+0639 ARABIC LETTER AIN */
   {0xFECF, 0xFED0, 0xFECE, 0xFECD}, /* U+063A ARABIC LETTER GHAIN */
-  {0x063B, 0x063B, 0x063B, 0x063B}, /* U+063B  */
-  {0x063C, 0x063C, 0x063C, 0x063C}, /* U+063C  */
-  {0x063D, 0x063D, 0x063D, 0x063D}, /* U+063D  */
-  {0x063E, 0x063E, 0x063E, 0x063E}, /* U+063E  */
-  {0x063F, 0x063F, 0x063F, 0x063F}, /* U+063F  */
-  {0x0640, 0x0640, 0x0640, 0x0640}, /* U+0640  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+063B  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+063C  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+063D  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+063E  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+063F  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0640  */
   {0xFED3, 0xFED4, 0xFED2, 0xFED1}, /* U+0641 ARABIC LETTER FEH */
   {0xFED7, 0xFED8, 0xFED6, 0xFED5}, /* U+0642 ARABIC LETTER QAF */
   {0xFEDB, 0xFEDC, 0xFEDA, 0xFED9}, /* U+0643 ARABIC LETTER KAF */
   {0xFEDF, 0xFEE0, 0xFEDE, 0xFEDD}, /* U+0644 ARABIC LETTER LAM */
   {0xFEE3, 0xFEE4, 0xFEE2, 0xFEE1}, /* U+0645 ARABIC LETTER MEEM */
   {0xFEE7, 0xFEE8, 0xFEE6, 0xFEE5}, /* U+0646 ARABIC LETTER NOON */
   {0xFEEB, 0xFEEC, 0xFEEA, 0xFEE9}, /* U+0647 ARABIC LETTER HEH */
-  {0x0648, 0x0648, 0xFEEE, 0xFEED}, /* U+0648 ARABIC LETTER WAW */
+  {0x0000, 0x0000, 0xFEEE, 0xFEED}, /* U+0648 ARABIC LETTER WAW */
   {0xFBE8, 0xFBE9, 0xFEF0, 0xFEEF}, /* U+0649 ARABIC LETTER */
   {0xFEF3, 0xFEF4, 0xFEF2, 0xFEF1}, /* U+064A ARABIC LETTER YEH */
-  {0x064B, 0x064B, 0x064B, 0x064B}, /* U+064B  */
-  {0x064C, 0x064C, 0x064C, 0x064C}, /* U+064C  */
-  {0x064D, 0x064D, 0x064D, 0x064D}, /* U+064D  */
-  {0x064E, 0x064E, 0x064E, 0x064E}, /* U+064E  */
-  {0x064F, 0x064F, 0x064F, 0x064F}, /* U+064F  */
-  {0x0650, 0x0650, 0x0650, 0x0650}, /* U+0650  */
-  {0x0651, 0x0651, 0x0651, 0x0651}, /* U+0651  */
-  {0x0652, 0x0652, 0x0652, 0x0652}, /* U+0652  */
-  {0x0653, 0x0653, 0x0653, 0x0653}, /* U+0653  */
-  {0x0654, 0x0654, 0x0654, 0x0654}, /* U+0654  */
-  {0x0655, 0x0655, 0x0655, 0x0655}, /* U+0655  */
-  {0x0656, 0x0656, 0x0656, 0x0656}, /* U+0656  */
-  {0x0657, 0x0657, 0x0657, 0x0657}, /* U+0657  */
-  {0x0658, 0x0658, 0x0658, 0x0658}, /* U+0658  */
-  {0x0659, 0x0659, 0x0659, 0x0659}, /* U+0659  */
-  {0x065A, 0x065A, 0x065A, 0x065A}, /* U+065A  */
-  {0x065B, 0x065B, 0x065B, 0x065B}, /* U+065B  */
-  {0x065C, 0x065C, 0x065C, 0x065C}, /* U+065C  */
-  {0x065D, 0x065D, 0x065D, 0x065D}, /* U+065D  */
-  {0x065E, 0x065E, 0x065E, 0x065E}, /* U+065E  */
-  {0x065F, 0x065F, 0x065F, 0x065F}, /* U+065F  */
-  {0x0660, 0x0660, 0x0660, 0x0660}, /* U+0660  */
-  {0x0661, 0x0661, 0x0661, 0x0661}, /* U+0661  */
-  {0x0662, 0x0662, 0x0662, 0x0662}, /* U+0662  */
-  {0x0663, 0x0663, 0x0663, 0x0663}, /* U+0663  */
-  {0x0664, 0x0664, 0x0664, 0x0664}, /* U+0664  */
-  {0x0665, 0x0665, 0x0665, 0x0665}, /* U+0665  */
-  {0x0666, 0x0666, 0x0666, 0x0666}, /* U+0666  */
-  {0x0667, 0x0667, 0x0667, 0x0667}, /* U+0667  */
-  {0x0668, 0x0668, 0x0668, 0x0668}, /* U+0668  */
-  {0x0669, 0x0669, 0x0669, 0x0669}, /* U+0669  */
-  {0x066A, 0x066A, 0x066A, 0x066A}, /* U+066A  */
-  {0x066B, 0x066B, 0x066B, 0x066B}, /* U+066B  */
-  {0x066C, 0x066C, 0x066C, 0x066C}, /* U+066C  */
-  {0x066D, 0x066D, 0x066D, 0x066D}, /* U+066D  */
-  {0x066E, 0x066E, 0x066E, 0x066E}, /* U+066E  */
-  {0x066F, 0x066F, 0x066F, 0x066F}, /* U+066F  */
-  {0x0670, 0x0670, 0x0670, 0x0670}, /* U+0670  */
-  {0x0671, 0x0671, 0xFB51, 0xFB50}, /* U+0671 ARABIC LETTER ALEF WASLA */
-  {0x0672, 0x0672, 0x0672, 0x0672}, /* U+0672  */
-  {0x0673, 0x0673, 0x0673, 0x0673}, /* U+0673  */
-  {0x0674, 0x0674, 0x0674, 0x0674}, /* U+0674  */
-  {0x0675, 0x0675, 0x0675, 0x0675}, /* U+0675  */
-  {0x0676, 0x0676, 0x0676, 0x0676}, /* U+0676  */
-  {0x0677, 0x0677, 0x0677, 0xFBDD}, /* U+0677 ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM */
-  {0x0678, 0x0678, 0x0678, 0x0678}, /* U+0678  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+064B  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+064C  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+064D  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+064E  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+064F  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0650  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0651  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0652  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0653  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0654  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0655  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0656  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0657  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0658  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0659  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+065A  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+065B  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+065C  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+065D  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+065E  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+065F  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0660  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0661  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0662  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0663  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0664  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0665  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0666  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0667  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0668  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0669  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+066A  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+066B  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+066C  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+066D  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+066E  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+066F  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0670  */
+  {0x0000, 0x0000, 0xFB51, 0xFB50}, /* U+0671 ARABIC LETTER ALEF WASLA */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0672  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0673  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0674  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0675  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0676  */
+  {0x0000, 0x0000, 0x0000, 0xFBDD}, /* U+0677 ARABIC LETTER U WITH HAMZA ABOVE ISOLATED FORM */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0678  */
   {0xFB68, 0xFB69, 0xFB67, 0xFB66}, /* U+0679 ARABIC LETTER TTEH */
   {0xFB60, 0xFB61, 0xFB5F, 0xFB5E}, /* U+067A ARABIC LETTER TTEHEH */
   {0xFB54, 0xFB55, 0xFB53, 0xFB52}, /* U+067B ARABIC LETTER BEEH */
-  {0x067C, 0x067C, 0x067C, 0x067C}, /* U+067C  */
-  {0x067D, 0x067D, 0x067D, 0x067D}, /* U+067D  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+067C  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+067D  */
   {0xFB58, 0xFB59, 0xFB57, 0xFB56}, /* U+067E ARABIC LETTER PEH */
   {0xFB64, 0xFB65, 0xFB63, 0xFB62}, /* U+067F ARABIC LETTER TEHEH */
   {0xFB5C, 0xFB5D, 0xFB5B, 0xFB5A}, /* U+0680 ARABIC LETTER BEHEH */
-  {0x0681, 0x0681, 0x0681, 0x0681}, /* U+0681  */
-  {0x0682, 0x0682, 0x0682, 0x0682}, /* U+0682  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0681  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0682  */
   {0xFB78, 0xFB79, 0xFB77, 0xFB76}, /* U+0683 ARABIC LETTER NYEH */
   {0xFB74, 0xFB75, 0xFB73, 0xFB72}, /* U+0684 ARABIC LETTER DYEH */
-  {0x0685, 0x0685, 0x0685, 0x0685}, /* U+0685  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0685  */
   {0xFB7C, 0xFB7D, 0xFB7B, 0xFB7A}, /* U+0686 ARABIC LETTER TCHEH */
   {0xFB80, 0xFB81, 0xFB7F, 0xFB7E}, /* U+0687 ARABIC LETTER TCHEHEH */
-  {0x0688, 0x0688, 0xFB89, 0xFB88}, /* U+0688 ARABIC LETTER DDAL */
-  {0x0689, 0x0689, 0x0689, 0x0689}, /* U+0689  */
-  {0x068A, 0x068A, 0x068A, 0x068A}, /* U+068A  */
-  {0x068B, 0x068B, 0x068B, 0x068B}, /* U+068B  */
-  {0x068C, 0x068C, 0xFB85, 0xFB84}, /* U+068C ARABIC LETTER DAHAL */
-  {0x068D, 0x068D, 0xFB83, 0xFB82}, /* U+068D ARABIC LETTER DDAHAL */
-  {0x068E, 0x068E, 0xFB87, 0xFB86}, /* U+068E ARABIC LETTER DUL */
-  {0x068F, 0x068F, 0x068F, 0x068F}, /* U+068F  */
-  {0x0690, 0x0690, 0x0690, 0x0690}, /* U+0690  */
-  {0x0691, 0x0691, 0xFB8D, 0xFB8C}, /* U+0691 ARABIC LETTER RREH */
-  {0x0692, 0x0692, 0x0692, 0x0692}, /* U+0692  */
-  {0x0693, 0x0693, 0x0693, 0x0693}, /* U+0693  */
-  {0x0694, 0x0694, 0x0694, 0x0694}, /* U+0694  */
-  {0x0695, 0x0695, 0x0695, 0x0695}, /* U+0695  */
-  {0x0696, 0x0696, 0x0696, 0x0696}, /* U+0696  */
-  {0x0697, 0x0697, 0x0697, 0x0697}, /* U+0697  */
-  {0x0698, 0x0698, 0xFB8B, 0xFB8A}, /* U+0698 ARABIC LETTER JEH */
-  {0x0699, 0x0699, 0x0699, 0x0699}, /* U+0699  */
-  {0x069A, 0x069A, 0x069A, 0x069A}, /* U+069A  */
-  {0x069B, 0x069B, 0x069B, 0x069B}, /* U+069B  */
-  {0x069C, 0x069C, 0x069C, 0x069C}, /* U+069C  */
-  {0x069D, 0x069D, 0x069D, 0x069D}, /* U+069D  */
-  {0x069E, 0x069E, 0x069E, 0x069E}, /* U+069E  */
-  {0x069F, 0x069F, 0x069F, 0x069F}, /* U+069F  */
-  {0x06A0, 0x06A0, 0x06A0, 0x06A0}, /* U+06A0  */
-  {0x06A1, 0x06A1, 0x06A1, 0x06A1}, /* U+06A1  */
-  {0x06A2, 0x06A2, 0x06A2, 0x06A2}, /* U+06A2  */
-  {0x06A3, 0x06A3, 0x06A3, 0x06A3}, /* U+06A3  */
+  {0x0000, 0x0000, 0xFB89, 0xFB88}, /* U+0688 ARABIC LETTER DDAL */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0689  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+068A  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+068B  */
+  {0x0000, 0x0000, 0xFB85, 0xFB84}, /* U+068C ARABIC LETTER DAHAL */
+  {0x0000, 0x0000, 0xFB83, 0xFB82}, /* U+068D ARABIC LETTER DDAHAL */
+  {0x0000, 0x0000, 0xFB87, 0xFB86}, /* U+068E ARABIC LETTER DUL */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+068F  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0690  */
+  {0x0000, 0x0000, 0xFB8D, 0xFB8C}, /* U+0691 ARABIC LETTER RREH */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0692  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0693  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0694  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0695  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0696  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0697  */
+  {0x0000, 0x0000, 0xFB8B, 0xFB8A}, /* U+0698 ARABIC LETTER JEH */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+0699  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+069A  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+069B  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+069C  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+069D  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+069E  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+069F  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06A0  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06A1  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06A2  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06A3  */
   {0xFB6C, 0xFB6D, 0xFB6B, 0xFB6A}, /* U+06A4 ARABIC LETTER VEH */
-  {0x06A5, 0x06A5, 0x06A5, 0x06A5}, /* U+06A5  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06A5  */
   {0xFB70, 0xFB71, 0xFB6F, 0xFB6E}, /* U+06A6 ARABIC LETTER PEHEH */
-  {0x06A7, 0x06A7, 0x06A7, 0x06A7}, /* U+06A7  */
-  {0x06A8, 0x06A8, 0x06A8, 0x06A8}, /* U+06A8  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06A7  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06A8  */
   {0xFB90, 0xFB91, 0xFB8F, 0xFB8E}, /* U+06A9 ARABIC LETTER KEHEH */
-  {0x06AA, 0x06AA, 0x06AA, 0x06AA}, /* U+06AA  */
-  {0x06AB, 0x06AB, 0x06AB, 0x06AB}, /* U+06AB  */
-  {0x06AC, 0x06AC, 0x06AC, 0x06AC}, /* U+06AC  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06AA  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06AB  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06AC  */
   {0xFBD5, 0xFBD6, 0xFBD4, 0xFBD3}, /* U+06AD ARABIC LETTER NG */
-  {0x06AE, 0x06AE, 0x06AE, 0x06AE}, /* U+06AE  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06AE  */
   {0xFB94, 0xFB95, 0xFB93, 0xFB92}, /* U+06AF ARABIC LETTER GAF */
-  {0x06B0, 0x06B0, 0x06B0, 0x06B0}, /* U+06B0  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06B0  */
   {0xFB9C, 0xFB9D, 0xFB9B, 0xFB9A}, /* U+06B1 ARABIC LETTER NGOEH */
-  {0x06B2, 0x06B2, 0x06B2, 0x06B2}, /* U+06B2  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06B2  */
   {0xFB98, 0xFB99, 0xFB97, 0xFB96}, /* U+06B3 ARABIC LETTER GUEH */
-  {0x06B4, 0x06B4, 0x06B4, 0x06B4}, /* U+06B4  */
-  {0x06B5, 0x06B5, 0x06B5, 0x06B5}, /* U+06B5  */
-  {0x06B6, 0x06B6, 0x06B6, 0x06B6}, /* U+06B6  */
-  {0x06B7, 0x06B7, 0x06B7, 0x06B7}, /* U+06B7  */
-  {0x06B8, 0x06B8, 0x06B8, 0x06B8}, /* U+06B8  */
-  {0x06B9, 0x06B9, 0x06B9, 0x06B9}, /* U+06B9  */
-  {0x06BA, 0x06BA, 0xFB9F, 0xFB9E}, /* U+06BA ARABIC LETTER NOON GHUNNA */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06B4  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06B5  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06B6  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06B7  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06B8  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06B9  */
+  {0x0000, 0x0000, 0xFB9F, 0xFB9E}, /* U+06BA ARABIC LETTER NOON GHUNNA */
   {0xFBA2, 0xFBA3, 0xFBA1, 0xFBA0}, /* U+06BB ARABIC LETTER RNOON */
-  {0x06BC, 0x06BC, 0x06BC, 0x06BC}, /* U+06BC  */
-  {0x06BD, 0x06BD, 0x06BD, 0x06BD}, /* U+06BD  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06BC  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06BD  */
   {0xFBAC, 0xFBAD, 0xFBAB, 0xFBAA}, /* U+06BE ARABIC LETTER HEH DOACHASHMEE */
-  {0x06BF, 0x06BF, 0x06BF, 0x06BF}, /* U+06BF  */
-  {0x06C0, 0x06C0, 0xFBA5, 0xFBA4}, /* U+06C0 ARABIC LETTER HEH WITH YEH ABOVE */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06BF  */
+  {0x0000, 0x0000, 0xFBA5, 0xFBA4}, /* U+06C0 ARABIC LETTER HEH WITH YEH ABOVE */
   {0xFBA8, 0xFBA9, 0xFBA7, 0xFBA6}, /* U+06C1 ARABIC LETTER HEH GOAL */
-  {0x06C2, 0x06C2, 0x06C2, 0x06C2}, /* U+06C2  */
-  {0x06C3, 0x06C3, 0x06C3, 0x06C3}, /* U+06C3  */
-  {0x06C4, 0x06C4, 0x06C4, 0x06C4}, /* U+06C4  */
-  {0x06C5, 0x06C5, 0xFBE1, 0xFBE0}, /* U+06C5 ARABIC LETTER KIRGHIZ OE */
-  {0x06C6, 0x06C6, 0xFBDA, 0xFBD9}, /* U+06C6 ARABIC LETTER OE */
-  {0x06C7, 0x06C7, 0xFBD8, 0xFBD7}, /* U+06C7 ARABIC LETTER U */
-  {0x06C8, 0x06C8, 0xFBDC, 0xFBDB}, /* U+06C8 ARABIC LETTER YU */
-  {0x06C9, 0x06C9, 0xFBE3, 0xFBE2}, /* U+06C9 ARABIC LETTER KIRGHIZ YU */
-  {0x06CA, 0x06CA, 0x06CA, 0x06CA}, /* U+06CA  */
-  {0x06CB, 0x06CB, 0xFBDF, 0xFBDE}, /* U+06CB ARABIC LETTER VE */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06C2  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06C3  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06C4  */
+  {0x0000, 0x0000, 0xFBE1, 0xFBE0}, /* U+06C5 ARABIC LETTER KIRGHIZ OE */
+  {0x0000, 0x0000, 0xFBDA, 0xFBD9}, /* U+06C6 ARABIC LETTER OE */
+  {0x0000, 0x0000, 0xFBD8, 0xFBD7}, /* U+06C7 ARABIC LETTER U */
+  {0x0000, 0x0000, 0xFBDC, 0xFBDB}, /* U+06C8 ARABIC LETTER YU */
+  {0x0000, 0x0000, 0xFBE3, 0xFBE2}, /* U+06C9 ARABIC LETTER KIRGHIZ YU */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06CA  */
+  {0x0000, 0x0000, 0xFBDF, 0xFBDE}, /* U+06CB ARABIC LETTER VE */
   {0xFBFE, 0xFBFF, 0xFBFD, 0xFBFC}, /* U+06CC ARABIC LETTER FARSI YEH */
-  {0x06CD, 0x06CD, 0x06CD, 0x06CD}, /* U+06CD  */
-  {0x06CE, 0x06CE, 0x06CE, 0x06CE}, /* U+06CE  */
-  {0x06CF, 0x06CF, 0x06CF, 0x06CF}, /* U+06CF  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06CD  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06CE  */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06CF  */
   {0xFBE6, 0xFBE7, 0xFBE5, 0xFBE4}, /* U+06D0 ARABIC LETTER E */
-  {0x06D1, 0x06D1, 0x06D1, 0x06D1}, /* U+06D1  */
-  {0x06D2, 0x06D2, 0xFBAF, 0xFBAE}, /* U+06D2 ARABIC LETTER YEH BARREE */
-  {0x06D3, 0x06D3, 0xFBB1, 0xFBB0}, /* U+06D3 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE */
+  {0x0000, 0x0000, 0x0000, 0x0000}, /* U+06D1  */
+  {0x0000, 0x0000, 0xFBAF, 0xFBAE}, /* U+06D2 ARABIC LETTER YEH BARREE */
+  {0x0000, 0x0000, 0xFBB1, 0xFBB0}, /* U+06D3 ARABIC LETTER YEH BARREE WITH HAMZA ABOVE */
 };
 
 #define SHAPING_TABLE_FIRST	0x0621
 #define SHAPING_TABLE_LAST	0x06D3
 
 
 static const struct ligature_set_t {
  uint16_t first;
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-arabic.cc
@@ -23,17 +23,16 @@
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-ot-shape-complex-private.hh"
 #include "hb-ot-shape-private.hh"
 
 
-
 /* buffer var allocations */
 #define arabic_shaping_action() complex_var_u8_0() /* arabic shaping action */
 
 
 /*
  * Bits used in the joining tables
  */
 enum {
@@ -76,34 +75,16 @@ static unsigned int get_joining_type (hb
   if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x200C, 0x200D))) {
     return u == 0x200C ? JOINING_TYPE_U : JOINING_TYPE_C;
   }
 
   return (FLAG(gen_cat) & (FLAG(HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) | FLAG(HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | FLAG(HB_UNICODE_GENERAL_CATEGORY_FORMAT))) ?
 	 JOINING_TYPE_T : JOINING_TYPE_U;
 }
 
-static hb_codepoint_t get_arabic_shape (hb_codepoint_t u, unsigned int shape)
-{
-  if (likely (hb_in_range<hb_codepoint_t> (u, SHAPING_TABLE_FIRST, SHAPING_TABLE_LAST)) && shape < 4)
-    return shaping_table[u - SHAPING_TABLE_FIRST][shape];
-  return u;
-}
-
-static uint16_t get_ligature (hb_codepoint_t first, hb_codepoint_t second)
-{
-  if (unlikely (!second)) return 0;
-  for (unsigned i = 0; i < ARRAY_LENGTH (ligature_table); i++)
-    if (ligature_table[i].first == first)
-      for (unsigned j = 0; j < ARRAY_LENGTH (ligature_table[i].ligatures); j++)
-	if (ligature_table[i].ligatures[j].second == second)
-	  return ligature_table[i].ligatures[j].ligature;
-  return 0;
-}
-
 static const hb_tag_t arabic_features[] =
 {
   HB_TAG('i','n','i','t'),
   HB_TAG('m','e','d','i'),
   HB_TAG('f','i','n','a'),
   HB_TAG('i','s','o','l'),
   /* Syriac */
   HB_TAG('m','e','d','2'),
@@ -156,16 +137,20 @@ static const struct arabic_state_table_e
   /* State 5: prev was FIN2/FIN3 ALAPH, not willing to join. */
   { {NONE,NONE,0}, {ISOL,ISOL,1}, {ISOL,ISOL,2}, {ISOL,FIN2,5}, {ISOL,ISOL,6}, },
 
   /* State 6: prev was DALATH/RISH, not willing to join. */
   { {NONE,NONE,0}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN3,5}, {NONE,ISOL,6}, }
 };
 
 
+static void
+arabic_fallback_shape (const hb_ot_shape_plan_t *plan,
+		       hb_font_t *font,
+		       hb_buffer_t *buffer);
 
 static void
 collect_features_arabic (hb_ot_shape_planner_t *plan)
 {
   hb_ot_map_builder_t *map = &plan->map;
 
   /* For Language forms (in ArabicOT speak), we do the iso/fina/medi/init together,
    * then rlig and calt each in their own stage.  This makes IranNastaliq's ALLAH
@@ -178,175 +163,175 @@ collect_features_arabic (hb_ot_shape_pla
    */
 
   map->add_bool_feature (HB_TAG('c','c','m','p'));
   map->add_bool_feature (HB_TAG('l','o','c','l'));
 
   map->add_gsub_pause (NULL);
 
   for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++)
-    map->add_bool_feature (arabic_features[i], false);
+    map->add_bool_feature (arabic_features[i], false, i < 4); /* The first four features have fallback. */
 
   map->add_gsub_pause (NULL);
 
-  map->add_bool_feature (HB_TAG('r','l','i','g'));
-  map->add_gsub_pause (NULL);
+  map->add_bool_feature (HB_TAG('r','l','i','g'), true, true);
+  map->add_gsub_pause (arabic_fallback_shape);
 
   map->add_bool_feature (HB_TAG('c','a','l','t'));
   map->add_gsub_pause (NULL);
 
   /* ArabicOT spec enables 'cswh' for Arabic where as for basic shaper it's disabled by default. */
   map->add_bool_feature (HB_TAG('c','s','w','h'));
 }
 
+#include "hb-ot-shape-complex-arabic-fallback.hh"
+
 struct arabic_shape_plan_t
 {
   ASSERT_POD ();
 
-  bool do_fallback;
   /* The "+ 1" in the next array is to accommodate for the "NONE" command,
    * which is not an OpenType feature, but this simplifies the code by not
    * having to do a "if (... < NONE) ..." and just rely on the fact that
    * mask_array[NONE] == 0. */
   hb_mask_t mask_array[ARABIC_NUM_FEATURES + 1];
+
+  bool do_fallback;
+  arabic_fallback_plan_t *fallback_plan;
 };
 
 static void *
 data_create_arabic (const hb_ot_shape_plan_t *plan)
 {
   arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) calloc (1, sizeof (arabic_shape_plan_t));
   if (unlikely (!arabic_plan))
     return NULL;
 
-  hb_mask_t total_masks = 0;
+  arabic_plan->do_fallback = plan->props.script == HB_SCRIPT_ARABIC;
   for (unsigned int i = 0; i < ARABIC_NUM_FEATURES; i++) {
     arabic_plan->mask_array[i] = plan->map.get_1_mask (arabic_features[i]);
-    total_masks |= arabic_plan->mask_array[i];
+    if (i < 4)
+      arabic_plan->do_fallback = arabic_plan->do_fallback && plan->map.needs_fallback (arabic_features[i]);
   }
 
-  /* Pitfalls:
-   * - This path fires if user force-set init/medi/fina/isol off,
-   * - If font does not declare script 'arab', well, what to do?
-   *   Most probably it's safe to assume that init/medi/fina/isol
-   *   still mean Arabic shaping, although they do not have to.
-   */
-  arabic_plan->do_fallback = 0 == total_masks;
-
   return arabic_plan;
 }
 
 static void
 data_destroy_arabic (void *data)
 {
-  free (data);
-}
-
-static void
-arabic_fallback_shape (hb_font_t *font, hb_buffer_t *buffer)
-{
-  /* Only Arabic has presentation forms encoded in Unicode. */
-  if (buffer->props.script != HB_SCRIPT_ARABIC)
-    return;
-
-  unsigned int count = buffer->len;
-  hb_codepoint_t glyph;
+  arabic_shape_plan_t *arabic_plan = (arabic_shape_plan_t *) data;
 
-  /* Shape to presentation forms */
-  for (unsigned int i = 0; i < count; i++) {
-    hb_codepoint_t u = buffer->info[i].codepoint;
-    hb_codepoint_t shaped = get_arabic_shape (u, buffer->info[i].arabic_shaping_action());
-    if (shaped != u && font->get_glyph (shaped, 0, &glyph))
-      buffer->info[i].codepoint = shaped;
-  }
+  arabic_fallback_plan_destroy (arabic_plan->fallback_plan);
 
-  /* Mandatory ligatures */
-  buffer->clear_output ();
-  for (buffer->idx = 0; buffer->idx + 1 < count;) {
-    hb_codepoint_t ligature = get_ligature (buffer->cur().codepoint,
-					    buffer->cur(+1).codepoint);
-    if (likely (!ligature) || !(font->get_glyph (ligature, 0, &glyph))) {
-      buffer->next_glyph ();
-      continue;
-    }
-
-    buffer->replace_glyphs (2, 1, &ligature);
-
-    /* Technically speaking we can skip marks and stuff, like the GSUB path does.
-     * But who cares, we're in fallback! */
-  }
-  for (; buffer->idx < count;)
-      buffer->next_glyph ();
-  buffer->swap_buffers ();
+  free (data);
 }
 
 static void
 arabic_joining (hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
-  unsigned int prev = 0, state = 0;
+  unsigned int prev = (unsigned int) -1, state = 0;
 
   HB_BUFFER_ALLOCATE_VAR (buffer, arabic_shaping_action);
 
+  /* Check pre-context */
+  for (unsigned int i = 0; i < buffer->context_len[0]; i++)
+  {
+    unsigned int this_type = get_joining_type (buffer->context[0][i], buffer->unicode->general_category (buffer->context[0][i]));
+
+    if (unlikely (this_type == JOINING_TYPE_T))
+      continue;
+
+    const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
+    state = entry->next_state;
+    break;
+  }
+
   for (unsigned int i = 0; i < count; i++)
   {
     unsigned int this_type = get_joining_type (buffer->info[i].codepoint, _hb_glyph_info_get_general_category (&buffer->info[i]));
 
     if (unlikely (this_type == JOINING_TYPE_T)) {
       buffer->info[i].arabic_shaping_action() = NONE;
       continue;
     }
 
     const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
 
-    if (entry->prev_action != NONE)
+    if (entry->prev_action != NONE && prev != (unsigned int) -1)
       buffer->info[prev].arabic_shaping_action() = entry->prev_action;
 
     buffer->info[i].arabic_shaping_action() = entry->curr_action;
 
     prev = i;
     state = entry->next_state;
   }
 
-  HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
-}
+  for (unsigned int i = 0; i < buffer->context_len[1]; i++)
+  {
+    unsigned int this_type = get_joining_type (buffer->context[1][i], buffer->unicode->general_category (buffer->context[0][i]));
+
+    if (unlikely (this_type == JOINING_TYPE_T))
+      continue;
 
-static void
-preprocess_text_arabic (const hb_ot_shape_plan_t *plan,
-			hb_buffer_t              *buffer,
-			hb_font_t                *font)
-{
-  const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
+    const arabic_state_table_entry *entry = &arabic_state_table[state][this_type];
+    if (entry->prev_action != NONE && prev != (unsigned int) -1)
+      buffer->info[prev].arabic_shaping_action() = entry->prev_action;
+    break;
+  }
 
-  if (unlikely (arabic_plan->do_fallback))
-  {
-    arabic_joining (buffer);
-    arabic_fallback_shape (font, buffer);
-  }
+
+  HB_BUFFER_DEALLOCATE_VAR (buffer, arabic_shaping_action);
 }
 
 static void
 setup_masks_arabic (const hb_ot_shape_plan_t *plan,
 		    hb_buffer_t              *buffer,
 		    hb_font_t                *font)
 {
   const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
 
-  if (likely (!arabic_plan->do_fallback))
+  arabic_joining (buffer);
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    buffer->info[i].mask |= arabic_plan->mask_array[buffer->info[i].arabic_shaping_action()];
+}
+
+
+static void
+arabic_fallback_shape (const hb_ot_shape_plan_t *plan,
+		       hb_font_t *font,
+		       hb_buffer_t *buffer)
+{
+  const arabic_shape_plan_t *arabic_plan = (const arabic_shape_plan_t *) plan->data;
+
+  if (!arabic_plan->do_fallback)
+    return;
+
+retry:
+  arabic_fallback_plan_t *fallback_plan = (arabic_fallback_plan_t *) hb_atomic_ptr_get (&arabic_plan->fallback_plan);
+  if (unlikely (!fallback_plan))
   {
-    arabic_joining (buffer);
-    unsigned int count = buffer->len;
-    for (unsigned int i = 0; i < count; i++)
-      buffer->info[i].mask |= arabic_plan->mask_array[buffer->info[i].arabic_shaping_action()];
+    /* This sucks.  We need a font to build the fallback plan... */
+    fallback_plan = arabic_fallback_plan_create (plan, font);
+    if (unlikely (!hb_atomic_ptr_cmpexch (&(const_cast<arabic_shape_plan_t *> (arabic_plan))->fallback_plan, NULL, fallback_plan))) {
+      arabic_fallback_plan_destroy (fallback_plan);
+      goto retry;
+    }
   }
+
+  arabic_fallback_plan_shape (fallback_plan, font, buffer);
 }
 
+
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_arabic =
 {
   "arabic",
   collect_features_arabic,
   NULL, /* override_features */
   data_create_arabic,
   data_destroy_arabic,
-  preprocess_text_arabic,
+  NULL, /* preprocess_text_arabic */
   NULL, /* normalization_preference */
   setup_masks_arabic,
   true, /* zero_width_attached_marks */
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.hh
@@ -27,605 +27,944 @@
  */
 
 #ifndef HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
 #define HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH
 
 #include "hb-private.hh"
 
 
-#line 36 "hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 36 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
 static const unsigned char _indic_syllable_machine_trans_keys[] = {
-	1u, 16u, 13u, 13u, 4u, 14u, 5u, 7u, 7u, 7u, 5u, 7u, 5u, 7u, 7u, 7u, 
+	1u, 16u, 13u, 13u, 5u, 7u, 5u, 7u, 7u, 7u, 5u, 7u, 5u, 7u, 7u, 7u, 
 	5u, 7u, 5u, 7u, 7u, 7u, 5u, 7u, 5u, 7u, 7u, 7u, 4u, 4u, 6u, 6u, 
 	16u, 16u, 4u, 7u, 6u, 6u, 16u, 16u, 4u, 7u, 6u, 6u, 16u, 16u, 4u, 7u, 
 	6u, 6u, 16u, 16u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 
-	4u, 14u, 4u, 14u, 4u, 14u, 1u, 16u, 13u, 13u, 4u, 14u, 5u, 7u, 7u, 7u, 
+	4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 1u, 16u, 13u, 13u, 5u, 7u, 5u, 7u, 
+	7u, 7u, 5u, 7u, 5u, 7u, 7u, 7u, 5u, 7u, 5u, 7u, 7u, 7u, 5u, 7u, 
+	5u, 7u, 7u, 7u, 4u, 4u, 6u, 6u, 16u, 16u, 4u, 7u, 6u, 6u, 16u, 16u, 
+	4u, 7u, 6u, 6u, 16u, 16u, 4u, 7u, 6u, 6u, 16u, 16u, 4u, 14u, 4u, 14u, 
+	4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 
+	1u, 16u, 13u, 13u, 5u, 7u, 5u, 7u, 7u, 7u, 5u, 7u, 5u, 7u, 7u, 7u, 
+	5u, 7u, 5u, 7u, 7u, 7u, 5u, 7u, 5u, 7u, 7u, 7u, 4u, 4u, 6u, 6u, 
+	16u, 16u, 4u, 7u, 6u, 6u, 16u, 16u, 4u, 7u, 6u, 6u, 16u, 16u, 4u, 7u, 
+	6u, 6u, 16u, 16u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 
+	4u, 14u, 4u, 14u, 4u, 14u, 1u, 16u, 13u, 13u, 5u, 7u, 5u, 7u, 7u, 7u, 
 	5u, 7u, 5u, 7u, 7u, 7u, 5u, 7u, 5u, 7u, 7u, 7u, 5u, 7u, 5u, 7u, 
 	7u, 7u, 4u, 4u, 6u, 6u, 16u, 16u, 4u, 7u, 6u, 6u, 16u, 16u, 4u, 7u, 
 	6u, 6u, 16u, 16u, 4u, 7u, 6u, 6u, 16u, 16u, 4u, 14u, 4u, 14u, 4u, 14u, 
-	4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 1u, 16u, 13u, 13u, 
-	4u, 14u, 5u, 7u, 7u, 7u, 5u, 7u, 5u, 7u, 7u, 7u, 5u, 7u, 5u, 7u, 
-	7u, 7u, 5u, 7u, 5u, 7u, 7u, 7u, 4u, 4u, 6u, 6u, 16u, 16u, 4u, 7u, 
-	6u, 6u, 16u, 16u, 4u, 7u, 6u, 6u, 16u, 16u, 4u, 7u, 6u, 6u, 16u, 16u, 
-	4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 
-	4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 
-	4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 
-	4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 8u, 14u, 5u, 14u, 
-	9u, 14u, 9u, 9u, 3u, 13u, 3u, 9u, 9u, 9u, 3u, 9u, 6u, 14u, 3u, 14u, 
-	4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 
-	3u, 14u, 4u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 
-	3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 
-	3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 3u, 14u, 4u, 14u, 
-	1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 
-	1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 
-	5u, 14u, 8u, 14u, 5u, 14u, 9u, 14u, 9u, 9u, 3u, 13u, 3u, 9u, 9u, 9u, 
-	3u, 9u, 6u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 
-	3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 1u, 16u, 
-	1u, 16u, 1u, 16u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 1u, 16u, 
-	1u, 16u, 1u, 16u, 3u, 14u, 1u, 16u, 1u, 16u, 4u, 14u, 1u, 16u, 3u, 14u, 
+	4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 4u, 14u, 1u, 16u, 
 	3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 
 	3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 
-	3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 8u, 14u, 5u, 14u, 9u, 14u, 9u, 9u, 
-	3u, 13u, 3u, 9u, 9u, 9u, 3u, 9u, 6u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 
+	3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 8u, 14u, 5u, 9u, 9u, 9u, 9u, 9u, 
+	3u, 13u, 3u, 9u, 8u, 9u, 3u, 9u, 3u, 13u, 3u, 14u, 3u, 14u, 4u, 14u, 
+	5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 
+	4u, 14u, 6u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 
+	1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 
+	1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 
+	1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 
+	1u, 16u, 3u, 14u, 3u, 14u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 
+	3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 
+	3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 8u, 14u, 
+	5u, 9u, 9u, 9u, 9u, 9u, 3u, 13u, 3u, 9u, 8u, 9u, 3u, 9u, 3u, 13u, 
+	3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 
+	4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 6u, 14u, 3u, 14u, 1u, 16u, 3u, 14u, 
+	3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 
+	1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 
+	1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 
+	1u, 16u, 1u, 16u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 
+	3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 
+	3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 
+	8u, 14u, 5u, 9u, 9u, 9u, 9u, 9u, 3u, 13u, 3u, 9u, 8u, 9u, 3u, 9u, 
+	3u, 13u, 3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 
+	3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 6u, 14u, 3u, 14u, 1u, 16u, 
+	3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 
+	3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 
+	1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 
+	1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 4u, 14u, 3u, 14u, 4u, 14u, 3u, 14u, 
+	3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 
+	3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 4u, 14u, 1u, 16u, 3u, 14u, 
+	3u, 14u, 4u, 14u, 5u, 14u, 8u, 14u, 5u, 9u, 9u, 9u, 9u, 9u, 3u, 13u, 
+	3u, 9u, 8u, 9u, 3u, 9u, 3u, 13u, 3u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 
 	3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 5u, 14u, 3u, 14u, 4u, 14u, 
-	1u, 16u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 1u, 16u, 1u, 16u, 
-	1u, 16u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 1u, 16u, 1u, 16u, 
-	1u, 16u, 3u, 14u, 1u, 16u, 3u, 14u, 1u, 16u, 0
+	6u, 14u, 3u, 14u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 
+	1u, 16u, 1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 
+	1u, 16u, 3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 
+	3u, 14u, 3u, 14u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 1u, 16u, 3u, 14u, 
+	1u, 16u, 3u, 14u, 1u, 16u, 0
 };
 
 static const char _indic_syllable_machine_key_spans[] = {
-	16, 1, 11, 3, 1, 3, 3, 1, 
+	16, 1, 3, 3, 1, 3, 3, 1, 
 	3, 3, 1, 3, 3, 1, 1, 1, 
 	1, 4, 1, 1, 4, 1, 1, 4, 
 	1, 1, 11, 11, 11, 11, 11, 11, 
-	11, 11, 11, 16, 1, 11, 3, 1, 
+	11, 11, 11, 11, 16, 1, 3, 3, 
+	1, 3, 3, 1, 3, 3, 1, 3, 
+	3, 1, 1, 1, 1, 4, 1, 1, 
+	4, 1, 1, 4, 1, 1, 11, 11, 
+	11, 11, 11, 11, 11, 11, 11, 11, 
+	16, 1, 3, 3, 1, 3, 3, 1, 
+	3, 3, 1, 3, 3, 1, 1, 1, 
+	1, 4, 1, 1, 4, 1, 1, 4, 
+	1, 1, 11, 11, 11, 11, 11, 11, 
+	11, 11, 11, 16, 1, 3, 3, 1, 
 	3, 3, 1, 3, 3, 1, 3, 3, 
 	1, 1, 1, 1, 4, 1, 1, 4, 
 	1, 1, 4, 1, 1, 11, 11, 11, 
-	11, 11, 11, 11, 11, 11, 16, 1, 
-	11, 3, 1, 3, 3, 1, 3, 3, 
-	1, 3, 3, 1, 1, 1, 1, 4, 
-	1, 1, 4, 1, 1, 4, 1, 1, 
-	11, 11, 11, 11, 11, 11, 11, 11, 
-	11, 16, 12, 12, 11, 16, 12, 12, 
-	11, 16, 12, 12, 11, 16, 12, 12, 
-	11, 16, 12, 12, 11, 10, 7, 10, 
-	6, 1, 11, 7, 1, 7, 9, 12, 
-	11, 10, 12, 11, 10, 12, 11, 10, 
-	12, 11, 11, 16, 12, 16, 16, 16, 
-	12, 16, 16, 16, 12, 16, 16, 16, 
-	12, 16, 16, 16, 12, 12, 12, 11, 
-	16, 12, 12, 11, 16, 12, 12, 11, 
-	16, 12, 12, 11, 16, 12, 12, 11, 
-	10, 7, 10, 6, 1, 11, 7, 1, 
-	7, 9, 12, 11, 10, 12, 11, 10, 
-	12, 11, 10, 12, 11, 16, 12, 16, 
-	16, 16, 12, 16, 16, 16, 12, 16, 
-	16, 16, 12, 16, 16, 11, 16, 12, 
+	11, 11, 11, 11, 11, 11, 11, 16, 
 	12, 12, 11, 16, 12, 12, 11, 16, 
 	12, 12, 11, 16, 12, 12, 11, 16, 
-	12, 12, 11, 10, 7, 10, 6, 1, 
-	11, 7, 1, 7, 9, 12, 11, 10, 
+	12, 12, 11, 10, 7, 5, 1, 1, 
+	11, 7, 2, 7, 11, 12, 12, 11, 
+	10, 12, 11, 10, 12, 11, 10, 12, 
+	11, 9, 12, 11, 16, 12, 12, 16, 
+	16, 16, 16, 16, 12, 12, 16, 16, 
+	16, 16, 16, 12, 12, 16, 16, 16, 
+	16, 16, 12, 12, 16, 16, 16, 16, 
+	16, 12, 12, 12, 12, 11, 16, 12, 
+	12, 11, 16, 12, 12, 11, 16, 12, 
+	12, 11, 16, 12, 12, 11, 10, 7, 
+	5, 1, 1, 11, 7, 2, 7, 11, 
+	12, 12, 11, 10, 12, 11, 10, 12, 
+	11, 10, 12, 11, 9, 12, 16, 12, 
+	12, 16, 16, 16, 16, 16, 12, 12, 
+	16, 16, 16, 16, 16, 12, 12, 16, 
+	16, 16, 16, 16, 12, 12, 16, 16, 
+	16, 16, 11, 16, 12, 12, 11, 16, 
+	12, 12, 11, 16, 12, 12, 11, 16, 
+	12, 12, 11, 16, 12, 12, 11, 10, 
+	7, 5, 1, 1, 11, 7, 2, 7, 
+	11, 12, 12, 11, 10, 12, 11, 10, 
+	12, 11, 10, 12, 11, 9, 12, 16, 
+	12, 12, 16, 16, 16, 16, 16, 12, 
+	12, 16, 16, 16, 16, 16, 12, 12, 
+	16, 16, 16, 16, 16, 12, 12, 16, 
+	16, 16, 16, 16, 11, 12, 11, 12, 
+	12, 11, 16, 12, 12, 11, 16, 12, 
+	12, 11, 16, 12, 12, 11, 16, 12, 
+	12, 11, 10, 7, 5, 1, 1, 11, 
+	7, 2, 7, 11, 12, 12, 11, 10, 
 	12, 11, 10, 12, 11, 10, 12, 11, 
-	16, 12, 16, 16, 16, 12, 16, 16, 
-	16, 12, 16, 16, 16, 12, 16, 16, 
-	16, 12, 16, 12, 16
+	9, 12, 16, 12, 12, 16, 16, 16, 
+	16, 16, 12, 12, 16, 16, 16, 16, 
+	16, 12, 12, 16, 16, 16, 16, 16, 
+	12, 12, 16, 16, 16, 16, 16, 12, 
+	16, 12, 16
 };
 
 static const short _indic_syllable_machine_index_offsets[] = {
-	0, 17, 19, 31, 35, 37, 41, 45, 
-	47, 51, 55, 57, 61, 65, 67, 69, 
-	71, 73, 78, 80, 82, 87, 89, 91, 
-	96, 98, 100, 112, 124, 136, 148, 160, 
-	172, 184, 196, 208, 225, 227, 239, 243, 
-	245, 249, 253, 255, 259, 263, 265, 269, 
-	273, 275, 277, 279, 281, 286, 288, 290, 
-	295, 297, 299, 304, 306, 308, 320, 332, 
-	344, 356, 368, 380, 392, 404, 416, 433, 
-	435, 447, 451, 453, 457, 461, 463, 467, 
-	471, 473, 477, 481, 483, 485, 487, 489, 
-	494, 496, 498, 503, 505, 507, 512, 514, 
-	516, 528, 540, 552, 564, 576, 588, 600, 
-	612, 624, 641, 654, 667, 679, 696, 709, 
-	722, 734, 751, 764, 777, 789, 806, 819, 
-	832, 844, 861, 874, 887, 899, 910, 918, 
-	929, 936, 938, 950, 958, 960, 968, 978, 
-	991, 1003, 1014, 1027, 1039, 1050, 1063, 1075, 
-	1086, 1099, 1111, 1123, 1140, 1153, 1170, 1187, 
-	1204, 1217, 1234, 1251, 1268, 1281, 1298, 1315, 
-	1332, 1345, 1362, 1379, 1396, 1409, 1422, 1435, 
-	1447, 1464, 1477, 1490, 1502, 1519, 1532, 1545, 
-	1557, 1574, 1587, 1600, 1612, 1629, 1642, 1655, 
-	1667, 1678, 1686, 1697, 1704, 1706, 1718, 1726, 
-	1728, 1736, 1746, 1759, 1771, 1782, 1795, 1807, 
-	1818, 1831, 1843, 1854, 1867, 1879, 1896, 1909, 
-	1926, 1943, 1960, 1973, 1990, 2007, 2024, 2037, 
-	2054, 2071, 2088, 2101, 2118, 2135, 2147, 2164, 
-	2177, 2190, 2203, 2215, 2232, 2245, 2258, 2270, 
-	2287, 2300, 2313, 2325, 2342, 2355, 2368, 2380, 
-	2397, 2410, 2423, 2435, 2446, 2454, 2465, 2472, 
-	2474, 2486, 2494, 2496, 2504, 2514, 2527, 2539, 
-	2550, 2563, 2575, 2586, 2599, 2611, 2622, 2635, 
-	2647, 2664, 2677, 2694, 2711, 2728, 2741, 2758, 
-	2775, 2792, 2805, 2822, 2839, 2856, 2869, 2886, 
-	2903, 2920, 2933, 2950, 2963
+	0, 17, 19, 23, 27, 29, 33, 37, 
+	39, 43, 47, 49, 53, 57, 59, 61, 
+	63, 65, 70, 72, 74, 79, 81, 83, 
+	88, 90, 92, 104, 116, 128, 140, 152, 
+	164, 176, 188, 200, 212, 229, 231, 235, 
+	239, 241, 245, 249, 251, 255, 259, 261, 
+	265, 269, 271, 273, 275, 277, 282, 284, 
+	286, 291, 293, 295, 300, 302, 304, 316, 
+	328, 340, 352, 364, 376, 388, 400, 412, 
+	424, 441, 443, 447, 451, 453, 457, 461, 
+	463, 467, 471, 473, 477, 481, 483, 485, 
+	487, 489, 494, 496, 498, 503, 505, 507, 
+	512, 514, 516, 528, 540, 552, 564, 576, 
+	588, 600, 612, 624, 641, 643, 647, 651, 
+	653, 657, 661, 663, 667, 671, 673, 677, 
+	681, 683, 685, 687, 689, 694, 696, 698, 
+	703, 705, 707, 712, 714, 716, 728, 740, 
+	752, 764, 776, 788, 800, 812, 824, 836, 
+	853, 866, 879, 891, 908, 921, 934, 946, 
+	963, 976, 989, 1001, 1018, 1031, 1044, 1056, 
+	1073, 1086, 1099, 1111, 1122, 1130, 1136, 1138, 
+	1140, 1152, 1160, 1163, 1171, 1183, 1196, 1209, 
+	1221, 1232, 1245, 1257, 1268, 1281, 1293, 1304, 
+	1317, 1329, 1339, 1352, 1364, 1381, 1394, 1407, 
+	1424, 1441, 1458, 1475, 1492, 1505, 1518, 1535, 
+	1552, 1569, 1586, 1603, 1616, 1629, 1646, 1663, 
+	1680, 1697, 1714, 1727, 1740, 1757, 1774, 1791, 
+	1808, 1825, 1838, 1851, 1864, 1877, 1889, 1906, 
+	1919, 1932, 1944, 1961, 1974, 1987, 1999, 2016, 
+	2029, 2042, 2054, 2071, 2084, 2097, 2109, 2120, 
+	2128, 2134, 2136, 2138, 2150, 2158, 2161, 2169, 
+	2181, 2194, 2207, 2219, 2230, 2243, 2255, 2266, 
+	2279, 2291, 2302, 2315, 2327, 2337, 2350, 2367, 
+	2380, 2393, 2410, 2427, 2444, 2461, 2478, 2491, 
+	2504, 2521, 2538, 2555, 2572, 2589, 2602, 2615, 
+	2632, 2649, 2666, 2683, 2700, 2713, 2726, 2743, 
+	2760, 2777, 2794, 2806, 2823, 2836, 2849, 2861, 
+	2878, 2891, 2904, 2916, 2933, 2946, 2959, 2971, 
+	2988, 3001, 3014, 3026, 3043, 3056, 3069, 3081, 
+	3092, 3100, 3106, 3108, 3110, 3122, 3130, 3133, 
+	3141, 3153, 3166, 3179, 3191, 3202, 3215, 3227, 
+	3238, 3251, 3263, 3274, 3287, 3299, 3309, 3322, 
+	3339, 3352, 3365, 3382, 3399, 3416, 3433, 3450, 
+	3463, 3476, 3493, 3510, 3527, 3544, 3561, 3574, 
+	3587, 3604, 3621, 3638, 3655, 3672, 3685, 3698, 
+	3715, 3732, 3749, 3766, 3783, 3795, 3808, 3820, 
+	3833, 3846, 3858, 3875, 3888, 3901, 3913, 3930, 
+	3943, 3956, 3968, 3985, 3998, 4011, 4023, 4040, 
+	4053, 4066, 4078, 4089, 4097, 4103, 4105, 4107, 
+	4119, 4127, 4130, 4138, 4150, 4163, 4176, 4188, 
+	4199, 4212, 4224, 4235, 4248, 4260, 4271, 4284, 
+	4296, 4306, 4319, 4336, 4349, 4362, 4379, 4396, 
+	4413, 4430, 4447, 4460, 4473, 4490, 4507, 4524, 
+	4541, 4558, 4571, 4584, 4601, 4618, 4635, 4652, 
+	4669, 4682, 4695, 4712, 4729, 4746, 4763, 4780, 
+	4793, 4810, 4823
 };
 
 static const short _indic_syllable_machine_indicies[] = {
 	1, 2, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 1, 
-	0, 3, 0, 4, 5, 5, 6, 0, 
-	0, 0, 0, 0, 0, 4, 0, 7, 
-	7, 6, 0, 6, 0, 8, 8, 9, 
-	0, 10, 10, 9, 0, 9, 0, 11, 
-	11, 12, 0, 13, 13, 12, 0, 12, 
-	0, 14, 14, 15, 0, 16, 16, 15, 
-	0, 15, 0, 17, 0, 18, 0, 19, 
-	0, 20, 14, 14, 15, 0, 21, 0, 
-	22, 0, 23, 11, 11, 12, 0, 24, 
-	0, 25, 0, 26, 8, 8, 9, 0, 
-	27, 0, 28, 0, 4, 5, 5, 6, 
-	0, 0, 0, 0, 0, 29, 4, 0, 
-	30, 5, 5, 6, 0, 0, 0, 0, 
-	0, 0, 30, 0, 30, 5, 5, 6, 
-	0, 0, 0, 0, 0, 31, 30, 0, 
-	32, 5, 5, 6, 0, 0, 0, 0, 
-	0, 0, 32, 0, 32, 5, 5, 6, 
-	0, 0, 0, 0, 0, 33, 32, 0, 
-	34, 5, 5, 6, 0, 0, 0, 0, 
-	0, 0, 34, 0, 34, 5, 5, 6, 
-	0, 0, 0, 0, 0, 35, 34, 0, 
-	36, 5, 5, 6, 0, 0, 0, 0, 
-	0, 0, 36, 0, 36, 5, 5, 6, 
-	0, 0, 0, 0, 0, 37, 36, 0, 
-	39, 40, 38, 38, 38, 38, 38, 38, 
-	38, 38, 38, 38, 38, 38, 38, 39, 
-	38, 41, 38, 42, 43, 43, 44, 38, 
-	38, 38, 38, 38, 38, 42, 38, 45, 
-	45, 44, 38, 44, 38, 46, 46, 47, 
-	38, 48, 48, 47, 38, 47, 38, 49, 
-	49, 50, 38, 51, 51, 50, 38, 50, 
-	38, 52, 52, 53, 38, 54, 54, 53, 
-	38, 53, 38, 55, 38, 56, 38, 57, 
-	38, 58, 52, 52, 53, 38, 59, 38, 
-	60, 38, 61, 49, 49, 50, 38, 62, 
-	38, 63, 38, 64, 46, 46, 47, 38, 
-	65, 38, 66, 38, 42, 43, 43, 44, 
-	38, 38, 38, 38, 38, 67, 42, 38, 
-	68, 43, 43, 44, 38, 38, 38, 38, 
-	38, 38, 68, 38, 68, 43, 43, 44, 
+	0, 3, 0, 4, 4, 5, 0, 6, 
+	6, 5, 0, 5, 0, 7, 7, 8, 
+	0, 9, 9, 8, 0, 8, 0, 10, 
+	10, 11, 0, 12, 12, 11, 0, 11, 
+	0, 13, 13, 14, 0, 15, 15, 14, 
+	0, 14, 0, 16, 0, 17, 0, 18, 
+	0, 19, 13, 13, 14, 0, 20, 0, 
+	21, 0, 22, 10, 10, 11, 0, 23, 
+	0, 24, 0, 25, 7, 7, 8, 0, 
+	26, 0, 27, 0, 28, 4, 4, 5, 
+	0, 0, 0, 0, 0, 0, 28, 0, 
+	28, 4, 4, 5, 0, 0, 0, 0, 
+	0, 29, 28, 0, 30, 4, 4, 5, 
+	0, 0, 0, 0, 0, 0, 30, 0, 
+	30, 4, 4, 5, 0, 0, 0, 0, 
+	0, 31, 30, 0, 32, 4, 4, 5, 
+	0, 0, 0, 0, 0, 0, 32, 0, 
+	32, 4, 4, 5, 0, 0, 0, 0, 
+	0, 33, 32, 0, 34, 4, 4, 5, 
+	0, 0, 0, 0, 0, 0, 34, 0, 
+	34, 4, 4, 5, 0, 0, 0, 0, 
+	0, 35, 34, 0, 36, 4, 4, 5, 
+	0, 0, 0, 0, 0, 0, 36, 0, 
+	36, 4, 4, 5, 0, 0, 0, 0, 
+	0, 37, 36, 0, 39, 40, 38, 38, 
+	38, 38, 38, 38, 38, 38, 38, 38, 
+	38, 38, 38, 39, 38, 41, 38, 42, 
+	42, 43, 38, 44, 44, 43, 38, 43, 
+	38, 45, 45, 46, 38, 47, 47, 46, 
+	38, 46, 38, 48, 48, 49, 38, 50, 
+	50, 49, 38, 49, 38, 51, 51, 52, 
+	38, 53, 53, 52, 38, 52, 38, 54, 
+	38, 55, 38, 56, 38, 57, 51, 51, 
+	52, 38, 58, 38, 59, 38, 60, 48, 
+	48, 49, 38, 61, 38, 62, 38, 63, 
+	45, 45, 46, 38, 64, 38, 65, 38, 
+	66, 42, 42, 43, 38, 38, 38, 38, 
+	38, 38, 66, 38, 66, 42, 42, 43, 
+	38, 38, 38, 38, 38, 67, 66, 38, 
+	68, 42, 42, 43, 38, 38, 38, 38, 
+	38, 38, 68, 38, 68, 42, 42, 43, 
 	38, 38, 38, 38, 38, 69, 68, 38, 
-	70, 43, 43, 44, 38, 38, 38, 38, 
-	38, 38, 70, 38, 70, 43, 43, 44, 
+	70, 42, 42, 43, 38, 38, 38, 38, 
+	38, 38, 70, 38, 70, 42, 42, 43, 
 	38, 38, 38, 38, 38, 71, 70, 38, 
-	72, 43, 43, 44, 38, 38, 38, 38, 
-	38, 38, 72, 38, 72, 43, 43, 44, 
+	72, 42, 42, 43, 38, 38, 38, 38, 
+	38, 38, 72, 38, 72, 42, 42, 43, 
 	38, 38, 38, 38, 38, 73, 72, 38, 
-	74, 43, 43, 44, 38, 38, 38, 38, 
-	38, 38, 74, 38, 74, 43, 43, 44, 
+	74, 42, 42, 43, 38, 38, 38, 38, 
+	38, 38, 74, 38, 74, 42, 42, 43, 
 	38, 38, 38, 38, 38, 75, 74, 38, 
 	77, 78, 76, 76, 76, 76, 76, 76, 
 	76, 76, 76, 76, 76, 76, 76, 77, 
-	76, 79, 76, 80, 81, 81, 82, 76, 
-	76, 76, 76, 76, 76, 80, 76, 83, 
-	83, 82, 76, 82, 76, 84, 84, 85, 
+	76, 79, 76, 80, 80, 81, 76, 83, 
+	83, 81, 82, 81, 82, 84, 84, 85, 
 	76, 86, 86, 85, 76, 85, 76, 87, 
 	87, 88, 76, 89, 89, 88, 76, 88, 
 	76, 90, 90, 91, 76, 92, 92, 91, 
 	76, 91, 76, 93, 76, 94, 76, 95, 
 	76, 96, 90, 90, 91, 76, 97, 76, 
 	98, 76, 99, 87, 87, 88, 76, 100, 
 	76, 101, 76, 102, 84, 84, 85, 76, 
-	103, 76, 104, 76, 80, 81, 81, 82, 
-	76, 76, 76, 76, 76, 105, 80, 76, 
-	106, 81, 81, 82, 76, 76, 76, 76, 
-	76, 76, 106, 76, 106, 81, 81, 82, 
-	76, 76, 76, 76, 76, 107, 106, 76, 
-	108, 81, 81, 82, 76, 76, 76, 76, 
-	76, 76, 108, 76, 108, 81, 81, 82, 
-	76, 76, 76, 76, 76, 109, 108, 76, 
-	110, 81, 81, 82, 76, 76, 76, 76, 
-	76, 76, 110, 76, 110, 81, 81, 82, 
-	76, 76, 76, 76, 76, 111, 110, 76, 
-	112, 81, 81, 82, 76, 76, 76, 76, 
-	76, 76, 112, 76, 112, 81, 81, 82, 
-	76, 76, 76, 76, 76, 113, 112, 76, 
+	103, 76, 104, 76, 105, 80, 80, 81, 
+	76, 76, 76, 76, 76, 76, 105, 76, 
+	105, 80, 80, 81, 76, 76, 76, 76, 
+	76, 106, 105, 76, 107, 80, 80, 81, 
+	76, 76, 76, 76, 76, 76, 107, 76, 
+	107, 80, 80, 81, 76, 76, 76, 76, 
+	76, 108, 107, 76, 109, 80, 80, 81, 
+	76, 76, 76, 76, 76, 76, 109, 76, 
+	109, 80, 80, 81, 76, 76, 76, 76, 
+	76, 110, 109, 76, 111, 80, 80, 81, 
+	76, 76, 76, 76, 76, 76, 111, 76, 
+	111, 80, 80, 81, 76, 76, 76, 76, 
+	76, 112, 111, 76, 113, 80, 80, 81, 
+	76, 76, 76, 76, 76, 76, 113, 76, 
 	115, 116, 114, 114, 114, 114, 114, 114, 
-	114, 114, 117, 117, 114, 114, 118, 119, 
-	114, 121, 122, 123, 124, 6, 125, 126, 
-	127, 120, 120, 37, 128, 120, 129, 122, 
-	124, 124, 6, 125, 126, 127, 120, 120, 
-	120, 128, 120, 122, 124, 124, 6, 125, 
-	126, 127, 120, 120, 120, 128, 120, 130, 
-	120, 120, 120, 19, 131, 120, 125, 126, 
-	120, 120, 120, 120, 132, 120, 130, 120, 
-	133, 134, 135, 136, 6, 125, 126, 127, 
-	120, 120, 35, 137, 120, 138, 134, 136, 
-	136, 6, 125, 126, 127, 120, 120, 120, 
-	137, 120, 134, 136, 136, 6, 125, 126, 
-	127, 120, 120, 120, 137, 120, 139, 120, 
-	120, 120, 19, 140, 120, 125, 126, 120, 
-	120, 120, 120, 132, 120, 139, 120, 141, 
-	142, 143, 144, 6, 125, 126, 127, 120, 
-	120, 33, 145, 120, 146, 142, 144, 144, 
-	6, 125, 126, 127, 120, 120, 120, 145, 
-	120, 142, 144, 144, 6, 125, 126, 127, 
-	120, 120, 120, 145, 120, 147, 120, 120, 
-	120, 19, 148, 120, 125, 126, 120, 120, 
-	120, 120, 132, 120, 147, 120, 149, 150, 
-	151, 152, 6, 125, 126, 127, 120, 120, 
-	31, 153, 120, 154, 150, 152, 152, 6, 
-	125, 126, 127, 120, 120, 120, 153, 120, 
-	150, 152, 152, 6, 125, 126, 127, 120, 
-	120, 120, 153, 120, 155, 120, 120, 120, 
-	19, 156, 120, 125, 126, 120, 120, 120, 
-	120, 132, 120, 155, 120, 157, 158, 159, 
-	160, 6, 125, 126, 127, 120, 120, 29, 
-	161, 120, 162, 158, 160, 160, 6, 125, 
-	126, 127, 120, 120, 120, 161, 120, 158, 
-	160, 160, 6, 125, 126, 127, 120, 120, 
-	120, 161, 120, 19, 19, 120, 125, 126, 
-	120, 120, 120, 120, 132, 120, 125, 126, 
-	120, 120, 120, 120, 132, 120, 163, 120, 
-	120, 120, 126, 120, 120, 120, 120, 132, 
-	120, 126, 120, 120, 120, 120, 132, 120, 
-	164, 120, 165, 120, 166, 120, 120, 120, 
-	126, 120, 120, 120, 3, 120, 2, 120, 
-	120, 120, 120, 120, 126, 120, 126, 120, 
-	165, 120, 120, 120, 120, 120, 126, 120, 
-	19, 120, 125, 126, 120, 120, 120, 120, 
-	132, 120, 167, 28, 168, 169, 9, 125, 
-	126, 120, 120, 120, 120, 132, 120, 28, 
-	168, 169, 9, 125, 126, 120, 120, 120, 
-	120, 132, 120, 168, 168, 9, 125, 126, 
-	120, 120, 120, 120, 132, 120, 170, 25, 
-	171, 172, 12, 125, 126, 120, 120, 120, 
-	120, 132, 120, 25, 171, 172, 12, 125, 
-	126, 120, 120, 120, 120, 132, 120, 171, 
-	171, 12, 125, 126, 120, 120, 120, 120, 
-	132, 120, 173, 22, 174, 175, 15, 125, 
-	126, 120, 120, 120, 120, 132, 120, 22, 
-	174, 175, 15, 125, 126, 120, 120, 120, 
-	120, 132, 120, 174, 174, 15, 125, 126, 
-	120, 120, 120, 120, 132, 120, 176, 19, 
-	120, 177, 120, 125, 126, 120, 120, 120, 
-	120, 132, 120, 19, 120, 177, 120, 125, 
-	126, 120, 120, 120, 120, 132, 120, 158, 
-	160, 160, 6, 125, 126, 120, 120, 120, 
-	120, 161, 120, 1, 2, 120, 120, 19, 
-	19, 120, 125, 126, 120, 120, 120, 120, 
-	132, 120, 1, 120, 157, 158, 160, 160, 
-	6, 125, 126, 127, 120, 120, 120, 161, 
-	120, 155, 120, 120, 120, 120, 120, 120, 
-	125, 126, 120, 120, 120, 120, 132, 120, 
-	155, 120, 155, 120, 120, 120, 120, 156, 
-	120, 125, 126, 120, 120, 120, 120, 132, 
-	120, 155, 120, 155, 2, 120, 120, 19, 
-	156, 120, 125, 126, 120, 120, 120, 120, 
-	132, 120, 155, 120, 149, 150, 152, 152, 
-	6, 125, 126, 127, 120, 120, 120, 153, 
-	120, 147, 120, 120, 120, 120, 120, 120, 
-	125, 126, 120, 120, 120, 120, 132, 120, 
-	147, 120, 147, 120, 120, 120, 120, 148, 
-	120, 125, 126, 120, 120, 120, 120, 132, 
-	120, 147, 120, 147, 2, 120, 120, 19, 
-	148, 120, 125, 126, 120, 120, 120, 120, 
-	132, 120, 147, 120, 141, 142, 144, 144, 
-	6, 125, 126, 127, 120, 120, 120, 145, 
-	120, 139, 120, 120, 120, 120, 120, 120, 
-	125, 126, 120, 120, 120, 120, 132, 120, 
-	139, 120, 139, 120, 120, 120, 120, 140, 
-	120, 125, 126, 120, 120, 120, 120, 132, 
-	120, 139, 120, 139, 2, 120, 120, 19, 
-	140, 120, 125, 126, 120, 120, 120, 120, 
-	132, 120, 139, 120, 133, 134, 136, 136, 
-	6, 125, 126, 127, 120, 120, 120, 137, 
-	120, 130, 120, 120, 120, 120, 120, 120, 
-	125, 126, 120, 120, 120, 120, 132, 120, 
-	130, 120, 130, 120, 120, 120, 120, 131, 
-	120, 125, 126, 120, 120, 120, 120, 132, 
-	120, 130, 120, 130, 2, 120, 120, 19, 
-	131, 120, 125, 126, 120, 120, 120, 120, 
-	132, 120, 130, 120, 121, 122, 124, 124, 
-	6, 125, 126, 127, 120, 120, 120, 128, 
-	120, 179, 180, 181, 182, 44, 183, 184, 
-	178, 178, 178, 75, 185, 178, 186, 180, 
-	187, 182, 44, 183, 184, 178, 178, 178, 
-	178, 185, 178, 180, 187, 182, 44, 183, 
-	184, 178, 178, 178, 178, 185, 178, 188, 
-	178, 178, 178, 57, 189, 178, 183, 184, 
-	178, 178, 178, 178, 190, 178, 188, 178, 
-	191, 192, 193, 194, 44, 183, 184, 178, 
-	178, 178, 73, 195, 178, 196, 192, 194, 
-	194, 44, 183, 184, 178, 178, 178, 178, 
-	195, 178, 192, 194, 194, 44, 183, 184, 
-	178, 178, 178, 178, 195, 178, 197, 178, 
-	178, 178, 57, 198, 178, 183, 184, 178, 
-	178, 178, 178, 190, 178, 197, 178, 199, 
-	200, 201, 202, 44, 183, 184, 178, 178, 
-	178, 71, 203, 178, 204, 200, 202, 202, 
-	44, 183, 184, 178, 178, 178, 178, 203, 
-	178, 200, 202, 202, 44, 183, 184, 178, 
-	178, 178, 178, 203, 178, 205, 178, 178, 
-	178, 57, 206, 178, 183, 184, 178, 178, 
-	178, 178, 190, 178, 205, 178, 207, 208, 
-	209, 210, 44, 183, 184, 178, 178, 178, 
-	69, 211, 178, 212, 208, 210, 210, 44, 
-	183, 184, 178, 178, 178, 178, 211, 178, 
-	208, 210, 210, 44, 183, 184, 178, 178, 
-	178, 178, 211, 178, 213, 178, 178, 178, 
-	57, 214, 178, 183, 184, 178, 178, 178, 
-	178, 190, 178, 213, 178, 215, 216, 217, 
-	218, 44, 183, 184, 178, 178, 178, 67, 
-	219, 178, 220, 216, 218, 218, 44, 183, 
-	184, 178, 178, 178, 178, 219, 178, 216, 
-	218, 218, 44, 183, 184, 178, 178, 178, 
-	178, 219, 178, 57, 57, 178, 183, 184, 
-	178, 178, 178, 178, 190, 178, 183, 184, 
-	178, 178, 178, 178, 190, 178, 221, 178, 
-	178, 178, 184, 178, 178, 178, 178, 190, 
-	178, 184, 178, 178, 178, 178, 190, 178, 
-	222, 178, 223, 178, 224, 178, 178, 178, 
-	184, 178, 178, 178, 41, 178, 40, 178, 
-	178, 178, 178, 178, 184, 178, 184, 178, 
-	223, 178, 178, 178, 178, 178, 184, 178, 
-	57, 178, 183, 184, 178, 178, 178, 178, 
-	190, 178, 225, 66, 226, 227, 47, 183, 
-	184, 178, 178, 178, 178, 190, 178, 66, 
-	226, 227, 47, 183, 184, 178, 178, 178, 
-	178, 190, 178, 226, 226, 47, 183, 184, 
-	178, 178, 178, 178, 190, 178, 228, 63, 
-	229, 230, 50, 183, 184, 178, 178, 178, 
-	178, 190, 178, 63, 229, 230, 50, 183, 
-	184, 178, 178, 178, 178, 190, 178, 229, 
-	229, 50, 183, 184, 178, 178, 178, 178, 
-	190, 178, 231, 60, 232, 233, 53, 183, 
-	184, 178, 178, 178, 178, 190, 178, 60, 
-	232, 233, 53, 183, 184, 178, 178, 178, 
-	178, 190, 178, 232, 232, 53, 183, 184, 
-	178, 178, 178, 178, 190, 178, 234, 57, 
-	178, 235, 178, 183, 184, 178, 178, 178, 
-	178, 190, 178, 57, 178, 235, 178, 183, 
-	184, 178, 178, 178, 178, 190, 178, 39, 
-	40, 178, 178, 57, 57, 178, 183, 184, 
-	178, 178, 178, 178, 190, 178, 39, 178, 
-	215, 216, 218, 218, 44, 183, 184, 178, 
-	178, 178, 178, 219, 178, 213, 178, 178, 
-	178, 178, 178, 178, 183, 184, 178, 178, 
-	178, 178, 190, 178, 213, 178, 213, 178, 
-	178, 178, 178, 214, 178, 183, 184, 178, 
-	178, 178, 178, 190, 178, 213, 178, 213, 
-	40, 178, 178, 57, 214, 178, 183, 184, 
-	178, 178, 178, 178, 190, 178, 213, 178, 
-	207, 208, 210, 210, 44, 183, 184, 178, 
-	178, 178, 178, 211, 178, 205, 178, 178, 
-	178, 178, 178, 178, 183, 184, 178, 178, 
-	178, 178, 190, 178, 205, 178, 205, 178, 
-	178, 178, 178, 206, 178, 183, 184, 178, 
-	178, 178, 178, 190, 178, 205, 178, 205, 
-	40, 178, 178, 57, 206, 178, 183, 184, 
-	178, 178, 178, 178, 190, 178, 205, 178, 
-	199, 200, 202, 202, 44, 183, 184, 178, 
-	178, 178, 178, 203, 178, 197, 178, 178, 
-	178, 178, 178, 178, 183, 184, 178, 178, 
-	178, 178, 190, 178, 197, 178, 197, 178, 
-	178, 178, 178, 198, 178, 183, 184, 178, 
-	178, 178, 178, 190, 178, 197, 178, 197, 
-	40, 178, 178, 57, 198, 178, 183, 184, 
-	178, 178, 178, 178, 190, 178, 197, 178, 
-	191, 192, 194, 194, 44, 183, 184, 178, 
-	178, 178, 178, 195, 178, 188, 178, 178, 
-	178, 178, 178, 178, 183, 184, 178, 178, 
-	178, 178, 190, 178, 188, 178, 188, 178, 
-	178, 178, 178, 189, 178, 183, 184, 178, 
-	178, 178, 178, 190, 178, 188, 178, 74, 
-	43, 43, 44, 178, 178, 178, 178, 178, 
-	178, 74, 178, 188, 40, 178, 178, 57, 
-	189, 178, 183, 184, 178, 178, 178, 178, 
-	190, 178, 188, 178, 179, 180, 187, 182, 
-	44, 183, 184, 178, 178, 178, 178, 185, 
-	178, 237, 238, 239, 240, 82, 241, 242, 
-	236, 236, 236, 113, 243, 236, 244, 238, 
-	240, 240, 82, 241, 242, 236, 236, 236, 
-	236, 243, 236, 238, 240, 240, 82, 241, 
-	242, 236, 236, 236, 236, 243, 236, 245, 
-	236, 236, 236, 95, 246, 236, 241, 242, 
-	236, 236, 236, 236, 247, 236, 245, 236, 
-	248, 249, 250, 251, 82, 241, 242, 236, 
-	236, 236, 111, 252, 236, 253, 249, 251, 
-	251, 82, 241, 242, 236, 236, 236, 236, 
-	252, 236, 249, 251, 251, 82, 241, 242, 
-	236, 236, 236, 236, 252, 236, 254, 236, 
-	236, 236, 95, 255, 236, 241, 242, 236, 
-	236, 236, 236, 247, 236, 254, 236, 256, 
-	257, 258, 259, 82, 241, 242, 236, 236, 
-	236, 109, 260, 236, 261, 257, 259, 259, 
-	82, 241, 242, 236, 236, 236, 236, 260, 
-	236, 257, 259, 259, 82, 241, 242, 236, 
-	236, 236, 236, 260, 236, 262, 236, 236, 
-	236, 95, 263, 236, 241, 242, 236, 236, 
-	236, 236, 247, 236, 262, 236, 264, 265, 
-	266, 267, 82, 241, 242, 236, 236, 236, 
-	107, 268, 236, 269, 265, 267, 267, 82, 
-	241, 242, 236, 236, 236, 236, 268, 236, 
-	265, 267, 267, 82, 241, 242, 236, 236, 
-	236, 236, 268, 236, 270, 236, 236, 236, 
-	95, 271, 236, 241, 242, 236, 236, 236, 
-	236, 247, 236, 270, 236, 272, 273, 274, 
-	275, 82, 241, 242, 236, 236, 236, 105, 
-	276, 236, 277, 273, 275, 275, 82, 241, 
-	242, 236, 236, 236, 236, 276, 236, 273, 
-	275, 275, 82, 241, 242, 236, 236, 236, 
-	236, 276, 236, 95, 95, 236, 241, 242, 
-	236, 236, 236, 236, 247, 236, 241, 242, 
-	236, 236, 236, 236, 247, 236, 278, 236, 
-	236, 236, 242, 236, 236, 236, 236, 247, 
-	236, 242, 236, 236, 236, 236, 247, 236, 
-	279, 236, 280, 236, 281, 236, 236, 236, 
-	242, 236, 236, 236, 79, 236, 78, 236, 
-	236, 236, 236, 236, 242, 236, 242, 236, 
-	280, 236, 236, 236, 236, 236, 242, 236, 
-	95, 236, 241, 242, 236, 236, 236, 236, 
-	247, 236, 282, 104, 283, 284, 85, 241, 
-	242, 236, 236, 236, 236, 247, 236, 104, 
-	283, 284, 85, 241, 242, 236, 236, 236, 
-	236, 247, 236, 283, 283, 85, 241, 242, 
-	236, 236, 236, 236, 247, 236, 285, 101, 
-	286, 287, 88, 241, 242, 236, 236, 236, 
-	236, 247, 236, 101, 286, 287, 88, 241, 
-	242, 236, 236, 236, 236, 247, 236, 286, 
-	286, 88, 241, 242, 236, 236, 236, 236, 
-	247, 236, 288, 98, 289, 290, 91, 241, 
-	242, 236, 236, 236, 236, 247, 236, 98, 
-	289, 290, 91, 241, 242, 236, 236, 236, 
-	236, 247, 236, 289, 289, 91, 241, 242, 
-	236, 236, 236, 236, 247, 236, 291, 95, 
-	236, 292, 236, 241, 242, 236, 236, 236, 
-	236, 247, 236, 95, 236, 292, 236, 241, 
-	242, 236, 236, 236, 236, 247, 236, 77, 
-	78, 236, 236, 95, 95, 236, 241, 242, 
-	236, 236, 236, 236, 247, 236, 77, 236, 
-	272, 273, 275, 275, 82, 241, 242, 236, 
-	236, 236, 236, 276, 236, 270, 236, 236, 
-	236, 236, 236, 236, 241, 242, 236, 236, 
-	236, 236, 247, 236, 270, 236, 270, 236, 
-	236, 236, 236, 271, 236, 241, 242, 236, 
-	236, 236, 236, 247, 236, 270, 236, 270, 
-	78, 236, 236, 95, 271, 236, 241, 242, 
-	236, 236, 236, 236, 247, 236, 270, 236, 
-	264, 265, 267, 267, 82, 241, 242, 236, 
-	236, 236, 236, 268, 236, 262, 236, 236, 
-	236, 236, 236, 236, 241, 242, 236, 236, 
-	236, 236, 247, 236, 262, 236, 262, 236, 
-	236, 236, 236, 263, 236, 241, 242, 236, 
-	236, 236, 236, 247, 236, 262, 236, 262, 
-	78, 236, 236, 95, 263, 236, 241, 242, 
-	236, 236, 236, 236, 247, 236, 262, 236, 
-	256, 257, 259, 259, 82, 241, 242, 236, 
-	236, 236, 236, 260, 236, 254, 236, 236, 
-	236, 236, 236, 236, 241, 242, 236, 236, 
-	236, 236, 247, 236, 254, 236, 254, 236, 
-	236, 236, 236, 255, 236, 241, 242, 236, 
-	236, 236, 236, 247, 236, 254, 236, 254, 
-	78, 236, 236, 95, 255, 236, 241, 242, 
-	236, 236, 236, 236, 247, 236, 254, 236, 
-	248, 249, 251, 251, 82, 241, 242, 236, 
-	236, 236, 236, 252, 236, 245, 236, 236, 
-	236, 236, 236, 236, 241, 242, 236, 236, 
-	236, 236, 247, 236, 245, 236, 245, 236, 
-	236, 236, 236, 246, 236, 241, 242, 236, 
-	236, 236, 236, 247, 236, 245, 236, 245, 
-	78, 236, 236, 95, 246, 236, 241, 242, 
-	236, 236, 236, 236, 247, 236, 245, 236, 
-	237, 238, 240, 240, 82, 241, 242, 236, 
-	236, 236, 236, 243, 236, 115, 116, 293, 
-	293, 293, 293, 293, 293, 293, 293, 117, 
-	117, 293, 293, 293, 115, 293, 121, 294, 
-	123, 124, 6, 125, 126, 127, 120, 120, 
-	37, 128, 120, 130, 116, 120, 120, 19, 
-	131, 120, 125, 126, 120, 117, 117, 120, 
-	132, 120, 130, 120, 0
+	114, 114, 114, 114, 114, 114, 114, 115, 
+	114, 117, 114, 118, 118, 119, 114, 120, 
+	120, 119, 114, 119, 114, 121, 121, 122, 
+	114, 123, 123, 122, 114, 122, 114, 124, 
+	124, 125, 114, 126, 126, 125, 114, 125, 
+	114, 127, 127, 128, 114, 129, 129, 128, 
+	114, 128, 114, 130, 114, 131, 114, 132, 
+	114, 133, 127, 127, 128, 114, 134, 114, 
+	135, 114, 136, 124, 124, 125, 114, 137, 
+	114, 138, 114, 139, 121, 121, 122, 114, 
+	140, 114, 141, 114, 142, 118, 118, 119, 
+	114, 114, 114, 114, 114, 114, 142, 114, 
+	142, 118, 118, 119, 114, 114, 114, 114, 
+	114, 143, 142, 114, 144, 118, 118, 119, 
+	114, 114, 114, 114, 114, 114, 144, 114, 
+	144, 118, 118, 119, 114, 114, 114, 114, 
+	114, 145, 144, 114, 146, 118, 118, 119, 
+	114, 114, 114, 114, 114, 114, 146, 114, 
+	146, 118, 118, 119, 114, 114, 114, 114, 
+	114, 147, 146, 114, 148, 118, 118, 119, 
+	114, 114, 114, 114, 114, 114, 148, 114, 
+	148, 118, 118, 119, 114, 114, 114, 114, 
+	114, 149, 148, 114, 150, 118, 118, 119, 
+	114, 114, 114, 114, 114, 114, 150, 114, 
+	150, 118, 118, 119, 114, 114, 114, 114, 
+	114, 151, 150, 114, 153, 154, 155, 156, 
+	157, 158, 81, 159, 160, 152, 161, 161, 
+	162, 163, 164, 165, 152, 167, 168, 169, 
+	170, 5, 171, 172, 173, 166, 166, 37, 
+	174, 166, 175, 168, 176, 176, 5, 171, 
+	172, 173, 166, 166, 166, 174, 166, 168, 
+	176, 176, 5, 171, 172, 173, 166, 166, 
+	166, 174, 166, 177, 166, 166, 166, 18, 
+	178, 166, 171, 172, 166, 166, 166, 166, 
+	179, 166, 177, 166, 180, 181, 182, 183, 
+	5, 171, 172, 173, 166, 166, 35, 184, 
+	166, 185, 181, 186, 186, 5, 171, 172, 
+	173, 166, 166, 166, 184, 166, 181, 186, 
+	186, 5, 171, 172, 173, 166, 166, 166, 
+	184, 166, 187, 166, 166, 166, 18, 188, 
+	166, 171, 172, 166, 166, 166, 166, 179, 
+	166, 187, 166, 189, 190, 191, 192, 5, 
+	171, 172, 173, 166, 166, 33, 193, 166, 
+	194, 190, 195, 195, 5, 171, 172, 173, 
+	166, 166, 166, 193, 166, 190, 195, 195, 
+	5, 171, 172, 173, 166, 166, 166, 193, 
+	166, 196, 166, 166, 166, 18, 197, 166, 
+	171, 172, 166, 166, 166, 166, 179, 166, 
+	196, 166, 198, 199, 200, 201, 5, 171, 
+	172, 173, 166, 166, 31, 202, 166, 203, 
+	199, 204, 204, 5, 171, 172, 173, 166, 
+	166, 166, 202, 166, 199, 204, 204, 5, 
+	171, 172, 173, 166, 166, 166, 202, 166, 
+	205, 166, 166, 166, 18, 206, 166, 171, 
+	172, 166, 166, 166, 166, 179, 166, 205, 
+	166, 207, 208, 209, 210, 5, 171, 172, 
+	173, 166, 166, 29, 211, 166, 212, 208, 
+	213, 213, 5, 171, 172, 173, 166, 166, 
+	166, 211, 166, 208, 213, 213, 5, 171, 
+	172, 173, 166, 166, 166, 211, 166, 18, 
+	214, 166, 171, 172, 166, 166, 166, 166, 
+	179, 166, 171, 172, 166, 166, 166, 166, 
+	179, 166, 215, 166, 166, 166, 172, 166, 
+	172, 166, 216, 166, 217, 166, 218, 219, 
+	166, 171, 172, 166, 166, 166, 3, 166, 
+	2, 166, 166, 166, 166, 171, 172, 166, 
+	171, 172, 166, 217, 166, 166, 166, 166, 
+	171, 172, 166, 217, 166, 218, 166, 166, 
+	171, 172, 166, 166, 166, 3, 166, 18, 
+	166, 220, 220, 5, 171, 172, 166, 166, 
+	166, 166, 179, 166, 221, 27, 222, 223, 
+	8, 171, 172, 166, 166, 166, 166, 179, 
+	166, 27, 222, 223, 8, 171, 172, 166, 
+	166, 166, 166, 179, 166, 222, 222, 8, 
+	171, 172, 166, 166, 166, 166, 179, 166, 
+	224, 24, 225, 226, 11, 171, 172, 166, 
+	166, 166, 166, 179, 166, 24, 225, 226, 
+	11, 171, 172, 166, 166, 166, 166, 179, 
+	166, 225, 225, 11, 171, 172, 166, 166, 
+	166, 166, 179, 166, 227, 21, 228, 229, 
+	14, 171, 172, 166, 166, 166, 166, 179, 
+	166, 21, 228, 229, 14, 171, 172, 166, 
+	166, 166, 166, 179, 166, 228, 228, 14, 
+	171, 172, 166, 166, 166, 166, 179, 166, 
+	230, 18, 166, 231, 166, 171, 172, 166, 
+	166, 166, 166, 179, 166, 18, 166, 231, 
+	166, 171, 172, 166, 166, 166, 166, 179, 
+	166, 232, 166, 171, 172, 166, 166, 166, 
+	166, 179, 166, 18, 166, 166, 166, 166, 
+	171, 172, 166, 166, 166, 166, 179, 166, 
+	208, 213, 213, 5, 171, 172, 166, 166, 
+	166, 166, 211, 166, 1, 2, 166, 166, 
+	18, 214, 166, 171, 172, 166, 166, 166, 
+	166, 179, 166, 1, 166, 207, 208, 213, 
+	213, 5, 171, 172, 173, 166, 166, 166, 
+	211, 166, 207, 208, 209, 213, 5, 171, 
+	172, 173, 166, 166, 29, 211, 166, 205, 
+	166, 233, 166, 220, 220, 5, 171, 172, 
+	166, 166, 166, 166, 179, 166, 205, 166, 
+	205, 166, 166, 166, 166, 166, 166, 171, 
+	172, 166, 166, 166, 166, 179, 166, 205, 
+	166, 205, 166, 166, 166, 166, 234, 166, 
+	171, 172, 166, 166, 166, 166, 179, 166, 
+	205, 166, 205, 166, 233, 166, 166, 166, 
+	166, 171, 172, 166, 166, 166, 166, 179, 
+	166, 205, 166, 205, 2, 166, 166, 18, 
+	206, 166, 171, 172, 166, 166, 166, 166, 
+	179, 166, 205, 166, 198, 199, 204, 204, 
+	5, 171, 172, 173, 166, 166, 166, 202, 
+	166, 198, 199, 200, 204, 5, 171, 172, 
+	173, 166, 166, 31, 202, 166, 196, 166, 
+	235, 166, 220, 220, 5, 171, 172, 166, 
+	166, 166, 166, 179, 166, 196, 166, 196, 
+	166, 166, 166, 166, 166, 166, 171, 172, 
+	166, 166, 166, 166, 179, 166, 196, 166, 
+	196, 166, 166, 166, 166, 236, 166, 171, 
+	172, 166, 166, 166, 166, 179, 166, 196, 
+	166, 196, 166, 235, 166, 166, 166, 166, 
+	171, 172, 166, 166, 166, 166, 179, 166, 
+	196, 166, 196, 2, 166, 166, 18, 197, 
+	166, 171, 172, 166, 166, 166, 166, 179, 
+	166, 196, 166, 189, 190, 195, 195, 5, 
+	171, 172, 173, 166, 166, 166, 193, 166, 
+	189, 190, 191, 195, 5, 171, 172, 173, 
+	166, 166, 33, 193, 166, 187, 166, 237, 
+	166, 220, 220, 5, 171, 172, 166, 166, 
+	166, 166, 179, 166, 187, 166, 187, 166, 
+	166, 166, 166, 166, 166, 171, 172, 166, 
+	166, 166, 166, 179, 166, 187, 166, 187, 
+	166, 166, 166, 166, 238, 166, 171, 172, 
+	166, 166, 166, 166, 179, 166, 187, 166, 
+	187, 166, 237, 166, 166, 166, 166, 171, 
+	172, 166, 166, 166, 166, 179, 166, 187, 
+	166, 187, 2, 166, 166, 18, 188, 166, 
+	171, 172, 166, 166, 166, 166, 179, 166, 
+	187, 166, 180, 181, 186, 186, 5, 171, 
+	172, 173, 166, 166, 166, 184, 166, 180, 
+	181, 182, 186, 5, 171, 172, 173, 166, 
+	166, 35, 184, 166, 177, 166, 239, 166, 
+	220, 220, 5, 171, 172, 166, 166, 166, 
+	166, 179, 166, 177, 166, 177, 166, 166, 
+	166, 166, 166, 166, 171, 172, 166, 166, 
+	166, 166, 179, 166, 177, 166, 177, 166, 
+	166, 166, 166, 240, 166, 171, 172, 166, 
+	166, 166, 166, 179, 166, 177, 166, 177, 
+	166, 239, 166, 166, 166, 166, 171, 172, 
+	166, 166, 166, 166, 179, 166, 177, 166, 
+	177, 2, 166, 166, 18, 178, 166, 171, 
+	172, 166, 166, 166, 166, 179, 166, 177, 
+	166, 167, 168, 176, 176, 5, 171, 172, 
+	173, 166, 166, 166, 174, 166, 167, 168, 
+	169, 176, 5, 171, 172, 173, 166, 166, 
+	37, 174, 166, 242, 243, 244, 245, 43, 
+	246, 247, 241, 241, 241, 75, 248, 241, 
+	249, 243, 250, 245, 43, 246, 247, 241, 
+	241, 241, 241, 248, 241, 243, 250, 245, 
+	43, 246, 247, 241, 241, 241, 241, 248, 
+	241, 251, 241, 241, 241, 56, 252, 241, 
+	246, 247, 241, 241, 241, 241, 253, 241, 
+	251, 241, 254, 255, 256, 257, 43, 246, 
+	247, 241, 241, 241, 73, 258, 241, 259, 
+	255, 260, 260, 43, 246, 247, 241, 241, 
+	241, 241, 258, 241, 255, 260, 260, 43, 
+	246, 247, 241, 241, 241, 241, 258, 241, 
+	261, 241, 241, 241, 56, 262, 241, 246, 
+	247, 241, 241, 241, 241, 253, 241, 261, 
+	241, 263, 264, 265, 266, 43, 246, 247, 
+	241, 241, 241, 71, 267, 241, 268, 264, 
+	269, 269, 43, 246, 247, 241, 241, 241, 
+	241, 267, 241, 264, 269, 269, 43, 246, 
+	247, 241, 241, 241, 241, 267, 241, 270, 
+	241, 241, 241, 56, 271, 241, 246, 247, 
+	241, 241, 241, 241, 253, 241, 270, 241, 
+	272, 273, 274, 275, 43, 246, 247, 241, 
+	241, 241, 69, 276, 241, 277, 273, 278, 
+	278, 43, 246, 247, 241, 241, 241, 241, 
+	276, 241, 273, 278, 278, 43, 246, 247, 
+	241, 241, 241, 241, 276, 241, 279, 241, 
+	241, 241, 56, 280, 241, 246, 247, 241, 
+	241, 241, 241, 253, 241, 279, 241, 281, 
+	282, 283, 284, 43, 246, 247, 241, 241, 
+	241, 67, 285, 241, 286, 282, 287, 287, 
+	43, 246, 247, 241, 241, 241, 241, 285, 
+	241, 282, 287, 287, 43, 246, 247, 241, 
+	241, 241, 241, 285, 241, 56, 288, 241, 
+	246, 247, 241, 241, 241, 241, 253, 241, 
+	246, 247, 241, 241, 241, 241, 253, 241, 
+	289, 241, 241, 241, 247, 241, 247, 241, 
+	290, 241, 291, 241, 292, 293, 241, 246, 
+	247, 241, 241, 241, 41, 241, 40, 241, 
+	241, 241, 241, 246, 247, 241, 246, 247, 
+	241, 291, 241, 241, 241, 241, 246, 247, 
+	241, 291, 241, 292, 241, 241, 246, 247, 
+	241, 241, 241, 41, 241, 56, 241, 294, 
+	294, 43, 246, 247, 241, 241, 241, 241, 
+	253, 241, 295, 65, 296, 297, 46, 246, 
+	247, 241, 241, 241, 241, 253, 241, 65, 
+	296, 297, 46, 246, 247, 241, 241, 241, 
+	241, 253, 241, 296, 296, 46, 246, 247, 
+	241, 241, 241, 241, 253, 241, 298, 62, 
+	299, 300, 49, 246, 247, 241, 241, 241, 
+	241, 253, 241, 62, 299, 300, 49, 246, 
+	247, 241, 241, 241, 241, 253, 241, 299, 
+	299, 49, 246, 247, 241, 241, 241, 241, 
+	253, 241, 301, 59, 302, 303, 52, 246, 
+	247, 241, 241, 241, 241, 253, 241, 59, 
+	302, 303, 52, 246, 247, 241, 241, 241, 
+	241, 253, 241, 302, 302, 52, 246, 247, 
+	241, 241, 241, 241, 253, 241, 304, 56, 
+	241, 305, 241, 246, 247, 241, 241, 241, 
+	241, 253, 241, 56, 241, 305, 241, 246, 
+	247, 241, 241, 241, 241, 253, 241, 306, 
+	241, 246, 247, 241, 241, 241, 241, 253, 
+	241, 56, 241, 241, 241, 241, 246, 247, 
+	241, 241, 241, 241, 253, 241, 39, 40, 
+	241, 241, 56, 288, 241, 246, 247, 241, 
+	241, 241, 241, 253, 241, 39, 241, 281, 
+	282, 287, 287, 43, 246, 247, 241, 241, 
+	241, 241, 285, 241, 281, 282, 283, 287, 
+	43, 246, 247, 241, 241, 241, 67, 285, 
+	241, 279, 241, 307, 241, 294, 294, 43, 
+	246, 247, 241, 241, 241, 241, 253, 241, 
+	279, 241, 279, 241, 241, 241, 241, 241, 
+	241, 246, 247, 241, 241, 241, 241, 253, 
+	241, 279, 241, 279, 241, 241, 241, 241, 
+	308, 241, 246, 247, 241, 241, 241, 241, 
+	253, 241, 279, 241, 279, 241, 307, 241, 
+	241, 241, 241, 246, 247, 241, 241, 241, 
+	241, 253, 241, 279, 241, 279, 40, 241, 
+	241, 56, 280, 241, 246, 247, 241, 241, 
+	241, 241, 253, 241, 279, 241, 272, 273, 
+	278, 278, 43, 246, 247, 241, 241, 241, 
+	241, 276, 241, 272, 273, 274, 278, 43, 
+	246, 247, 241, 241, 241, 69, 276, 241, 
+	270, 241, 309, 241, 294, 294, 43, 246, 
+	247, 241, 241, 241, 241, 253, 241, 270, 
+	241, 270, 241, 241, 241, 241, 241, 241, 
+	246, 247, 241, 241, 241, 241, 253, 241, 
+	270, 241, 270, 241, 241, 241, 241, 310, 
+	241, 246, 247, 241, 241, 241, 241, 253, 
+	241, 270, 241, 270, 241, 309, 241, 241, 
+	241, 241, 246, 247, 241, 241, 241, 241, 
+	253, 241, 270, 241, 270, 40, 241, 241, 
+	56, 271, 241, 246, 247, 241, 241, 241, 
+	241, 253, 241, 270, 241, 263, 264, 269, 
+	269, 43, 246, 247, 241, 241, 241, 241, 
+	267, 241, 263, 264, 265, 269, 43, 246, 
+	247, 241, 241, 241, 71, 267, 241, 261, 
+	241, 311, 241, 294, 294, 43, 246, 247, 
+	241, 241, 241, 241, 253, 241, 261, 241, 
+	261, 241, 241, 241, 241, 241, 241, 246, 
+	247, 241, 241, 241, 241, 253, 241, 261, 
+	241, 261, 241, 241, 241, 241, 312, 241, 
+	246, 247, 241, 241, 241, 241, 253, 241, 
+	261, 241, 261, 241, 311, 241, 241, 241, 
+	241, 246, 247, 241, 241, 241, 241, 253, 
+	241, 261, 241, 261, 40, 241, 241, 56, 
+	262, 241, 246, 247, 241, 241, 241, 241, 
+	253, 241, 261, 241, 254, 255, 260, 260, 
+	43, 246, 247, 241, 241, 241, 241, 258, 
+	241, 254, 255, 256, 260, 43, 246, 247, 
+	241, 241, 241, 73, 258, 241, 251, 241, 
+	313, 241, 294, 294, 43, 246, 247, 241, 
+	241, 241, 241, 253, 241, 251, 241, 251, 
+	241, 241, 241, 241, 241, 241, 246, 247, 
+	241, 241, 241, 241, 253, 241, 251, 241, 
+	251, 241, 241, 241, 241, 314, 241, 246, 
+	247, 241, 241, 241, 241, 253, 241, 251, 
+	241, 251, 241, 313, 241, 241, 241, 241, 
+	246, 247, 241, 241, 241, 241, 253, 241, 
+	251, 241, 74, 42, 42, 43, 241, 241, 
+	241, 241, 241, 241, 74, 241, 251, 40, 
+	241, 241, 56, 252, 241, 246, 247, 241, 
+	241, 241, 241, 253, 241, 251, 241, 242, 
+	243, 250, 245, 43, 246, 247, 241, 241, 
+	241, 241, 248, 241, 316, 156, 317, 317, 
+	81, 159, 160, 315, 315, 315, 315, 163, 
+	315, 156, 317, 317, 81, 159, 160, 315, 
+	315, 315, 315, 163, 315, 318, 315, 315, 
+	315, 95, 319, 315, 159, 160, 315, 315, 
+	315, 315, 320, 315, 318, 315, 321, 322, 
+	323, 324, 81, 159, 160, 315, 315, 315, 
+	112, 325, 315, 326, 322, 327, 327, 81, 
+	159, 160, 315, 315, 315, 315, 325, 315, 
+	322, 327, 327, 81, 159, 160, 315, 315, 
+	315, 315, 325, 315, 328, 315, 315, 315, 
+	95, 329, 315, 159, 160, 315, 315, 315, 
+	315, 320, 315, 328, 315, 330, 331, 332, 
+	333, 81, 159, 160, 315, 315, 315, 110, 
+	334, 315, 335, 331, 336, 336, 81, 159, 
+	160, 315, 315, 315, 315, 334, 315, 331, 
+	336, 336, 81, 159, 160, 315, 315, 315, 
+	315, 334, 315, 337, 315, 315, 315, 95, 
+	338, 315, 159, 160, 315, 315, 315, 315, 
+	320, 315, 337, 315, 339, 340, 341, 342, 
+	81, 159, 160, 315, 315, 315, 108, 343, 
+	315, 344, 340, 345, 345, 81, 159, 160, 
+	315, 315, 315, 315, 343, 315, 340, 345, 
+	345, 81, 159, 160, 315, 315, 315, 315, 
+	343, 315, 346, 315, 315, 315, 95, 347, 
+	315, 159, 160, 315, 315, 315, 315, 320, 
+	315, 346, 315, 348, 349, 350, 351, 81, 
+	159, 160, 315, 315, 315, 106, 352, 315, 
+	353, 349, 354, 354, 81, 159, 160, 315, 
+	315, 315, 315, 352, 315, 349, 354, 354, 
+	81, 159, 160, 315, 315, 315, 315, 352, 
+	315, 95, 355, 315, 159, 160, 315, 315, 
+	315, 315, 320, 315, 159, 160, 315, 315, 
+	315, 315, 320, 315, 356, 315, 315, 315, 
+	160, 315, 160, 315, 357, 315, 358, 315, 
+	359, 360, 315, 159, 160, 315, 315, 315, 
+	79, 315, 78, 315, 315, 315, 315, 159, 
+	160, 315, 159, 160, 315, 358, 315, 315, 
+	315, 315, 159, 160, 315, 358, 315, 359, 
+	315, 315, 159, 160, 315, 315, 315, 79, 
+	315, 95, 315, 361, 361, 81, 159, 160, 
+	315, 315, 315, 315, 320, 315, 362, 104, 
+	363, 364, 85, 159, 160, 315, 315, 315, 
+	315, 320, 315, 104, 363, 364, 85, 159, 
+	160, 315, 315, 315, 315, 320, 315, 363, 
+	363, 85, 159, 160, 315, 315, 315, 315, 
+	320, 315, 365, 101, 366, 367, 88, 159, 
+	160, 315, 315, 315, 315, 320, 315, 101, 
+	366, 367, 88, 159, 160, 315, 315, 315, 
+	315, 320, 315, 366, 366, 88, 159, 160, 
+	315, 315, 315, 315, 320, 315, 368, 98, 
+	369, 370, 91, 159, 160, 315, 315, 315, 
+	315, 320, 315, 98, 369, 370, 91, 159, 
+	160, 315, 315, 315, 315, 320, 315, 369, 
+	369, 91, 159, 160, 315, 315, 315, 315, 
+	320, 315, 371, 95, 315, 372, 315, 159, 
+	160, 315, 315, 315, 315, 320, 315, 95, 
+	315, 372, 315, 159, 160, 315, 315, 315, 
+	315, 320, 315, 373, 315, 159, 160, 315, 
+	315, 315, 315, 320, 315, 95, 315, 315, 
+	315, 315, 159, 160, 315, 315, 315, 315, 
+	320, 315, 77, 78, 315, 315, 95, 355, 
+	315, 159, 160, 315, 315, 315, 315, 320, 
+	315, 77, 315, 348, 349, 354, 354, 81, 
+	159, 160, 315, 315, 315, 315, 352, 315, 
+	348, 349, 350, 354, 81, 159, 160, 315, 
+	315, 315, 106, 352, 315, 346, 315, 374, 
+	315, 361, 361, 81, 159, 160, 315, 315, 
+	315, 315, 320, 315, 346, 315, 346, 315, 
+	315, 315, 315, 315, 315, 159, 160, 315, 
+	315, 315, 315, 320, 315, 346, 315, 346, 
+	315, 315, 315, 315, 375, 315, 159, 160, 
+	315, 315, 315, 315, 320, 315, 346, 315, 
+	346, 315, 374, 315, 315, 315, 315, 159, 
+	160, 315, 315, 315, 315, 320, 315, 346, 
+	315, 346, 78, 315, 315, 95, 347, 315, 
+	159, 160, 315, 315, 315, 315, 320, 315, 
+	346, 315, 339, 340, 345, 345, 81, 159, 
+	160, 315, 315, 315, 315, 343, 315, 339, 
+	340, 341, 345, 81, 159, 160, 315, 315, 
+	315, 108, 343, 315, 337, 315, 376, 315, 
+	361, 361, 81, 159, 160, 315, 315, 315, 
+	315, 320, 315, 337, 315, 337, 315, 315, 
+	315, 315, 315, 315, 159, 160, 315, 315, 
+	315, 315, 320, 315, 337, 315, 337, 315, 
+	315, 315, 315, 377, 315, 159, 160, 315, 
+	315, 315, 315, 320, 315, 337, 315, 337, 
+	315, 376, 315, 315, 315, 315, 159, 160, 
+	315, 315, 315, 315, 320, 315, 337, 315, 
+	337, 78, 315, 315, 95, 338, 315, 159, 
+	160, 315, 315, 315, 315, 320, 315, 337, 
+	315, 330, 331, 336, 336, 81, 159, 160, 
+	315, 315, 315, 315, 334, 315, 330, 331, 
+	332, 336, 81, 159, 160, 315, 315, 315, 
+	110, 334, 315, 328, 315, 378, 315, 361, 
+	361, 81, 159, 160, 315, 315, 315, 315, 
+	320, 315, 328, 315, 328, 315, 315, 315, 
+	315, 315, 315, 159, 160, 315, 315, 315, 
+	315, 320, 315, 328, 315, 328, 315, 315, 
+	315, 315, 379, 315, 159, 160, 315, 315, 
+	315, 315, 320, 315, 328, 315, 328, 315, 
+	378, 315, 315, 315, 315, 159, 160, 315, 
+	315, 315, 315, 320, 315, 328, 315, 328, 
+	78, 315, 315, 95, 329, 315, 159, 160, 
+	315, 315, 315, 315, 320, 315, 328, 315, 
+	321, 322, 327, 327, 81, 159, 160, 315, 
+	315, 315, 315, 325, 315, 321, 322, 323, 
+	327, 81, 159, 160, 315, 315, 315, 112, 
+	325, 315, 318, 315, 380, 315, 361, 361, 
+	81, 159, 160, 315, 315, 315, 315, 320, 
+	315, 318, 315, 318, 315, 315, 315, 315, 
+	315, 315, 159, 160, 315, 315, 315, 315, 
+	320, 315, 318, 315, 318, 315, 315, 315, 
+	315, 381, 315, 159, 160, 315, 315, 315, 
+	315, 320, 315, 318, 315, 318, 315, 380, 
+	315, 315, 315, 315, 159, 160, 315, 315, 
+	315, 315, 320, 315, 318, 315, 318, 78, 
+	315, 315, 95, 319, 315, 159, 160, 315, 
+	315, 315, 315, 320, 315, 318, 315, 113, 
+	80, 80, 81, 382, 382, 382, 382, 382, 
+	162, 113, 382, 155, 156, 317, 317, 81, 
+	159, 160, 315, 315, 315, 315, 163, 315, 
+	113, 80, 80, 81, 382, 382, 382, 382, 
+	382, 382, 113, 382, 384, 385, 386, 387, 
+	119, 388, 389, 383, 383, 383, 151, 390, 
+	383, 391, 385, 387, 387, 119, 388, 389, 
+	383, 383, 383, 383, 390, 383, 385, 387, 
+	387, 119, 388, 389, 383, 383, 383, 383, 
+	390, 383, 392, 383, 383, 383, 132, 393, 
+	383, 388, 389, 383, 383, 383, 383, 394, 
+	383, 392, 383, 395, 396, 397, 398, 119, 
+	388, 389, 383, 383, 383, 149, 399, 383, 
+	400, 396, 401, 401, 119, 388, 389, 383, 
+	383, 383, 383, 399, 383, 396, 401, 401, 
+	119, 388, 389, 383, 383, 383, 383, 399, 
+	383, 402, 383, 383, 383, 132, 403, 383, 
+	388, 389, 383, 383, 383, 383, 394, 383, 
+	402, 383, 404, 405, 406, 407, 119, 388, 
+	389, 383, 383, 383, 147, 408, 383, 409, 
+	405, 410, 410, 119, 388, 389, 383, 383, 
+	383, 383, 408, 383, 405, 410, 410, 119, 
+	388, 389, 383, 383, 383, 383, 408, 383, 
+	411, 383, 383, 383, 132, 412, 383, 388, 
+	389, 383, 383, 383, 383, 394, 383, 411, 
+	383, 413, 414, 415, 416, 119, 388, 389, 
+	383, 383, 383, 145, 417, 383, 418, 414, 
+	419, 419, 119, 388, 389, 383, 383, 383, 
+	383, 417, 383, 414, 419, 419, 119, 388, 
+	389, 383, 383, 383, 383, 417, 383, 420, 
+	383, 383, 383, 132, 421, 383, 388, 389, 
+	383, 383, 383, 383, 394, 383, 420, 383, 
+	422, 423, 424, 425, 119, 388, 389, 383, 
+	383, 383, 143, 426, 383, 427, 423, 428, 
+	428, 119, 388, 389, 383, 383, 383, 383, 
+	426, 383, 423, 428, 428, 119, 388, 389, 
+	383, 383, 383, 383, 426, 383, 132, 429, 
+	383, 388, 389, 383, 383, 383, 383, 394, 
+	383, 388, 389, 383, 383, 383, 383, 394, 
+	383, 430, 383, 383, 383, 389, 383, 389, 
+	383, 431, 383, 432, 383, 433, 434, 383, 
+	388, 389, 383, 383, 383, 117, 383, 116, 
+	383, 383, 383, 383, 388, 389, 383, 388, 
+	389, 383, 432, 383, 383, 383, 383, 388, 
+	389, 383, 432, 383, 433, 383, 383, 388, 
+	389, 383, 383, 383, 117, 383, 132, 383, 
+	435, 435, 119, 388, 389, 383, 383, 383, 
+	383, 394, 383, 436, 141, 437, 438, 122, 
+	388, 389, 383, 383, 383, 383, 394, 383, 
+	141, 437, 438, 122, 388, 389, 383, 383, 
+	383, 383, 394, 383, 437, 437, 122, 388, 
+	389, 383, 383, 383, 383, 394, 383, 439, 
+	138, 440, 441, 125, 388, 389, 383, 383, 
+	383, 383, 394, 383, 138, 440, 441, 125, 
+	388, 389, 383, 383, 383, 383, 394, 383, 
+	440, 440, 125, 388, 389, 383, 383, 383, 
+	383, 394, 383, 442, 135, 443, 444, 128, 
+	388, 389, 383, 383, 383, 383, 394, 383, 
+	135, 443, 444, 128, 388, 389, 383, 383, 
+	383, 383, 394, 383, 443, 443, 128, 388, 
+	389, 383, 383, 383, 383, 394, 383, 445, 
+	132, 383, 446, 383, 388, 389, 383, 383, 
+	383, 383, 394, 383, 132, 383, 446, 383, 
+	388, 389, 383, 383, 383, 383, 394, 383, 
+	447, 383, 388, 389, 383, 383, 383, 383, 
+	394, 383, 132, 383, 383, 383, 383, 388, 
+	389, 383, 383, 383, 383, 394, 383, 115, 
+	116, 383, 383, 132, 429, 383, 388, 389, 
+	383, 383, 383, 383, 394, 383, 115, 383, 
+	422, 423, 428, 428, 119, 388, 389, 383, 
+	383, 383, 383, 426, 383, 422, 423, 424, 
+	428, 119, 388, 389, 383, 383, 383, 143, 
+	426, 383, 420, 383, 448, 383, 435, 435, 
+	119, 388, 389, 383, 383, 383, 383, 394, 
+	383, 420, 383, 420, 383, 383, 383, 383, 
+	383, 383, 388, 389, 383, 383, 383, 383, 
+	394, 383, 420, 383, 420, 383, 383, 383, 
+	383, 449, 383, 388, 389, 383, 383, 383, 
+	383, 394, 383, 420, 383, 420, 383, 448, 
+	383, 383, 383, 383, 388, 389, 383, 383, 
+	383, 383, 394, 383, 420, 383, 420, 116, 
+	383, 383, 132, 421, 383, 388, 389, 383, 
+	383, 383, 383, 394, 383, 420, 383, 413, 
+	414, 419, 419, 119, 388, 389, 383, 383, 
+	383, 383, 417, 383, 413, 414, 415, 419, 
+	119, 388, 389, 383, 383, 383, 145, 417, 
+	383, 411, 383, 450, 383, 435, 435, 119, 
+	388, 389, 383, 383, 383, 383, 394, 383, 
+	411, 383, 411, 383, 383, 383, 383, 383, 
+	383, 388, 389, 383, 383, 383, 383, 394, 
+	383, 411, 383, 411, 383, 383, 383, 383, 
+	451, 383, 388, 389, 383, 383, 383, 383, 
+	394, 383, 411, 383, 411, 383, 450, 383, 
+	383, 383, 383, 388, 389, 383, 383, 383, 
+	383, 394, 383, 411, 383, 411, 116, 383, 
+	383, 132, 412, 383, 388, 389, 383, 383, 
+	383, 383, 394, 383, 411, 383, 404, 405, 
+	410, 410, 119, 388, 389, 383, 383, 383, 
+	383, 408, 383, 404, 405, 406, 410, 119, 
+	388, 389, 383, 383, 383, 147, 408, 383, 
+	402, 383, 452, 383, 435, 435, 119, 388, 
+	389, 383, 383, 383, 383, 394, 383, 402, 
+	383, 402, 383, 383, 383, 383, 383, 383, 
+	388, 389, 383, 383, 383, 383, 394, 383, 
+	402, 383, 402, 383, 383, 383, 383, 453, 
+	383, 388, 389, 383, 383, 383, 383, 394, 
+	383, 402, 383, 402, 383, 452, 383, 383, 
+	383, 383, 388, 389, 383, 383, 383, 383, 
+	394, 383, 402, 383, 402, 116, 383, 383, 
+	132, 403, 383, 388, 389, 383, 383, 383, 
+	383, 394, 383, 402, 383, 395, 396, 401, 
+	401, 119, 388, 389, 383, 383, 383, 383, 
+	399, 383, 395, 396, 397, 401, 119, 388, 
+	389, 383, 383, 383, 149, 399, 383, 392, 
+	383, 454, 383, 435, 435, 119, 388, 389, 
+	383, 383, 383, 383, 394, 383, 392, 383, 
+	392, 383, 383, 383, 383, 383, 383, 388, 
+	389, 383, 383, 383, 383, 394, 383, 392, 
+	383, 392, 383, 383, 383, 383, 455, 383, 
+	388, 389, 383, 383, 383, 383, 394, 383, 
+	392, 383, 392, 383, 454, 383, 383, 383, 
+	383, 388, 389, 383, 383, 383, 383, 394, 
+	383, 392, 383, 392, 116, 383, 383, 132, 
+	393, 383, 388, 389, 383, 383, 383, 383, 
+	394, 383, 392, 383, 384, 385, 387, 387, 
+	119, 388, 389, 383, 383, 383, 383, 390, 
+	383, 153, 154, 382, 382, 382, 382, 382, 
+	382, 382, 382, 161, 161, 382, 382, 382, 
+	153, 382, 167, 456, 169, 170, 5, 171, 
+	172, 173, 166, 166, 37, 174, 166, 177, 
+	154, 166, 166, 18, 178, 166, 171, 172, 
+	166, 161, 161, 166, 179, 166, 177, 166, 
+	0
 };
 
 static const short _indic_syllable_machine_trans_targs[] = {
-	105, 130, 132, 133, 134, 3, 135, 4, 
-	6, 138, 7, 9, 141, 10, 12, 144, 
-	13, 15, 16, 126, 18, 19, 143, 21, 
-	22, 140, 24, 25, 137, 148, 150, 152, 
-	154, 156, 158, 160, 162, 164, 105, 189, 
-	191, 192, 193, 38, 194, 39, 41, 197, 
-	42, 44, 200, 45, 47, 203, 48, 50, 
-	51, 185, 53, 54, 202, 56, 57, 199, 
-	59, 60, 196, 206, 208, 210, 212, 214, 
-	216, 218, 220, 223, 105, 248, 250, 251, 
-	252, 73, 253, 74, 76, 256, 77, 79, 
-	259, 80, 82, 262, 83, 85, 86, 244, 
-	88, 89, 261, 91, 92, 258, 94, 95, 
-	255, 265, 267, 269, 271, 273, 275, 277, 
-	279, 281, 105, 106, 165, 224, 282, 283, 
-	105, 107, 109, 34, 33, 127, 129, 146, 
-	163, 108, 110, 161, 0, 111, 113, 32, 
-	31, 159, 112, 114, 157, 115, 117, 30, 
-	29, 155, 116, 118, 153, 119, 121, 28, 
-	27, 151, 120, 122, 149, 123, 125, 26, 
-	2, 147, 124, 128, 105, 131, 1, 136, 
-	5, 23, 139, 8, 20, 142, 11, 17, 
-	145, 14, 105, 166, 168, 69, 221, 186, 
-	188, 222, 167, 68, 169, 219, 35, 170, 
-	172, 67, 66, 217, 171, 173, 215, 174, 
-	176, 65, 64, 213, 175, 177, 211, 178, 
-	180, 63, 62, 209, 179, 181, 207, 182, 
-	184, 61, 37, 205, 183, 187, 105, 190, 
-	36, 195, 40, 58, 198, 43, 55, 201, 
-	46, 52, 204, 49, 105, 225, 227, 104, 
-	103, 245, 247, 280, 226, 228, 278, 70, 
-	229, 231, 102, 101, 276, 230, 232, 274, 
-	233, 235, 100, 99, 272, 234, 236, 270, 
-	237, 239, 98, 97, 268, 238, 240, 266, 
-	241, 243, 96, 72, 264, 242, 246, 105, 
-	249, 71, 254, 75, 93, 257, 78, 90, 
-	260, 81, 87, 263, 84, 105, 284
+	143, 168, 170, 171, 3, 174, 4, 6, 
+	177, 7, 9, 180, 10, 12, 183, 13, 
+	15, 16, 164, 18, 19, 182, 21, 22, 
+	179, 24, 25, 176, 185, 189, 193, 196, 
+	200, 203, 207, 210, 214, 217, 143, 243, 
+	245, 246, 39, 249, 40, 42, 252, 43, 
+	45, 255, 46, 48, 258, 49, 51, 52, 
+	239, 54, 55, 257, 57, 58, 254, 60, 
+	61, 251, 260, 263, 267, 270, 274, 277, 
+	281, 284, 288, 292, 143, 316, 318, 319, 
+	75, 322, 143, 76, 78, 325, 79, 81, 
+	328, 82, 84, 331, 85, 87, 88, 312, 
+	90, 91, 330, 93, 94, 327, 96, 97, 
+	324, 333, 336, 340, 343, 347, 350, 354, 
+	357, 361, 143, 391, 393, 394, 110, 397, 
+	111, 113, 400, 114, 116, 403, 117, 119, 
+	406, 120, 122, 123, 387, 125, 126, 405, 
+	128, 129, 402, 131, 132, 399, 408, 411, 
+	415, 418, 422, 425, 429, 432, 436, 439, 
+	143, 144, 219, 293, 295, 364, 366, 313, 
+	315, 367, 365, 363, 440, 441, 143, 145, 
+	147, 35, 218, 165, 167, 187, 216, 146, 
+	34, 148, 212, 0, 149, 151, 33, 211, 
+	209, 150, 32, 152, 205, 153, 155, 31, 
+	204, 202, 154, 30, 156, 198, 157, 159, 
+	29, 197, 195, 158, 28, 160, 191, 161, 
+	163, 27, 190, 188, 162, 26, 173, 166, 
+	143, 169, 1, 172, 2, 175, 5, 23, 
+	178, 8, 20, 181, 11, 17, 184, 14, 
+	186, 192, 194, 199, 201, 206, 208, 213, 
+	215, 143, 220, 222, 71, 290, 240, 242, 
+	291, 221, 70, 223, 286, 36, 224, 226, 
+	69, 285, 283, 225, 68, 227, 279, 228, 
+	230, 67, 278, 276, 229, 66, 231, 272, 
+	232, 234, 65, 271, 269, 233, 64, 235, 
+	265, 236, 238, 63, 264, 262, 237, 62, 
+	248, 241, 143, 244, 37, 247, 38, 250, 
+	41, 59, 253, 44, 56, 256, 47, 53, 
+	259, 50, 261, 266, 268, 273, 275, 280, 
+	282, 287, 289, 143, 294, 106, 296, 359, 
+	72, 297, 299, 105, 358, 356, 298, 104, 
+	300, 352, 301, 303, 103, 351, 349, 302, 
+	102, 304, 345, 305, 307, 101, 344, 342, 
+	306, 100, 308, 338, 309, 311, 99, 337, 
+	335, 310, 98, 321, 314, 143, 317, 73, 
+	320, 74, 323, 77, 95, 326, 80, 92, 
+	329, 83, 89, 332, 86, 334, 339, 341, 
+	346, 348, 353, 355, 360, 362, 143, 143, 
+	368, 370, 142, 141, 388, 390, 438, 369, 
+	371, 434, 107, 372, 374, 140, 433, 431, 
+	373, 139, 375, 427, 376, 378, 138, 426, 
+	424, 377, 137, 379, 420, 380, 382, 136, 
+	419, 417, 381, 135, 383, 413, 384, 386, 
+	134, 412, 410, 385, 133, 396, 389, 143, 
+	392, 108, 395, 109, 398, 112, 130, 401, 
+	115, 127, 404, 118, 124, 407, 121, 409, 
+	414, 416, 421, 423, 428, 430, 435, 437, 
+	442
 };
 
 static const char _indic_syllable_machine_trans_actions[] = {
-	1, 2, 0, 0, 2, 0, 2, 0, 
-	0, 2, 0, 0, 2, 0, 0, 2, 
-	0, 0, 0, 2, 0, 0, 2, 0, 
-	0, 2, 0, 0, 2, 2, 2, 2, 
+	1, 2, 0, 0, 0, 2, 0, 0, 
+	2, 0, 0, 2, 0, 0, 2, 0, 
+	0, 0, 2, 0, 0, 2, 0, 0, 
+	2, 0, 0, 2, 2, 2, 2, 2, 
 	2, 2, 2, 2, 2, 2, 3, 2, 
-	0, 0, 2, 0, 2, 0, 0, 2, 
-	0, 0, 2, 0, 0, 2, 0, 0, 
-	0, 2, 0, 0, 2, 0, 0, 2, 
-	0, 0, 2, 2, 2, 2, 2, 2, 
+	0, 0, 0, 2, 0, 0, 2, 0, 
+	0, 2, 0, 0, 2, 0, 0, 0, 
+	2, 0, 0, 2, 0, 0, 2, 0, 
+	0, 2, 2, 2, 2, 2, 2, 2, 
 	2, 2, 2, 2, 4, 2, 0, 0, 
-	2, 0, 2, 0, 0, 2, 0, 0, 
+	0, 2, 5, 0, 0, 2, 0, 0, 
 	2, 0, 0, 2, 0, 0, 0, 2, 
 	0, 0, 2, 0, 0, 2, 0, 0, 
+	2, 2, 6, 2, 6, 2, 6, 2, 
+	6, 2, 7, 2, 0, 0, 0, 2, 
+	0, 0, 2, 0, 0, 2, 0, 0, 
+	2, 0, 0, 0, 2, 0, 0, 2, 
+	0, 0, 2, 0, 0, 2, 2, 2, 
 	2, 2, 2, 2, 2, 2, 2, 2, 
-	2, 2, 7, 2, 2, 2, 0, 2, 
-	8, 2, 2, 0, 0, 2, 0, 2, 
-	2, 2, 2, 2, 0, 2, 2, 0, 
-	0, 2, 2, 2, 2, 2, 2, 0, 
-	0, 2, 2, 2, 2, 2, 2, 0, 
-	0, 2, 2, 2, 2, 2, 2, 0, 
-	0, 2, 2, 2, 9, 0, 0, 2, 
-	0, 0, 2, 0, 0, 2, 0, 0, 
-	2, 0, 10, 2, 2, 0, 2, 2, 
+	10, 2, 2, 6, 2, 11, 11, 0, 
+	0, 2, 6, 2, 0, 2, 12, 2, 
+	2, 0, 2, 0, 0, 2, 2, 2, 
 	0, 2, 2, 0, 2, 2, 0, 2, 
-	2, 0, 0, 2, 2, 2, 2, 2, 
-	2, 0, 0, 2, 2, 2, 2, 2, 
-	2, 0, 0, 2, 2, 2, 2, 2, 
-	2, 0, 0, 2, 2, 2, 11, 0, 
-	0, 2, 0, 0, 2, 0, 0, 2, 
-	0, 0, 2, 0, 12, 2, 2, 0, 
-	0, 2, 0, 2, 2, 2, 2, 0, 
-	2, 2, 0, 0, 2, 2, 2, 2, 
-	2, 2, 0, 0, 2, 2, 2, 2, 
-	2, 2, 0, 0, 2, 2, 2, 2, 
-	2, 2, 0, 0, 2, 2, 2, 13, 
+	2, 2, 0, 2, 2, 2, 2, 0, 
+	2, 2, 2, 0, 2, 2, 2, 2, 
+	0, 2, 2, 2, 0, 2, 2, 2, 
+	2, 0, 2, 2, 2, 0, 2, 0, 
+	13, 0, 0, 2, 0, 2, 0, 0, 
+	2, 0, 0, 2, 0, 0, 2, 0, 
+	2, 2, 2, 2, 2, 2, 2, 2, 
+	2, 14, 2, 2, 0, 2, 0, 0, 
+	2, 2, 0, 2, 2, 0, 2, 2, 
+	0, 2, 2, 2, 0, 2, 2, 2, 
+	2, 0, 2, 2, 2, 0, 2, 2, 
+	2, 2, 0, 2, 2, 2, 0, 2, 
+	2, 2, 2, 0, 2, 2, 2, 0, 
+	2, 0, 15, 0, 0, 2, 0, 2, 
 	0, 0, 2, 0, 0, 2, 0, 0, 
-	2, 0, 0, 2, 0, 14, 2
+	2, 0, 2, 2, 2, 2, 2, 2, 
+	2, 2, 2, 16, 6, 0, 6, 6, 
+	0, 6, 2, 0, 6, 2, 6, 0, 
+	6, 6, 6, 2, 0, 6, 2, 6, 
+	0, 6, 6, 6, 2, 0, 6, 2, 
+	6, 0, 6, 6, 6, 2, 0, 6, 
+	2, 6, 0, 6, 0, 17, 0, 0, 
+	2, 0, 2, 0, 0, 2, 0, 0, 
+	2, 0, 0, 2, 0, 2, 2, 2, 
+	2, 2, 2, 2, 2, 2, 18, 19, 
+	2, 2, 0, 0, 0, 0, 2, 2, 
+	2, 2, 0, 2, 2, 0, 2, 2, 
+	2, 0, 2, 2, 2, 2, 0, 2, 
+	2, 2, 0, 2, 2, 2, 2, 0, 
+	2, 2, 2, 0, 2, 2, 2, 2, 
+	0, 2, 2, 2, 0, 2, 0, 20, 
+	0, 0, 2, 0, 2, 0, 0, 2, 
+	0, 0, 2, 0, 0, 2, 0, 2, 
+	2, 2, 2, 2, 2, 2, 2, 2, 
+	2
 };
 
 static const char _indic_syllable_machine_to_state_actions[] = {
 	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, 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, 0, 0, 0, 0, 
-	0, 5, 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, 8, 
 	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, 0, 0, 0, 0, 0, 0, 
 	0, 0, 0, 0, 0, 0, 0, 0, 
@@ -637,34 +976,70 @@ static const char _indic_syllable_machin
 	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, 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, 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, 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 _indic_syllable_machine_from_state_actions[] = {
 	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, 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, 0, 0, 0, 0, 
-	0, 6, 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, 9, 
+	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, 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, 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, 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, 
@@ -676,122 +1051,141 @@ static const char _indic_syllable_machin
 	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, 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 _indic_syllable_machine_eof_trans[] = {
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
 	1, 1, 1, 1, 1, 1, 1, 1, 
-	1, 1, 1, 39, 39, 39, 39, 39, 
+	1, 1, 1, 1, 39, 39, 39, 39, 
 	39, 39, 39, 39, 39, 39, 39, 39, 
 	39, 39, 39, 39, 39, 39, 39, 39, 
 	39, 39, 39, 39, 39, 39, 39, 39, 
-	39, 39, 39, 39, 39, 39, 77, 77, 
-	77, 77, 77, 77, 77, 77, 77, 77, 
+	39, 39, 39, 39, 39, 39, 39, 39, 
+	77, 77, 77, 83, 83, 77, 77, 77, 
 	77, 77, 77, 77, 77, 77, 77, 77, 
 	77, 77, 77, 77, 77, 77, 77, 77, 
 	77, 77, 77, 77, 77, 77, 77, 77, 
-	77, 0, 121, 121, 121, 121, 121, 121, 
-	121, 121, 121, 121, 121, 121, 121, 121, 
-	121, 121, 121, 121, 121, 121, 121, 121, 
-	121, 121, 121, 121, 121, 121, 121, 121, 
-	121, 121, 121, 121, 121, 121, 121, 121, 
-	121, 121, 121, 121, 121, 121, 121, 121, 
-	121, 121, 121, 121, 121, 121, 121, 121, 
-	121, 121, 121, 121, 121, 179, 179, 179, 
-	179, 179, 179, 179, 179, 179, 179, 179, 
-	179, 179, 179, 179, 179, 179, 179, 179, 
-	179, 179, 179, 179, 179, 179, 179, 179, 
-	179, 179, 179, 179, 179, 179, 179, 179, 
-	179, 179, 179, 179, 179, 179, 179, 179, 
-	179, 179, 179, 179, 179, 179, 179, 179, 
-	179, 179, 179, 179, 179, 179, 179, 179, 
-	237, 237, 237, 237, 237, 237, 237, 237, 
-	237, 237, 237, 237, 237, 237, 237, 237, 
-	237, 237, 237, 237, 237, 237, 237, 237, 
-	237, 237, 237, 237, 237, 237, 237, 237, 
-	237, 237, 237, 237, 237, 237, 237, 237, 
-	237, 237, 237, 237, 237, 237, 237, 237, 
-	237, 237, 237, 237, 237, 237, 237, 237, 
-	237, 237, 294, 121, 121
+	77, 77, 77, 115, 115, 115, 115, 115, 
+	115, 115, 115, 115, 115, 115, 115, 115, 
+	115, 115, 115, 115, 115, 115, 115, 115, 
+	115, 115, 115, 115, 115, 115, 115, 115, 
+	115, 115, 115, 115, 115, 115, 115, 0, 
+	167, 167, 167, 167, 167, 167, 167, 167, 
+	167, 167, 167, 167, 167, 167, 167, 167, 
+	167, 167, 167, 167, 167, 167, 167, 167, 
+	167, 167, 167, 167, 167, 167, 167, 167, 
+	167, 167, 167, 167, 167, 167, 167, 167, 
+	167, 167, 167, 167, 167, 167, 167, 167, 
+	167, 167, 167, 167, 167, 167, 167, 167, 
+	167, 167, 167, 167, 167, 167, 167, 167, 
+	167, 167, 167, 167, 167, 167, 167, 167, 
+	167, 167, 167, 242, 242, 242, 242, 242, 
+	242, 242, 242, 242, 242, 242, 242, 242, 
+	242, 242, 242, 242, 242, 242, 242, 242, 
+	242, 242, 242, 242, 242, 242, 242, 242, 
+	242, 242, 242, 242, 242, 242, 242, 242, 
+	242, 242, 242, 242, 242, 242, 242, 242, 
+	242, 242, 242, 242, 242, 242, 242, 242, 
+	242, 242, 242, 242, 242, 242, 242, 242, 
+	242, 242, 242, 242, 242, 242, 242, 242, 
+	242, 242, 242, 242, 242, 316, 316, 316, 
+	316, 316, 316, 316, 316, 316, 316, 316, 
+	316, 316, 316, 316, 316, 316, 316, 316, 
+	316, 316, 316, 316, 316, 316, 316, 316, 
+	316, 316, 316, 316, 316, 316, 316, 316, 
+	316, 316, 316, 316, 316, 316, 316, 316, 
+	316, 316, 316, 316, 316, 316, 316, 316, 
+	316, 316, 316, 316, 316, 316, 316, 316, 
+	316, 316, 316, 316, 316, 316, 316, 316, 
+	316, 316, 316, 316, 383, 316, 383, 384, 
+	384, 384, 384, 384, 384, 384, 384, 384, 
+	384, 384, 384, 384, 384, 384, 384, 384, 
+	384, 384, 384, 384, 384, 384, 384, 384, 
+	384, 384, 384, 384, 384, 384, 384, 384, 
+	384, 384, 384, 384, 384, 384, 384, 384, 
+	384, 384, 384, 384, 384, 384, 384, 384, 
+	384, 384, 384, 384, 384, 384, 384, 384, 
+	384, 384, 384, 384, 384, 384, 384, 384, 
+	384, 384, 384, 384, 384, 384, 384, 384, 
+	383, 167, 167
 };
 
-static const int indic_syllable_machine_start = 105;
-static const int indic_syllable_machine_first_final = 105;
+static const int indic_syllable_machine_start = 143;
+static const int indic_syllable_machine_first_final = 143;
 static const int indic_syllable_machine_error = -1;
 
-static const int indic_syllable_machine_en_main = 105;
+static const int indic_syllable_machine_en_main = 143;
 
 
 #line 36 "../../src/hb-ot-shape-complex-indic-machine.rl"
 
 
 
-#line 88 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 90 "../../src/hb-ot-shape-complex-indic-machine.rl"
 
 
-#define process_syllable(func) \
+#define found_syllable(syllable_type) \
   HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #func); \
+    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; \
-    PASTE (initial_reordering_, func) (plan, buffer, last, p+1); \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
     last = p+1; \
     syllable_serial++; \
-    if (unlikely (!syllable_serial)) syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
 
 static void
-find_syllables (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer)
+find_syllables (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts, te, act;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   
-#line 759 "hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 1153 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
 	{
 	cs = indic_syllable_machine_start;
 	ts = 0;
 	te = 0;
 	act = 0;
 	}
 
-#line 110 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 111 "../../src/hb-ot-shape-complex-indic-machine.rl"
 
 
   p = 0;
   pe = eof = buffer->len;
 
   unsigned int last = 0;
-  uint8_t syllable_serial = 1;
+  unsigned int syllable_serial = 1;
   
-#line 776 "hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 1170 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
 	{
 	int _slen;
 	int _trans;
 	const unsigned char *_keys;
 	const short *_inds;
 	if ( p == pe )
 		goto _test_eof;
 _resume:
 	switch ( _indic_syllable_machine_from_state_actions[cs] ) {
-	case 6:
-#line 1 "NONE"
+	case 9:
+#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{ts = p;}
 	break;
-#line 790 "hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 1184 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
 	}
 
 	_keys = _indic_syllable_machine_trans_keys + (cs<<1);
 	_inds = _indic_syllable_machine_indicies + _indic_syllable_machine_index_offsets[cs];
 
 	_slen = _indic_syllable_machine_key_spans[cs];
 	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].indic_category()) &&
 		( info[p].indic_category()) <= _keys[1] ?
@@ -800,85 +1194,121 @@ find_syllables (const hb_ot_shape_plan_t
 _eof_trans:
 	cs = _indic_syllable_machine_trans_targs[_trans];
 
 	if ( _indic_syllable_machine_trans_actions[_trans] == 0 )
 		goto _again;
 
 	switch ( _indic_syllable_machine_trans_actions[_trans] ) {
 	case 2:
-#line 1 "NONE"
+#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{te = p+1;}
 	break;
-	case 9:
-#line 81 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{te = p+1;{ process_syllable (consonant_syllable); }}
+	case 13:
+#line 82 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ found_syllable (consonant_syllable); }}
+	break;
+	case 15:
+#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ found_syllable (vowel_syllable); }}
 	break;
-	case 11:
+	case 20:
+#line 84 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ found_syllable (standalone_cluster); }}
+	break;
+	case 17:
+#line 85 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ found_syllable (broken_cluster); }}
+	break;
+	case 10:
+#line 86 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;{ found_syllable (non_indic_cluster); }}
+	break;
+	case 12:
 #line 82 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{te = p+1;{ process_syllable (vowel_syllable); }}
+	{te = p;p--;{ found_syllable (consonant_syllable); }}
+	break;
+	case 14:
+#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (vowel_syllable); }}
+	break;
+	case 19:
+#line 84 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (standalone_cluster); }}
 	break;
-	case 13:
+	case 16:
+#line 85 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (broken_cluster); }}
+	break;
+	case 18:
+#line 86 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p;p--;{ found_syllable (non_indic_cluster); }}
+	break;
+	case 1:
+#line 82 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (consonant_syllable); }}
+	break;
+	case 3:
 #line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{te = p+1;{ process_syllable (standalone_cluster); }}
+	{{p = ((te))-1;}{ found_syllable (vowel_syllable); }}
 	break;
 	case 7:
 #line 84 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{te = p+1;{ process_syllable (non_indic); }}
-	break;
-	case 8:
-#line 81 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ process_syllable (consonant_syllable); }}
-	break;
-	case 10:
-#line 82 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ process_syllable (vowel_syllable); }}
-	break;
-	case 12:
-#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ process_syllable (standalone_cluster); }}
-	break;
-	case 14:
-#line 84 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{te = p;p--;{ process_syllable (non_indic); }}
-	break;
-	case 1:
-#line 81 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{{p = ((te))-1;}{ process_syllable (consonant_syllable); }}
-	break;
-	case 3:
-#line 82 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{{p = ((te))-1;}{ process_syllable (vowel_syllable); }}
+	{{p = ((te))-1;}{ found_syllable (standalone_cluster); }}
 	break;
 	case 4:
-#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
-	{{p = ((te))-1;}{ process_syllable (standalone_cluster); }}
+#line 85 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
+	break;
+	case 5:
+#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{	switch( act ) {
+	case 4:
+	{{p = ((te))-1;} found_syllable (broken_cluster); }
+	break;
+	case 5:
+	{{p = ((te))-1;} found_syllable (non_indic_cluster); }
 	break;
-#line 856 "hb-ot-shape-complex-indic-machine.hh.tmp"
+	}
+	}
+	break;
+	case 6:
+#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;}
+#line 85 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{act = 4;}
+	break;
+	case 11:
+#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{te = p+1;}
+#line 86 "../../src/hb-ot-shape-complex-indic-machine.rl"
+	{act = 5;}
+	break;
+#line 1286 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
 	}
 
 _again:
 	switch ( _indic_syllable_machine_to_state_actions[cs] ) {
-	case 5:
-#line 1 "NONE"
+	case 8:
+#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
 	{ts = 0;}
 	break;
-#line 865 "hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 1295 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
 	}
 
 	if ( ++p != pe )
 		goto _resume;
 	_test_eof: {}
 	if ( p == eof )
 	{
 	if ( _indic_syllable_machine_eof_trans[cs] > 0 ) {
 		_trans = _indic_syllable_machine_eof_trans[cs] - 1;
 		goto _eof_trans;
 	}
 	}
 
 	}
 
-#line 119 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 120 "../../src/hb-ot-shape-complex-indic-machine.rl"
 
 }
 
 #endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-machine.rl
@@ -57,66 +57,67 @@ Repha = 15;
 Ra    = 16;
 
 c = (C | Ra);			# is_consonant
 n = ((ZWNJ?.RS)? (N.N?)?);	# is_consonant_modifier
 z = ZWJ|ZWNJ;			# is_joiner
 h = H | Coeng;			# is_halant_or_coeng
 reph = (Ra H | Repha);		# possible reph
 
-cn = c.n?;
+cn = c.ZWJ?.n?;
 forced_rakar = ZWJ H ZWJ Ra;
 matra_group = z{0,3}.M.N?.(H | forced_rakar)?;
-syllable_tail = (SM.ZWNJ?)? (Coeng (cn|V))? (VD VD?)?;
+syllable_tail =  (Coeng (cn|V))? (SM.ZWNJ?)? (VD VD?)?;
 place_holder = NBSP | DOTTEDCIRCLE;
-halant_group = (z?.h.ZWJ?);
+halant_group = (z?.h.(ZWJ.N?)?);
 final_halant_group = halant_group | h.ZWNJ;
-halant_or_matra_group = (final_halant_group | matra_group{0,4});
+halant_or_matra_group = (final_halant_group | (h.ZWJ)? matra_group{0,4});
 
 
 consonant_syllable =	Repha? (cn.halant_group){0,4} cn A? halant_or_matra_group? syllable_tail;
 vowel_syllable =	reph? V.n? (ZWJ | (halant_group.cn){0,4} halant_or_matra_group? syllable_tail);
 standalone_cluster =	reph? place_holder.n? (halant_group.cn){0,4} halant_or_matra_group? syllable_tail;
+broken_cluster =	n? (halant_group.cn){0,4} halant_or_matra_group syllable_tail;
 other =			any;
 
 main := |*
-	consonant_syllable	=> { process_syllable (consonant_syllable); };
-	vowel_syllable		=> { process_syllable (vowel_syllable); };
-	standalone_cluster	=> { process_syllable (standalone_cluster); };
-	other			=> { process_syllable (non_indic); };
+	consonant_syllable	=> { found_syllable (consonant_syllable); };
+	vowel_syllable		=> { found_syllable (vowel_syllable); };
+	standalone_cluster	=> { found_syllable (standalone_cluster); };
+	broken_cluster		=> { found_syllable (broken_cluster); };
+	other			=> { found_syllable (non_indic_cluster); };
 *|;
 
 
 }%%
 
-#define process_syllable(func) \
+#define found_syllable(syllable_type) \
   HB_STMT_START { \
-    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #func); \
+    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; \
-    PASTE (initial_reordering_, func) (plan, buffer, last, p+1); \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
     last = p+1; \
     syllable_serial++; \
-    if (unlikely (!syllable_serial)) syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
   } HB_STMT_END
 
 static void
-find_syllables (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer)
+find_syllables (hb_buffer_t *buffer)
 {
   unsigned int p, pe, eof, ts, te, act;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   %%{
     write init;
     getkey info[p].indic_category();
   }%%
 
   p = 0;
   pe = eof = buffer->len;
 
   unsigned int last = 0;
-  uint8_t syllable_serial = 1;
+  unsigned int syllable_serial = 1;
   %%{
     write exec;
   }%%
 }
 
 #endif /* HB_OT_SHAPE_COMPLEX_INDIC_MACHINE_HH */
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic-private.hh
@@ -295,17 +295,17 @@ is_consonant (const hb_glyph_info_t &inf
 #define HALANT_OR_COENG_FLAGS (FLAG (OT_H) | FLAG (OT_Coeng))
 static inline bool
 is_halant_or_coeng (const hb_glyph_info_t &info)
 {
   return is_one_of (info, HALANT_OR_COENG_FLAGS);
 }
 
 static inline void
-set_indic_properties (hb_glyph_info_t   &info)
+set_indic_properties (hb_glyph_info_t &info)
 {
   hb_codepoint_t u = info.codepoint;
   unsigned int type = get_indic_categories (u);
   indic_category_t cat = (indic_category_t) (type & 0x0F);
   indic_position_t pos = (indic_position_t) (type >> 4);
 
 
   /*
--- a/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-complex-indic.cc
@@ -198,29 +198,36 @@ enum {
   _ABVM,
   _BLWM,
 
   INDIC_NUM_FEATURES,
   INDIC_BASIC_FEATURES = INIT /* Don't forget to update this! */
 };
 
 static void
+setup_syllables (const hb_ot_shape_plan_t *plan,
+		 hb_font_t *font,
+		 hb_buffer_t *buffer);
+static void
 initial_reordering (const hb_ot_shape_plan_t *plan,
 		    hb_font_t *font,
 		    hb_buffer_t *buffer);
 static void
 final_reordering (const hb_ot_shape_plan_t *plan,
 		  hb_font_t *font,
 		  hb_buffer_t *buffer);
 
 static void
 collect_features_indic (hb_ot_shape_planner_t *plan)
 {
   hb_ot_map_builder_t *map = &plan->map;
 
+  /* Do this before any lookups have been applied. */
+  map->add_gsub_pause (setup_syllables);
+
   map->add_bool_feature (HB_TAG('l','o','c','l'));
   /* 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_bool_feature (HB_TAG('c','c','m','p'));
 
 
   unsigned int i = 0;
   map->add_gsub_pause (initial_reordering);
@@ -249,20 +256,21 @@ struct would_substitute_feature_t
   {
     map->get_stage_lookups (0/*GSUB*/,
 			    map->get_feature_stage (0/*GSUB*/, feature_tag),
 			    &lookups, &count);
   }
 
   inline bool would_substitute (hb_codepoint_t    *glyphs,
 				unsigned int       glyphs_count,
+				bool               zero_context,
 				hb_face_t         *face) const
   {
     for (unsigned int i = 0; i < count; i++)
-      if (hb_ot_layout_would_substitute_lookup_fast (face, glyphs, glyphs_count, lookups[i].index))
+      if (hb_ot_layout_would_substitute_lookup_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context))
 	return true;
     return false;
   }
 
   private:
   const hb_ot_map_t::lookup_map_t *lookups;
   unsigned int count;
 };
@@ -335,39 +343,59 @@ data_destroy_indic (void *data)
   free (data);
 }
 
 static indic_position_t
 consonant_position_from_face (const indic_shape_plan_t *indic_plan,
 			      hb_codepoint_t *glyphs, unsigned int glyphs_len,
 			      hb_face_t      *face)
 {
-  if (indic_plan->pref.would_substitute (glyphs, glyphs_len, face)) return POS_BELOW_C;
-  if (indic_plan->blwf.would_substitute (glyphs, glyphs_len, face)) return POS_BELOW_C;
-  if (indic_plan->pstf.would_substitute (glyphs, glyphs_len, face)) return POS_POST_C;
+  bool zero_context = indic_plan->is_old_spec ? false : true;
+  if (indic_plan->pref.would_substitute (glyphs, glyphs_len, zero_context, face)) return POS_BELOW_C;
+  if (indic_plan->blwf.would_substitute (glyphs, glyphs_len, zero_context, face)) return POS_BELOW_C;
+  if (indic_plan->pstf.would_substitute (glyphs, glyphs_len, zero_context, face)) return POS_POST_C;
   return POS_BASE_C;
 }
 
 
+enum syllable_type_t {
+  consonant_syllable,
+  vowel_syllable,
+  standalone_cluster,
+  broken_cluster,
+  non_indic_cluster,
+};
+
+#include "hb-ot-shape-complex-indic-machine.hh"
+
+
 static void
 setup_masks_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
 		   hb_buffer_t              *buffer,
 		   hb_font_t                *font HB_UNUSED)
 {
   HB_BUFFER_ALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_ALLOCATE_VAR (buffer, indic_position);
 
   /* We cannot setup masks here.  We save information about characters
    * and setup masks later on in a pause-callback. */
 
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
     set_indic_properties (buffer->info[i]);
 }
 
+static void
+setup_syllables (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		 hb_font_t *font HB_UNUSED,
+		 hb_buffer_t *buffer)
+{
+  find_syllables (buffer);
+}
+
 static int
 compare_indic_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 {
   int a = pa->indic_position();
   int b = pb->indic_position();
 
   return a < b ? -1 : a == b ? 0 : +1;
 }
@@ -440,17 +468,17 @@ initial_reordering_consonant_syllable (c
     {
       limit += 2;
       while (limit < end && is_joiner (info[limit]))
         limit++;
       base = start;
       has_reph = true;
     };
 
-    switch (indic_plan->config->base_pos == BASE_POS_LAST)
+    switch (indic_plan->config->base_pos)
     {
       case BASE_POS_LAST:
       {
 	/* -> starting from the end of the syllable, move backwards */
 	unsigned int i = end;
 	bool seen_below = false;
 	do {
 	  i--;
@@ -689,23 +717,22 @@ initial_reordering_consonant_syllable (c
     if (base < end)
       info[base].mask |= mask;
     /* Post-base */
     mask = indic_plan->mask_array[BLWF] | indic_plan->mask_array[ABVF] | indic_plan->mask_array[PSTF];
     for (unsigned int i = base + 1; i < end; i++)
       info[i].mask  |= mask;
   }
 
-  /* XXX This will not match for old-Indic spec since the Halant-Ra order is reversed already. */
   if (indic_plan->mask_array[PREF] && base + 2 < end)
   {
     /* Find a Halant,Ra sequence and mark it for pre-base reordering processing. */
     for (unsigned int i = base + 1; i + 1 < end; i++)
-      if (is_halant_or_coeng (info[i]) &&
-	  info[i + 1].indic_category() == OT_Ra)
+      if (is_halant_or_coeng (info[i + (indic_plan->is_old_spec ? 1 : 0)]) &&
+	  info[i + (indic_plan->is_old_spec ? 0 : 1)].indic_category() == OT_Ra)
       {
 	info[i++].mask |= indic_plan->mask_array[PREF];
 	info[i++].mask |= indic_plan->mask_array[PREF];
 
 	/* Mark the subsequent stuff with 'cfar'.  Used in Khmer.
 	 * Read the feature spec.
 	 * This allows distinguishing the following cases with MS Khmer fonts:
 	 * U+1784,U+17D2,U+179A,U+17D2,U+1782
@@ -764,33 +791,118 @@ initial_reordering_standalone_cluster (c
     if (buffer->info[end - 1].indic_category() == OT_DOTTEDCIRCLE)
       return;
   }
 
   initial_reordering_consonant_syllable (plan, buffer, start, end);
 }
 
 static void
-initial_reordering_non_indic (const hb_ot_shape_plan_t *plan HB_UNUSED,
-			      hb_buffer_t *buffer HB_UNUSED,
-			      unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
+initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan,
+				   hb_buffer_t *buffer,
+				   unsigned int start, unsigned int end)
+{
+  /* We already inserted dotted-circles, so just call the standalone_cluster. */
+  initial_reordering_standalone_cluster (plan, buffer, start, end);
+}
+
+static void
+initial_reordering_non_indic_cluster (const hb_ot_shape_plan_t *plan 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. */
 }
 
-#include "hb-ot-shape-complex-indic-machine.hh"
+
+static void
+initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
+			     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, buffer, start, end); return;
+  case vowel_syllable:		initial_reordering_vowel_syllable     (plan, buffer, start, end); return;
+  case standalone_cluster:	initial_reordering_standalone_cluster (plan, buffer, start, end); return;
+  case broken_cluster:		initial_reordering_broken_cluster     (plan, buffer, start, end); return;
+  case non_indic_cluster:	initial_reordering_non_indic_cluster  (plan, buffer, start, end); return;
+  }
+}
+
+static inline void
+insert_dotted_circles (const hb_ot_shape_plan_t *plan,
+		       hb_font_t *font,
+		       hb_buffer_t *buffer)
+{
+  /* Note: This loop is extra overhead, but should not be measurable. */
+  bool has_broken_syllables = false;
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    if ((buffer->info[i].syllable() & 0x0F) == broken_cluster) {
+      has_broken_syllables = true;
+      break;
+    }
+  if (likely (!has_broken_syllables))
+    return;
+
+
+  hb_codepoint_t dottedcircle_glyph;
+  if (!font->get_glyph (0x25CC, 0, &dottedcircle_glyph))
+    return;
+
+  hb_glyph_info_t dottedcircle;
+  dottedcircle.codepoint = 0x25CC;
+  set_indic_properties (dottedcircle);
+  dottedcircle.codepoint = dottedcircle_glyph;
+
+  buffer->clear_output ();
+
+  buffer->idx = 0;
+  unsigned int last_syllable = 0;
+  while (buffer->idx < buffer->len)
+  {
+    unsigned int syllable = buffer->cur().syllable();
+    syllable_type_t syllable_type = (syllable_type_t) (syllable & 0x0F);
+    if (unlikely (last_syllable != syllable && syllable_type == broken_cluster))
+    {
+      hb_glyph_info_t info = dottedcircle;
+      info.cluster = buffer->cur().cluster;
+      info.mask = buffer->cur().mask;
+      info.syllable() = buffer->cur().syllable();
+      buffer->output_info (info);
+      last_syllable = syllable;
+    }
+    buffer->next_glyph ();
+  }
+
+  buffer->swap_buffers ();
+}
 
 static void
 initial_reordering (const hb_ot_shape_plan_t *plan,
 		    hb_font_t *font,
 		    hb_buffer_t *buffer)
 {
   update_consonant_positions (plan, font, buffer);
-  find_syllables (plan, buffer);
+  insert_dotted_circles (plan, font, buffer);
+
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+  if (unlikely (!count)) return;
+  unsigned int last = 0;
+  unsigned int last_syllable = info[0].syllable();
+  for (unsigned int i = 1; i < count; i++)
+    if (last_syllable != info[i].syllable()) {
+      initial_reordering_syllable (plan, buffer, last, i);
+      last = i;
+      last_syllable = info[last].syllable();
+    }
+  initial_reordering_syllable (plan, buffer, last, count);
 }
 
 static void
 final_reordering_syllable (const hb_ot_shape_plan_t *plan,
 			   hb_buffer_t *buffer,
 			   unsigned int start, unsigned int end)
 {
   const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
@@ -824,21 +936,21 @@ final_reordering_syllable (const hb_ot_s
    *     halant, position is moved after it.
    */
 
   if (start + 1 < end && start < base) /* Otherwise there can't be any pre-base matra characters. */
   {
     /* If we lost track of base, alas, position before last thingy. */
     unsigned int new_pos = base == end ? base - 2 : base - 1;
 
-    /* Malayalam does not have "half" forms or explicit virama forms.
-     * The glyphs formed by 'half' are Chillus.  We want to position
-     * matra after them all.
+    /* Malayalam / Tamil do not have "half" forms or explicit virama forms.
+     * The glyphs formed by 'half' are Chillus or ligated explicit viramas.
+     * We want to position matra after them.
      */
-    if (buffer->props.script != HB_SCRIPT_MALAYALAM)
+    if (buffer->props.script != HB_SCRIPT_MALAYALAM && buffer->props.script != HB_SCRIPT_TAMIL)
     {
       while (new_pos > start &&
 	     !(is_one_of (info[new_pos], (FLAG (OT_M) | FLAG (OT_H) | FLAG (OT_Coeng)))))
 	new_pos--;
 
       /* If we found no Halant we are done.
        * Otherwise only proceed if the Halant does
        * not belong to the Matra itself! */
@@ -848,17 +960,17 @@ final_reordering_syllable (const hb_ot_s
 	/* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
 	if (new_pos + 1 < end && is_joiner (info[new_pos + 1]))
 	  new_pos++;
       }
       else
         new_pos = start; /* No move. */
     }
 
-    if (start < new_pos)
+    if (start < new_pos && info[new_pos].indic_position () != POS_PRE_M)
     {
       /* Now go see if there's actually any matras... */
       for (unsigned int i = new_pos; i > start; i--)
 	if (info[i - 1].indic_position () == POS_PRE_M)
 	{
 	  unsigned int old_pos = i - 1;
 	  hb_glyph_info_t tmp = info[old_pos];
 	  memmove (&info[old_pos], &info[old_pos + 1], (new_pos - old_pos) * sizeof (info[0]));
@@ -1104,38 +1216,48 @@ final_reordering_syllable (const hb_ot_s
 
 
 static void
 final_reordering (const hb_ot_shape_plan_t *plan,
 		  hb_font_t *font,
 		  hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
-  if (!count) return;
+  if (unlikely (!count)) return;
 
   hb_glyph_info_t *info = buffer->info;
   unsigned int last = 0;
   unsigned int last_syllable = info[0].syllable();
   for (unsigned int i = 1; i < count; i++)
     if (last_syllable != info[i].syllable()) {
       final_reordering_syllable (plan, buffer, last, i);
       last = i;
       last_syllable = info[last].syllable();
     }
   final_reordering_syllable (plan, buffer, last, count);
 
+  /* Zero syllables now... */
+  for (unsigned int i = 0; i < count; i++)
+    info[i].syllable() = 0;
+
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_category);
   HB_BUFFER_DEALLOCATE_VAR (buffer, indic_position);
 }
 
 
+static hb_ot_shape_normalization_mode_t
+normalization_preference_indic (const hb_ot_shape_plan_t *plan)
+{
+  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
+}
+
 const hb_ot_complex_shaper_t _hb_ot_complex_shaper_indic =
 {
   "indic",
   collect_features_indic,
   override_features_indic,
   data_create_indic,
   data_destroy_indic,
   NULL, /* preprocess_text */
-  NULL, /* normalization_preference */
+  normalization_preference_indic,
   setup_masks_indic,
   false, /* zero_width_attached_marks */
 };
--- a/gfx/harfbuzz/src/hb-ot-shape-fallback-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-fallback-private.hh
@@ -31,9 +31,14 @@
 
 #include "hb-ot-shape-private.hh"
 
 
 HB_INTERNAL void _hb_ot_shape_fallback_position (const hb_ot_shape_plan_t *plan,
 						 hb_font_t *font,
 						 hb_buffer_t  *buffer);
 
+HB_INTERNAL void _hb_ot_shape_fallback_position_recategorize_marks (const hb_ot_shape_plan_t *plan,
+								    hb_font_t *font,
+								    hb_buffer_t  *buffer);
+
+
 #endif /* HB_OT_SHAPE_FALLBACK_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-ot-shape-fallback.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-fallback.cc
@@ -21,38 +21,65 @@
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-ot-shape-fallback-private.hh"
 
-static void
-zero_mark_advances (hb_buffer_t *buffer,
-		    unsigned int start,
-		    unsigned int end)
+static unsigned int
+recategorize_combining_class (hb_codepoint_t u,
+			      unsigned int klass)
 {
-  for (unsigned int i = start; i < end; i++)
-    if (_hb_glyph_info_get_general_category (&buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
+  if (klass >= 200)
+    return klass;
+
+  /* Thai / Lao need some per-character work. */
+  if ((u & ~0xFF) == 0x0E00)
+  {
+    if (unlikely (klass == 0))
     {
-      buffer->pos[i].x_advance = 0;
-      buffer->pos[i].y_advance = 0;
+      switch (u)
+      {
+        case 0x0E31:
+        case 0x0E34:
+        case 0x0E35:
+        case 0x0E36:
+        case 0x0E37:
+        case 0x0E47:
+        case 0x0E4C:
+        case 0x0E4D:
+        case 0x0E4E:
+	  klass = HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT;
+	  break;
+
+        case 0x0EB1:
+        case 0x0EB4:
+        case 0x0EB5:
+        case 0x0EB6:
+        case 0x0EB7:
+        case 0x0EBB:
+        case 0x0ECC:
+        case 0x0ECD:
+	  klass = HB_UNICODE_COMBINING_CLASS_ABOVE;
+	  break;
+
+        case 0x0EBC:
+	  klass = HB_UNICODE_COMBINING_CLASS_BELOW;
+	  break;
+      }
+    } else {
+      /* Thai virama is below-right */
+      if (u == 0x0E3A)
+	klass = HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT;
     }
-}
+  }
 
-static unsigned int
-recategorize_combining_class (unsigned int modified_combining_class)
-{
-  if (modified_combining_class >= 200)
-    return modified_combining_class;
-
-  /* This should be kept in sync with modified combining class mapping
-   * from hb-unicode.cc. */
-  switch (modified_combining_class)
+  switch (klass)
   {
 
     /* Hebrew */
 
     case HB_MODIFIED_COMBINING_CLASS_CCC10: /* sheva */
     case HB_MODIFIED_COMBINING_CLASS_CCC11: /* hataf segol */
     case HB_MODIFIED_COMBINING_CLASS_CCC12: /* hataf patah */
     case HB_MODIFIED_COMBINING_CLASS_CCC13: /* hataf qamats */
@@ -96,49 +123,74 @@ recategorize_combining_class (unsigned i
 
     case HB_MODIFIED_COMBINING_CLASS_CCC29: /* kasratan */
     case HB_MODIFIED_COMBINING_CLASS_CCC32: /* kasra */
       return HB_UNICODE_COMBINING_CLASS_BELOW;
 
 
     /* Thai */
 
-    /* Note: to be useful we also need to position U+0E3A that has ccc=9 (virama).
-     * But viramas can be both above and below based on the codepoint / script. */
-
     case HB_MODIFIED_COMBINING_CLASS_CCC103: /* sara u / sara uu */
-      return HB_UNICODE_COMBINING_CLASS_BELOW;
+      return HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT;
 
     case HB_MODIFIED_COMBINING_CLASS_CCC107: /* mai */
-      return HB_UNICODE_COMBINING_CLASS_ABOVE;
+      return HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT;
 
 
     /* Lao */
 
     case HB_MODIFIED_COMBINING_CLASS_CCC118: /* sign u / sign uu */
-      return HB_UNICODE_COMBINING_CLASS_BELOW;
+      return HB_UNICODE_COMBINING_CLASS_BELOW_RIGHT;
 
     case HB_MODIFIED_COMBINING_CLASS_CCC122: /* mai */
-      return HB_UNICODE_COMBINING_CLASS_ABOVE;
+      return HB_UNICODE_COMBINING_CLASS_ABOVE_RIGHT;
 
 
     /* Tibetan */
 
     case HB_MODIFIED_COMBINING_CLASS_CCC129: /* sign aa */
       return HB_UNICODE_COMBINING_CLASS_BELOW;
 
     case HB_MODIFIED_COMBINING_CLASS_CCC130: /* sign i*/
       return HB_UNICODE_COMBINING_CLASS_ABOVE;
 
     case HB_MODIFIED_COMBINING_CLASS_CCC132: /* sign u */
       return HB_UNICODE_COMBINING_CLASS_BELOW;
 
   }
 
-  return modified_combining_class;
+  return klass;
+}
+
+void
+_hb_ot_shape_fallback_position_recategorize_marks (const hb_ot_shape_plan_t *plan,
+						   hb_font_t *font,
+						   hb_buffer_t  *buffer)
+{
+  unsigned int count = buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    if (_hb_glyph_info_get_general_category (&buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK) {
+      unsigned int combining_class = _hb_glyph_info_get_modified_combining_class (&buffer->info[i]);
+      combining_class = recategorize_combining_class (buffer->info[i].codepoint, combining_class);
+      _hb_glyph_info_set_modified_combining_class (&buffer->info[i], combining_class);
+    }
+}
+
+
+static void
+zero_mark_advances (hb_buffer_t *buffer,
+		    unsigned int start,
+		    unsigned int end)
+{
+  for (unsigned int i = start; i < end; i++)
+    if (_hb_glyph_info_get_general_category (&buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
+    {
+      buffer->pos[i].x_advance = 0;
+      buffer->pos[i].y_advance = 0;
+    }
 }
 
 static inline void
 position_mark (const hb_ot_shape_plan_t *plan,
 	       hb_font_t *font,
 	       hb_buffer_t  *buffer,
 	       hb_glyph_extents_t &base_extents,
 	       unsigned int i,
@@ -166,16 +218,17 @@ position_mark (const hb_ot_shape_plan_t 
 	pos.x_offset += base_extents.x_bearing - mark_extents.width / 2 - mark_extents.x_bearing;
         break;
       } else if (buffer->props.direction == HB_DIRECTION_RTL) {
 	pos.x_offset += base_extents.x_bearing + base_extents.width - mark_extents.width / 2 - mark_extents.x_bearing;
         break;
       }
       /* Fall through */
 
+    default:
     case HB_UNICODE_COMBINING_CLASS_ATTACHED_BELOW:
     case HB_UNICODE_COMBINING_CLASS_ATTACHED_ABOVE:
     case HB_UNICODE_COMBINING_CLASS_BELOW:
     case HB_UNICODE_COMBINING_CLASS_ABOVE:
       /* Center align. */
       pos.x_offset += base_extents.x_bearing + (base_extents.width - mark_extents.width) / 2 - mark_extents.x_bearing;
       break;
 
@@ -229,87 +282,117 @@ position_mark (const hb_ot_shape_plan_t 
 
 static inline void
 position_around_base (const hb_ot_shape_plan_t *plan,
 		      hb_font_t *font,
 		      hb_buffer_t  *buffer,
 		      unsigned int base,
 		      unsigned int end)
 {
+  hb_direction_t horiz_dir = HB_DIRECTION_INVALID;
   hb_glyph_extents_t base_extents;
   if (!font->get_glyph_extents (buffer->info[base].codepoint,
 				&base_extents))
   {
     /* If extents don't work, zero marks and go home. */
     zero_mark_advances (buffer, base + 1, end);
     return;
   }
   base_extents.x_bearing += buffer->pos[base].x_offset;
   base_extents.y_bearing += buffer->pos[base].y_offset;
 
-  /* XXX Handle ligature component positioning... */
-  HB_UNUSED bool is_ligature = is_a_ligature (buffer->info[base]);
+  unsigned int lig_id = get_lig_id (buffer->info[base]);
+  unsigned int num_lig_components = get_lig_num_comps (buffer->info[base]);
 
   hb_position_t x_offset = 0, y_offset = 0;
   if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) {
     x_offset -= buffer->pos[base].x_advance;
     y_offset -= buffer->pos[base].y_advance;
   }
+
+  hb_glyph_extents_t component_extents = base_extents;
+  unsigned int last_lig_component = (unsigned int) -1;
   unsigned int last_combining_class = 255;
   hb_glyph_extents_t cluster_extents;
   for (unsigned int i = base + 1; i < end; i++)
-    if (_hb_glyph_info_get_general_category (&buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
+    if (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]))
     {
-      unsigned int this_combining_class = recategorize_combining_class (_hb_glyph_info_get_modified_combining_class (&buffer->info[i]));
-      if (this_combining_class != last_combining_class)
-        cluster_extents = base_extents;
+      if (num_lig_components > 1) {
+	unsigned int this_lig_id = get_lig_id (buffer->info[i]);
+	unsigned int this_lig_component = get_lig_comp (buffer->info[i]) - 1;
+	/* Conditions for attaching to the last component. */
+	if (!lig_id || lig_id != this_lig_id || this_lig_component >= num_lig_components)
+	  this_lig_component = num_lig_components - 1;
+	if (last_lig_component != this_lig_component)
+	{
+	  last_lig_component = this_lig_component;
+	  last_combining_class = 255;
+	  component_extents = base_extents;
+	  if (unlikely (horiz_dir == HB_DIRECTION_INVALID)) {
+	    if (HB_DIRECTION_IS_HORIZONTAL (plan->props.direction))
+	      horiz_dir = plan->props.direction;
+	    else
+	      horiz_dir = hb_script_get_horizontal_direction (plan->props.script);
+	  }
+	  if (horiz_dir == HB_DIRECTION_LTR)
+	    component_extents.x_bearing += (this_lig_component * component_extents.width) / num_lig_components;
+	  else
+	    component_extents.x_bearing += ((num_lig_components - 1 - this_lig_component) * component_extents.width) / num_lig_components;
+	  component_extents.width /= num_lig_components;
+	}
+      }
 
-      position_mark (plan, font, buffer, base_extents, i, this_combining_class);
+      unsigned int this_combining_class = _hb_glyph_info_get_modified_combining_class (&buffer->info[i]);
+      if (last_combining_class != this_combining_class)
+      {
+	last_combining_class = this_combining_class;
+        cluster_extents = component_extents;
+      }
+
+      position_mark (plan, font, buffer, cluster_extents, i, this_combining_class);
 
       buffer->pos[i].x_advance = 0;
       buffer->pos[i].y_advance = 0;
       buffer->pos[i].x_offset += x_offset;
       buffer->pos[i].y_offset += y_offset;
 
-      /* combine cluster extents. */
-
-      last_combining_class = this_combining_class;
     } else {
       if (HB_DIRECTION_IS_FORWARD (buffer->props.direction)) {
 	x_offset -= buffer->pos[i].x_advance;
 	y_offset -= buffer->pos[i].y_advance;
       } else {
 	x_offset += buffer->pos[i].x_advance;
 	y_offset += buffer->pos[i].y_advance;
       }
     }
-
-
 }
 
 static inline void
 position_cluster (const hb_ot_shape_plan_t *plan,
 		  hb_font_t *font,
 		  hb_buffer_t  *buffer,
 		  unsigned int start,
 		  unsigned int end)
 {
   if (end - start < 2)
     return;
 
   /* Find the base glyph */
   for (unsigned int i = start; i < end; i++)
-    if (is_a_ligature (buffer->info[i]) ||
-        !(FLAG (_hb_glyph_info_get_general_category (&buffer->info[i])) &
-	  (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
-	   FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
-	   FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))))
+    if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[i])))
     {
-      position_around_base (plan, font, buffer, i, end);
-      break;
+      /* Find mark glyphs */
+      unsigned int j;
+      for (j = i + 1; j < end; j++)
+	if (!HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[j])))
+	  break;
+
+      position_around_base (plan, font, buffer, i, j);
+
+      i = j - 1;
     }
 }
 
 void
 _hb_ot_shape_fallback_position (const hb_ot_shape_plan_t *plan,
 				hb_font_t *font,
 				hb_buffer_t  *buffer)
 {
--- a/gfx/harfbuzz/src/hb-ot-shape-normalize-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-normalize-private.hh
@@ -33,16 +33,17 @@
 #include "hb-buffer.h"
 
 /* buffer var allocations, used during the normalization process */
 #define glyph_index()	var1.u32
 
 enum hb_ot_shape_normalization_mode_t {
   HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED,
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS, /* never composes base-to-base */
+  HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT, /* always fully decomposes and then recompose back */
   HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL, /* including base-to-base composition */
 
   HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT = HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS
 };
 
 HB_INTERNAL void _hb_ot_shape_normalize (hb_font_t *font,
 					 hb_buffer_t *buffer,
 					 hb_ot_shape_normalization_mode_t mode);
--- a/gfx/harfbuzz/src/hb-ot-shape-normalize.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape-normalize.cc
@@ -125,20 +125,17 @@ decompose_func (hb_unicode_funcs_t *unic
 
 static hb_bool_t
 compose_func (hb_unicode_funcs_t *unicode,
 	      hb_codepoint_t  a,
 	      hb_codepoint_t  b,
 	      hb_codepoint_t *ab)
 {
   /* XXX, this belongs to indic normalizer. */
-  if ((FLAG (unicode->general_category (a)) &
-       (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
-	FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
-	FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK))))
+  if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (unicode->general_category (a)))
     return false;
   /* XXX, add composition-exclusion exceptions to Indic shaper. */
   if (a == 0x09AF && b == 0x09BC) { *ab = 0x09DF; return true; }
 
   /* XXX, these belong to the hebew / default shaper. */
   /* Hebrew presentation-form shaping.
    * https://bugzilla.mozilla.org/show_bug.cgi?id=728866 */
   // Hebrew presentation forms with dagesh, for characters 0x05D0..0x05EA;
@@ -409,20 +406,20 @@ decompose_multi_char_cluster (hb_font_t 
   while (buffer->idx < end)
     decompose_current_character (font, buffer, false);
   /* We can be smarter here and only return true if there are at least two ccc!=0 marks.
    * But does not matter. */
   return true;
 }
 
 static inline bool
-decompose_cluster (hb_font_t *font, hb_buffer_t *buffer, bool recompose, unsigned int end)
+decompose_cluster (hb_font_t *font, hb_buffer_t *buffer, bool short_circuit, unsigned int end)
 {
   if (likely (buffer->idx + 1 == end))
-    return decompose_current_character (font, buffer, recompose);
+    return decompose_current_character (font, buffer, short_circuit);
   else
     return decompose_multi_char_cluster (font, buffer, end);
 }
 
 
 static int
 compare_combining_class (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
 {
@@ -432,17 +429,18 @@ compare_combining_class (const hb_glyph_
   return a < b ? -1 : a == b ? 0 : +1;
 }
 
 
 void
 _hb_ot_shape_normalize (hb_font_t *font, hb_buffer_t *buffer,
 			hb_ot_shape_normalization_mode_t mode)
 {
-  bool recompose = mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED;
+  bool short_circuit = mode != HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED &&
+		       mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
   bool can_use_recompose = false;
   unsigned int count;
 
   /* We do a fairly straightforward yet custom normalization process in three
    * separate rounds: decompose, reorder, recompose (if desired).  Currently
    * this makes two buffer swaps.  We can make it faster by moving the last
    * two rounds into the inner loop for the first round, but it's more readable
    * this way. */
@@ -454,17 +452,17 @@ void
   count = buffer->len;
   for (buffer->idx = 0; buffer->idx < count;)
   {
     unsigned int end;
     for (end = buffer->idx + 1; end < count; end++)
       if (buffer->cur().cluster != buffer->info[end].cluster)
         break;
 
-    can_use_recompose = decompose_cluster (font, buffer, recompose, end) || can_use_recompose;
+    can_use_recompose = decompose_cluster (font, buffer, short_circuit, end) || can_use_recompose;
   }
   buffer->swap_buffers ();
 
 
   if (mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL && !can_use_recompose)
     return; /* Done! */
 
 
@@ -490,17 +488,17 @@ void
     }
 
     hb_bubble_sort (buffer->info + i, end - i, compare_combining_class);
 
     i = end;
   }
 
 
-  if (!recompose)
+  if (mode == HB_OT_SHAPE_NORMALIZATION_MODE_DECOMPOSED)
     return;
 
   /* Third round, recompose */
 
   /* As noted in the comment earlier, we don't try to combine
    * ccc=0 chars with their previous Starter. */
 
   buffer->clear_output ();
--- a/gfx/harfbuzz/src/hb-ot-shape-private.hh
+++ b/gfx/harfbuzz/src/hb-ot-shape-private.hh
@@ -90,16 +90,22 @@ inline void
 }
 
 inline hb_unicode_general_category_t
 _hb_glyph_info_get_general_category (const hb_glyph_info_t *info)
 {
   return (hb_unicode_general_category_t) (info->unicode_props0() & 0x7F);
 }
 
+inline void
+_hb_glyph_info_set_modified_combining_class (hb_glyph_info_t *info, unsigned int modified_class)
+{
+  info->unicode_props1() = modified_class;
+}
+
 inline unsigned int
 _hb_glyph_info_get_modified_combining_class (const hb_glyph_info_t *info)
 {
   return info->unicode_props1();
 }
 
 inline hb_bool_t
 _hb_glyph_info_is_zero_width (const hb_glyph_info_t *info)
--- a/gfx/harfbuzz/src/hb-ot-shape.cc
+++ b/gfx/harfbuzz/src/hb-ot-shape.cc
@@ -230,24 +230,50 @@ static void
 hb_set_unicode_props (hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
     _hb_glyph_info_set_unicode_props (&buffer->info[i], buffer->unicode);
 }
 
 static void
+hb_insert_dotted_circle (hb_buffer_t *buffer, hb_font_t *font)
+{
+  if (buffer->context_len[0] ||
+      _hb_glyph_info_get_general_category (&buffer->info[0]) !=
+      HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
+    return;
+
+  hb_codepoint_t dottedcircle_glyph;
+  if (!font->get_glyph (0x25CC, 0, &dottedcircle_glyph))
+    return;
+
+  hb_glyph_info_t dottedcircle;
+  dottedcircle.codepoint = 0x25CC;
+  _hb_glyph_info_set_unicode_props (&dottedcircle, buffer->unicode);
+
+  buffer->clear_output ();
+
+  buffer->idx = 0;
+  hb_glyph_info_t info = dottedcircle;
+  info.cluster = buffer->cur().cluster;
+  info.mask = buffer->cur().mask;
+  buffer->output_info (info);
+  while (buffer->idx < buffer->len)
+    buffer->next_glyph ();
+
+  buffer->swap_buffers ();
+}
+
+static void
 hb_form_clusters (hb_buffer_t *buffer)
 {
   unsigned int count = buffer->len;
   for (unsigned int i = 1; i < count; i++)
-    if (FLAG (_hb_glyph_info_get_general_category (&buffer->info[i])) &
-	(FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) |
-	 FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) |
-	 FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
+    if (HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->info[i])))
       buffer->merge_clusters (i - 1, i + 1);
 }
 
 static void
 hb_ensure_native_direction (hb_buffer_t *buffer)
 {
   hb_direction_t direction = buffer->props.direction;
 
@@ -312,58 +338,60 @@ hb_ot_map_glyphs_fast (hb_buffer_t  *buf
 {
   /* Normalization process sets up glyph_index(), we just copy it. */
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
     buffer->info[i].codepoint = buffer->info[i].glyph_index();
 }
 
 static inline void
+hb_synthesize_glyph_classes (hb_ot_shape_context_t *c)
+{
+  unsigned int count = c->buffer->len;
+  for (unsigned int i = 0; i < count; i++)
+    c->buffer->info[i].glyph_props() = _hb_glyph_info_get_general_category (&c->buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK ?
+				       HB_OT_LAYOUT_GLYPH_CLASS_MARK :
+				       HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
+}
+
+static inline void
 hb_ot_substitute_default (hb_ot_shape_context_t *c)
 {
   if (c->plan->shaper->preprocess_text)
     c->plan->shaper->preprocess_text (c->plan, c->buffer, c->font);
 
   hb_ot_mirror_chars (c);
 
   HB_BUFFER_ALLOCATE_VAR (c->buffer, glyph_index);
 
   _hb_ot_shape_normalize (c->font, c->buffer,
 			  c->plan->shaper->normalization_preference ?
 			  c->plan->shaper->normalization_preference (c->plan) :
 			  HB_OT_SHAPE_NORMALIZATION_MODE_DEFAULT);
 
   hb_ot_shape_setup_masks (c);
 
+  /* This is unfortunate to go here, but necessary... */
+  if (!hb_ot_layout_has_positioning (c->face))
+    _hb_ot_shape_fallback_position_recategorize_marks (c->plan, c->font, c->buffer);
+
   hb_ot_map_glyphs_fast (c->buffer);
 
   HB_BUFFER_DEALLOCATE_VAR (c->buffer, glyph_index);
 }
 
 static inline void
-hb_synthesize_glyph_classes (hb_ot_shape_context_t *c)
-{
-  unsigned int count = c->buffer->len;
-  for (unsigned int i = 0; i < count; i++)
-    c->buffer->info[i].glyph_props() = _hb_glyph_info_get_general_category (&c->buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK ?
-				       HB_OT_LAYOUT_GLYPH_CLASS_MARK :
-				       HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH;
-}
-
-
-static inline void
 hb_ot_substitute_complex (hb_ot_shape_context_t *c)
 {
   hb_ot_layout_substitute_start (c->font, c->buffer);
 
   if (!hb_ot_layout_has_glyph_classes (c->face))
     hb_synthesize_glyph_classes (c);
 
-  if (hb_ot_layout_has_substitution (c->face))
-    c->plan->substitute (c->font, c->buffer);
+  c->plan->substitute (c->font, c->buffer);
 
   hb_ot_layout_substitute_finish (c->font, c->buffer);
 
   return;
 }
 
 static inline void
 hb_ot_substitute (hb_ot_shape_context_t *c)
@@ -481,24 +509,28 @@ hb_ot_position (hb_ot_shape_context_t *c
 }
 
 
 /* Post-process */
 
 static void
 hb_ot_hide_zerowidth (hb_ot_shape_context_t *c)
 {
-  hb_codepoint_t space;
-  if (!c->font->get_glyph (' ', 0, &space))
-    return; /* No point! */
+  hb_codepoint_t space = 0;
 
   unsigned int count = c->buffer->len;
   for (unsigned int i = 0; i < count; i++)
     if (unlikely (!is_a_ligature (c->buffer->info[i]) &&
-		  _hb_glyph_info_is_zero_width (&c->buffer->info[i]))) {
+		  _hb_glyph_info_is_zero_width (&c->buffer->info[i])))
+    {
+      if (!space) {
+        /* We assume that the space glyph is not gid0. */
+        if (unlikely (!c->font->get_glyph (' ', 0, &space)) || !space)
+	return; /* No point! */
+      }
       c->buffer->info[i].codepoint = space;
       c->buffer->pos[i].x_advance = 0;
       c->buffer->pos[i].y_advance = 0;
     }
 }
 
 
 /* Pull it all together! */
@@ -512,16 +544,17 @@ hb_ot_shape_internal (hb_ot_shape_contex
   c->target_direction = c->buffer->props.direction;
 
   HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props0);
   HB_BUFFER_ALLOCATE_VAR (c->buffer, unicode_props1);
 
   c->buffer->clear_output ();
 
   hb_set_unicode_props (c->buffer);
+  hb_insert_dotted_circle (c->buffer, c->font);
   hb_form_clusters (c->buffer);
 
   hb_ensure_native_direction (c->buffer);
 
   hb_ot_substitute (c);
   hb_ot_position (c);
 
   hb_ot_hide_zerowidth (c);
@@ -551,17 +584,17 @@ hb_bool_t
 
 
 static inline void
 hb_ot_map_glyphs_dumb (hb_font_t    *font,
 		       hb_buffer_t  *buffer)
 {
   unsigned int count = buffer->len;
   for (unsigned int i = 0; i < count; i++)
-    font->get_glyph (buffer->cur().codepoint, 0, &buffer->cur().codepoint);
+    font->get_glyph (buffer->info[i].codepoint, 0, &buffer->info[i].codepoint);
 }
 
 void
 hb_ot_shape_glyphs_closure (hb_font_t          *font,
 			    hb_buffer_t        *buffer,
 			    const hb_feature_t *features,
 			    unsigned int        num_features,
 			    hb_set_t           *glyphs)
--- a/gfx/harfbuzz/src/hb-private.hh
+++ b/gfx/harfbuzz/src/hb-private.hh
@@ -72,16 +72,18 @@ static inline Type MIN (const Type &a, c
 #undef MAX
 template <typename Type>
 static inline Type MAX (const Type &a, const Type &b) { return a > b ? a : b; }
 
 
 #undef  ARRAY_LENGTH
 template <typename Type, unsigned int n>
 static inline unsigned int ARRAY_LENGTH (const Type (&a)[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
 #define HB_STMT_END   while (0)
 
 #define _ASSERT_STATIC1(_line, _cond)	typedef int _static_assert_on_line_##_line##_failed[(_cond)?1:-1]
 #define _ASSERT_STATIC0(_line, _cond)	_ASSERT_STATIC1 (_line, (_cond))
 #define ASSERT_STATIC(_cond)		_ASSERT_STATIC0 (__LINE__, (_cond))
 
@@ -539,17 +541,17 @@ static inline bool
 	   unsigned int max_level)
 {
   return level < max_level;
 }
 
 #define DEBUG_LEVEL(WHAT, LEVEL) (_hb_debug ((LEVEL), HB_DEBUG_##WHAT))
 #define DEBUG(WHAT) (DEBUG_LEVEL (WHAT, 0))
 
-template <int max_level> inline void
+template <int max_level> static inline void
 _hb_debug_msg_va (const char *what,
 		  const void *obj,
 		  const char *func,
 		  bool indented,
 		  unsigned int level,
 		  int level_dir,
 		  const char *message,
 		  va_list ap)
@@ -576,22 +578,23 @@ template <int max_level> inline void
     fprintf (stderr, "%2d %s" VRBAR "%s",
 	     level,
 	     bars + sizeof (bars) - 1 - MIN ((unsigned int) sizeof (bars), (unsigned int) (sizeof (VBAR) - 1) * level),
 	     level_dir ? (level_dir > 0 ? DLBAR : ULBAR) : LBAR);
   } else
     fprintf (stderr, "   " VRBAR LBAR);
 
   if (func) {
-    /* If there's a class name, just write that. */
-    const char *dotdot = strstr (func, "::");
+    /* Skip return type */
     const char *space = strchr (func, ' ');
-    if (space && dotdot && space < dotdot)
+    if (space)
       func = space + 1;
-    unsigned int func_len = dotdot ? dotdot - func : strlen (func);
+    /* Skip parameter list */
+    const char *paren = strchr (func, '(');
+    unsigned int func_len = paren ? paren - func : strlen (func);
     fprintf (stderr, "%.*s: ", func_len, func);
   }
 
   if (message)
     vfprintf (stderr, message, ap);
 
   fprintf (stderr, "\n");
 }
@@ -600,26 +603,26 @@ template <> inline void
 		     const void *obj HB_UNUSED,
 		     const char *func HB_UNUSED,
 		     bool indented HB_UNUSED,
 		     unsigned int level HB_UNUSED,
 		     int level_dir HB_UNUSED,
 		     const char *message HB_UNUSED,
 		     va_list ap HB_UNUSED) {}
 
-template <int max_level> inline void
+template <int max_level> static inline void
 _hb_debug_msg (const char *what,
 	       const void *obj,
 	       const char *func,
 	       bool indented,
 	       unsigned int level,
 	       int level_dir,
 	       const char *message,
 	       ...) HB_PRINTF_FUNC(7, 8);
-template <int max_level> inline void
+template <int max_level> static inline void
 _hb_debug_msg (const char *what,
 	       const void *obj,
 	       const char *func,
 	       bool indented,
 	       unsigned int level,
 	       int level_dir,
 	       const char *message,
 	       ...)
--- a/gfx/harfbuzz/src/hb-shape.cc
+++ b/gfx/harfbuzz/src/hb-shape.cc
@@ -1,10 +1,11 @@
 /*
  * Copyright © 2009  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -17,26 +18,193 @@
  *
  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #include "hb-private.hh"
 
 #include "hb-shaper-private.hh"
 #include "hb-shape-plan-private.hh"
 #include "hb-buffer-private.hh"
 #include "hb-font-private.hh"
 
 
+static void
+parse_space (const char **pp, const char *end)
+{
+  char c;
+#define ISSPACE(c) ((c)==' '||(c)=='\f'||(c)=='\n'||(c)=='\r'||(c)=='\t'||(c)=='\v')
+  while (*pp < end && (c = **pp, ISSPACE (c)))
+    (*pp)++;
+#undef ISSPACE
+}
+
+static hb_bool_t
+parse_char (const char **pp, const char *end, char c)
+{
+  parse_space (pp, end);
+
+  if (*pp == end || **pp != c)
+    return false;
+
+  (*pp)++;
+  return true;
+}
+
+static hb_bool_t
+parse_uint (const char **pp, const char *end, unsigned int *pv)
+{
+  char buf[32];
+  strncpy (buf, *pp, end - *pp);
+  buf[ARRAY_LENGTH (buf) - 1] = '\0';
+
+  char *p = buf;
+  char *pend = p;
+  unsigned int v;
+
+  v = strtol (p, &pend, 0);
+
+  if (p == pend)
+    return false;
+
+  *pv = v;
+  *pp += pend - p;
+  return true;
+}
+
+static hb_bool_t
+parse_feature_value_prefix (const char **pp, const char *end, hb_feature_t *feature)
+{
+  if (parse_char (pp, end, '-'))
+    feature->value = 0;
+  else {
+    parse_char (pp, end, '+');
+    feature->value = 1;
+  }
+
+  return true;
+}
+
+static hb_bool_t
+parse_feature_tag (const char **pp, const char *end, hb_feature_t *feature)
+{
+  const char *p = *pp;
+  char c;
+
+  parse_space (pp, end);
+
+#define ISALNUM(c) (('a' <= (c) && (c) <= 'z') || ('A' <= (c) && (c) <= 'Z') || ('0' <= (c) && (c) <= '9'))
+  while (*pp < end && (c = **pp, ISALNUM(c)))
+    (*pp)++;
+#undef ISALNUM
+
+  if (p == *pp)
+    return false;
+
+  feature->tag = hb_tag_from_string (p, *pp - p);
+  return true;
+}
+
+static hb_bool_t
+parse_feature_indices (const char **pp, const char *end, hb_feature_t *feature)
+{
+  parse_space (pp, end);
+
+  hb_bool_t has_start;
+
+  feature->start = 0;
+  feature->end = (unsigned int) -1;
+
+  if (!parse_char (pp, end, '['))
+    return true;
+
+  has_start = parse_uint (pp, end, &feature->start);
+
+  if (parse_char (pp, end, ':')) {
+    parse_uint (pp, end, &feature->end);
+  } else {
+    if (has_start)
+      feature->end = feature->start + 1;
+  }
+
+  return parse_char (pp, end, ']');
+}
+
+static hb_bool_t
+parse_feature_value_postfix (const char **pp, const char *end, hb_feature_t *feature)
+{
+  return !parse_char (pp, end, '=') || parse_uint (pp, end, &feature->value);
+}
+
+
+static hb_bool_t
+parse_one_feature (const char **pp, const char *end, hb_feature_t *feature)
+{
+  return parse_feature_value_prefix (pp, end, feature) &&
+	 parse_feature_tag (pp, end, feature) &&
+	 parse_feature_indices (pp, end, feature) &&
+	 parse_feature_value_postfix (pp, end, feature) &&
+	 *pp == end;
+}
+
+hb_bool_t
+hb_feature_from_string (const char *str, int len,
+			hb_feature_t *feature)
+{
+  if (len < 0)
+    len = strlen (str);
+
+  return parse_one_feature (&str, str + len, feature);
+}
+
+void
+hb_feature_to_string (hb_feature_t *feature,
+		      char *buf, unsigned int size)
+{
+  if (unlikely (!size)) return;
+
+  char s[128];
+  unsigned int len = 0;
+  if (feature->value == 0)
+    s[len++] = '-';
+  hb_tag_to_string (feature->tag, s + len);
+  len += 4;
+  while (len && s[len - 1] == ' ')
+    len--;
+  if (feature->start != 0 || feature->start != (unsigned int) -1)
+  {
+    s[len++] = '[';
+    if (feature->start)
+      len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->start);
+    if (feature->end != feature->start + 1) {
+      s[len++] = ':';
+      if (feature->end != (unsigned int) -1)
+	len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->end);
+    }
+    s[len++] = ']';
+  }
+  if (feature->value > 1)
+  {
+    s[len++] = '=';
+    len += snprintf (s + len, ARRAY_LENGTH (s) - len, "%d", feature->value);
+  }
+  assert (len < ARRAY_LENGTH (s));
+  len = MIN (len, size - 1);
+  memcpy (buf, s, len);
+  s[len] = '\0';
+}
+
+
 static const char **static_shaper_list;
 
 static
 void free_static_shaper_list (void)
 {
   free (static_shaper_list);
 }
 
@@ -80,21 +248,26 @@ hb_shape_full (hb_font_t          *font,
 	       hb_buffer_t        *buffer,
 	       const hb_feature_t *features,
 	       unsigned int        num_features,
 	       const char * const *shaper_list)
 {
   if (unlikely (!buffer->len))
     return true;
 
+  assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
+
   buffer->guess_properties ();
 
   hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props, features, num_features, shaper_list);
   hb_bool_t res = hb_shape_plan_execute (shape_plan, font, buffer, features, num_features);
   hb_shape_plan_destroy (shape_plan);
+
+  if (res)
+    buffer->content_type = HB_BUFFER_CONTENT_TYPE_GLYPHS;
   return res;
 }
 
 void
 hb_shape (hb_font_t           *font,
 	  hb_buffer_t         *buffer,
 	  const hb_feature_t  *features,
 	  unsigned int         num_features)
--- a/gfx/harfbuzz/src/hb-shape.h
+++ b/gfx/harfbuzz/src/hb-shape.h
@@ -1,10 +1,11 @@
 /*
  * Copyright © 2009  Red Hat, Inc.
+ * Copyright © 2012  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
  * Permission is hereby granted, without written agreement and without
  * license or royalty fees, to use, copy, modify, and distribute this
  * software and its documentation for any purpose, provided that the
  * above copyright notice and the following two paragraphs appear in
  * all copies of this software.
@@ -17,16 +18,17 @@
  *
  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  *
  * Red Hat Author(s): Behdad Esfahbod
+ * Google Author(s): Behdad Esfahbod
  */
 
 #ifndef HB_H_IN
 #error "Include <hb.h> instead."
 #endif
 
 #ifndef HB_SHAPE_H
 #define HB_SHAPE_H
@@ -40,16 +42,27 @@ HB_BEGIN_DECLS
 
 typedef struct hb_feature_t {
   hb_tag_t      tag;
   uint32_t      value;
   unsigned int  start;
   unsigned int  end;
 } hb_feature_t;
 
+/* len=-1 means str is NUL-terminated */
+hb_bool_t
+hb_feature_from_string (const char *str, int len,
+			hb_feature_t *feature);
+
+/* something like 128 bytes is more than enough.
+ * nul-terminates. */
+void
+hb_feature_to_string (hb_feature_t *feature,
+		      char *buf, unsigned int size);
+
 
 void
 hb_shape (hb_font_t           *font,
 	  hb_buffer_t         *buffer,
 	  const hb_feature_t  *features,
 	  unsigned int         num_features);
 
 hb_bool_t
--- a/gfx/harfbuzz/src/hb-shaper-list.hh
+++ b/gfx/harfbuzz/src/hb-shaper-list.hh
@@ -42,9 +42,13 @@ HB_SHAPER_IMPLEMENT (coretext)
 #ifdef HAVE_OT
 HB_SHAPER_IMPLEMENT (ot) /* <--- This is our main OpenType shaper. */
 #endif
 
 #ifdef HAVE_HB_OLD
 HB_SHAPER_IMPLEMENT (old)
 #endif
 
+#ifdef HAVE_ICU_LE
+HB_SHAPER_IMPLEMENT (icu_le)
+#endif
+
 HB_SHAPER_IMPLEMENT (fallback) /* <--- This should be last. */
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-ucdn.cc
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2012 Grigori Goronzy <greg@kinoho.net>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "hb-private.hh"
+
+#include "hb-unicode-private.hh"
+
+#include "ucdn.h"
+
+static const hb_script_t ucdn_script_translate[] =
+{
+    HB_SCRIPT_COMMON,
+    HB_SCRIPT_LATIN,
+    HB_SCRIPT_GREEK,
+    HB_SCRIPT_CYRILLIC,
+    HB_SCRIPT_ARMENIAN,
+    HB_SCRIPT_HEBREW,
+    HB_SCRIPT_ARABIC,
+    HB_SCRIPT_SYRIAC,
+    HB_SCRIPT_THAANA,
+    HB_SCRIPT_DEVANAGARI,
+    HB_SCRIPT_BENGALI,
+    HB_SCRIPT_GURMUKHI,
+    HB_SCRIPT_GUJARATI,
+    HB_SCRIPT_ORIYA,
+    HB_SCRIPT_TAMIL,
+    HB_SCRIPT_TELUGU,
+    HB_SCRIPT_KANNADA,
+    HB_SCRIPT_MALAYALAM,
+    HB_SCRIPT_SINHALA,
+    HB_SCRIPT_THAI,
+    HB_SCRIPT_LAO,
+    HB_SCRIPT_TIBETAN,
+    HB_SCRIPT_MYANMAR,
+    HB_SCRIPT_GEORGIAN,
+    HB_SCRIPT_HANGUL,
+    HB_SCRIPT_ETHIOPIC,
+    HB_SCRIPT_CHEROKEE,
+    HB_SCRIPT_CANADIAN_ABORIGINAL,
+    HB_SCRIPT_OGHAM,
+    HB_SCRIPT_RUNIC,
+    HB_SCRIPT_KHMER,
+    HB_SCRIPT_MONGOLIAN,
+    HB_SCRIPT_HIRAGANA,
+    HB_SCRIPT_KATAKANA,
+    HB_SCRIPT_BOPOMOFO,
+    HB_SCRIPT_HAN,
+    HB_SCRIPT_YI,
+    HB_SCRIPT_OLD_ITALIC,
+    HB_SCRIPT_GOTHIC,
+    HB_SCRIPT_DESERET,
+    HB_SCRIPT_INHERITED,
+    HB_SCRIPT_TAGALOG,
+    HB_SCRIPT_HANUNOO,
+    HB_SCRIPT_BUHID,
+    HB_SCRIPT_TAGBANWA,
+    HB_SCRIPT_LIMBU,
+    HB_SCRIPT_TAI_LE,
+    HB_SCRIPT_LINEAR_B,
+    HB_SCRIPT_UGARITIC,
+    HB_SCRIPT_SHAVIAN,
+    HB_SCRIPT_OSMANYA,
+    HB_SCRIPT_CYPRIOT,
+    HB_SCRIPT_BRAILLE,
+    HB_SCRIPT_BUGINESE,
+    HB_SCRIPT_COPTIC,
+    HB_SCRIPT_NEW_TAI_LUE,
+    HB_SCRIPT_GLAGOLITIC,
+    HB_SCRIPT_TIFINAGH,
+    HB_SCRIPT_SYLOTI_NAGRI,
+    HB_SCRIPT_OLD_PERSIAN,
+    HB_SCRIPT_KHAROSHTHI,
+    HB_SCRIPT_BALINESE,
+    HB_SCRIPT_CUNEIFORM,
+    HB_SCRIPT_PHOENICIAN,
+    HB_SCRIPT_PHAGS_PA,
+    HB_SCRIPT_NKO,
+    HB_SCRIPT_SUNDANESE,
+    HB_SCRIPT_LEPCHA,
+    HB_SCRIPT_OL_CHIKI,
+    HB_SCRIPT_VAI,
+    HB_SCRIPT_SAURASHTRA,
+    HB_SCRIPT_KAYAH_LI,
+    HB_SCRIPT_REJANG,
+    HB_SCRIPT_LYCIAN,
+    HB_SCRIPT_CARIAN,
+    HB_SCRIPT_LYDIAN,
+    HB_SCRIPT_CHAM,
+    HB_SCRIPT_TAI_THAM,
+    HB_SCRIPT_TAI_VIET,
+    HB_SCRIPT_AVESTAN,
+    HB_SCRIPT_EGYPTIAN_HIEROGLYPHS,
+    HB_SCRIPT_SAMARITAN,
+    HB_SCRIPT_LISU,
+    HB_SCRIPT_BAMUM,
+    HB_SCRIPT_JAVANESE,
+    HB_SCRIPT_MEETEI_MAYEK,
+    HB_SCRIPT_IMPERIAL_ARAMAIC,
+    HB_SCRIPT_OLD_SOUTH_ARABIAN,
+    HB_SCRIPT_INSCRIPTIONAL_PARTHIAN,
+    HB_SCRIPT_INSCRIPTIONAL_PAHLAVI,
+    HB_SCRIPT_OLD_TURKIC,
+    HB_SCRIPT_KAITHI,
+    HB_SCRIPT_BATAK,
+    HB_SCRIPT_BRAHMI,
+    HB_SCRIPT_MANDAIC,
+    HB_SCRIPT_CHAKMA,
+    HB_SCRIPT_MEROITIC_CURSIVE,
+    HB_SCRIPT_MEROITIC_HIEROGLYPHS,
+    HB_SCRIPT_MIAO,
+    HB_SCRIPT_SHARADA,
+    HB_SCRIPT_SORA_SOMPENG,
+    HB_SCRIPT_TAKRI,
+    HB_SCRIPT_UNKNOWN,
+};
+
+static hb_unicode_combining_class_t
+hb_ucdn_combining_class(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
+        void *user_data)
+{
+    return (hb_unicode_combining_class_t) ucdn_get_combining_class(unicode);
+}
+
+static unsigned int
+hb_ucdn_eastasian_width(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
+        void *user_data)
+{
+    int w = ucdn_get_east_asian_width(unicode);
+    return (w == UCDN_EAST_ASIAN_F || w == UCDN_EAST_ASIAN_W) ? 2 : 1;
+}
+
+static hb_unicode_general_category_t
+hb_ucdn_general_category(hb_unicode_funcs_t *ufuncs,
+        hb_codepoint_t unicode, void *user_data)
+{
+    return (hb_unicode_general_category_t)ucdn_get_general_category(unicode);
+}
+
+static hb_codepoint_t
+hb_ucdn_mirroring(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
+        void *user_data)
+{
+    return ucdn_mirror(unicode);
+}
+
+static hb_script_t
+hb_ucdn_script(hb_unicode_funcs_t *ufuncs, hb_codepoint_t unicode,
+        void *user_data)
+{
+    return ucdn_script_translate[ucdn_get_script(unicode)];
+}
+
+static hb_bool_t
+hb_ucdn_compose(hb_unicode_funcs_t *ufuncs, hb_codepoint_t a,
+        hb_codepoint_t b, hb_codepoint_t *ab, void *user_data)
+{
+    return ucdn_compose(ab, a, b);
+}
+
+static hb_bool_t
+hb_ucdn_decompose(hb_unicode_funcs_t *ufuncs, hb_codepoint_t ab,
+        hb_codepoint_t *a, hb_codepoint_t *b, void *user_data)
+{
+    return ucdn_decompose(ab, a, b);
+}
+
+static unsigned int
+hb_ucdn_decompose_compatibility(hb_unicode_funcs_t *ufuncs, hb_codepoint_t u,
+        hb_codepoint_t *decomposed, void *user_data)
+{
+    return ucdn_compat_decompose(u, decomposed);
+}
+
+extern "C" HB_INTERNAL
+hb_unicode_funcs_t *
+hb_ucdn_get_unicode_funcs (void)
+{
+  static const hb_unicode_funcs_t _hb_ucdn_unicode_funcs = {
+    HB_OBJECT_HEADER_STATIC,
+
+    NULL, /* parent */
+    true, /* immutable */
+    {
+#define HB_UNICODE_FUNC_IMPLEMENT(name) hb_ucdn_##name,
+      HB_UNICODE_FUNCS_IMPLEMENT_CALLBACKS
+#undef HB_UNICODE_FUNC_IMPLEMENT
+    }
+  };
+
+  return const_cast<hb_unicode_funcs_t *> (&_hb_ucdn_unicode_funcs);
+}
+
--- a/gfx/harfbuzz/src/hb-unicode-private.hh
+++ b/gfx/harfbuzz/src/hb-unicode-private.hh
@@ -263,9 +263,18 @@ extern HB_INTERNAL const hb_unicode_func
 #define HB_MODIFIED_COMBINING_CLASS_CCC122 122 /* mai * */
 
 /* Tibetan */
 #define HB_MODIFIED_COMBINING_CLASS_CCC129 129 /* sign aa */
 #define HB_MODIFIED_COMBINING_CLASS_CCC130 130 /* sign i */
 #define HB_MODIFIED_COMBINING_CLASS_CCC132 132 /* sign u */
 
 
+/* Misc */
+
+#define HB_UNICODE_GENERAL_CATEGORY_IS_MARK(gen_cat) \
+	(FLAG (gen_cat) & \
+	 (FLAG (HB_UNICODE_GENERAL_CATEGORY_SPACING_MARK) | \
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_ENCLOSING_MARK) | \
+	  FLAG (HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)))
+
+
 #endif /* HB_UNICODE_PRIVATE_HH */
--- a/gfx/harfbuzz/src/hb-unicode.cc
+++ b/gfx/harfbuzz/src/hb-unicode.cc
@@ -107,16 +107,17 @@ hb_unicode_decompose_compatibility_nil (
 {
   return 0;
 }
 
 
 #define HB_UNICODE_FUNCS_IMPLEMENT_SET \
   HB_UNICODE_FUNCS_IMPLEMENT (glib) \
   HB_UNICODE_FUNCS_IMPLEMENT (icu) \
+  HB_UNICODE_FUNCS_IMPLEMENT (ucdn) \
   HB_UNICODE_FUNCS_IMPLEMENT (nil) \
   /* ^--- Add new callbacks before nil */
 
 #define hb_nil_get_unicode_funcs hb_unicode_funcs_get_empty
 
 /* Prototype them all */
 #define HB_UNICODE_FUNCS_IMPLEMENT(set) \
 extern "C" hb_unicode_funcs_t *hb_##set##_get_unicode_funcs (void);
@@ -129,16 +130,18 @@ hb_unicode_funcs_get_default (void)
 {
 #define HB_UNICODE_FUNCS_IMPLEMENT(set) \
   return hb_##set##_get_unicode_funcs ();
 
 #ifdef HAVE_GLIB
   HB_UNICODE_FUNCS_IMPLEMENT(glib)
 #elif defined(HAVE_ICU)
   HB_UNICODE_FUNCS_IMPLEMENT(icu)
+#elif defined(HAVE_UCDN)
+  HB_UNICODE_FUNCS_IMPLEMENT(ucdn)
 #else
 #define HB_UNICODE_FUNCS_NIL 1
   HB_UNICODE_FUNCS_IMPLEMENT(nil)
 #endif
 
 #undef HB_UNICODE_FUNCS_IMPLEMENT
 }
 
--- a/gfx/harfbuzz/src/hb-uniscribe.cc
+++ b/gfx/harfbuzz/src/hb-uniscribe.cc
@@ -28,18 +28,16 @@
 #define WIN32_LEAN_AND_MEAN
 
 #define HB_SHAPER uniscribe
 #include "hb-shaper-impl-private.hh"
 
 #include <windows.h>
 #include <usp10.h>
 
-typedef ULONG WIN_ULONG;
-
 #include "hb-uniscribe.h"
 
 #include "hb-ot-name-table.hh"
 #include "hb-ot-tag.h"
 
 
 #ifndef HB_DEBUG_UNISCRIBE
 #define HB_DEBUG_UNISCRIBE (HB_DEBUG+0)
@@ -116,18 +114,18 @@ struct hb_uniscribe_shaper_font_data_t {
 static bool
 populate_log_font (LOGFONTW  *lf,
 		   hb_font_t *font)
 {
   memset (lf, 0, sizeof (*lf));
   lf->lfHeight = -font->y_scale;
   lf->lfCharSet = DEFAULT_CHARSET;
 
-  hb_blob_t *blob = Sanitizer<name>::sanitize (hb_face_reference_table (font->face, HB_TAG ('n','a','m','e')));
-  const name *name_table = Sanitizer<name>::lock_instance (blob);
+  hb_blob_t *blob = OT::Sanitizer<OT::name>::sanitize (hb_face_reference_table (font->face, HB_TAG ('n','a','m','e')));
+  const OT::name *name_table = OT::Sanitizer<OT::name>::lock_instance (blob);
   unsigned int len = name_table->get_name (3, 1, 0x409, 4,
 					   lf->lfFaceName,
 					   sizeof (lf->lfFaceName[0]) * LF_FACESIZE)
 					  / sizeof (lf->lfFaceName[0]);
   hb_blob_destroy (blob);
 
   if (unlikely (!len)) {
     DEBUG_MSG (UNISCRIBE, NULL, "Didn't find English name table entry");
@@ -300,17 +298,17 @@ retry:
 
 #undef ALLOCATE_ARRAY
 
 #define MAX_ITEMS 256
 
   SCRIPT_ITEM items[MAX_ITEMS + 1];
   SCRIPT_CONTROL bidi_control = {0};
   SCRIPT_STATE bidi_state = {0};
-  WIN_ULONG script_tags[MAX_ITEMS];
+  ULONG script_tags[MAX_ITEMS];
   int item_count;
 
   /* 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;
new file mode 100644
--- /dev/null
+++ b/gfx/harfbuzz/src/hb-utf-private.hh
@@ -0,0 +1,204 @@
+/*
+ * Copyright © 2011,2012  Google, Inc.
+ *
+ *  This is part of HarfBuzz, a text shaping library.
+ *
+ * Permission is hereby granted, without written agreement and without
+ * license or royalty fees, to use, copy, modify, and distribute this
+ * software and its documentation for any purpose, provided that the
+ * above copyright notice and the following two paragraphs appear in
+ * all copies of this software.
+ *
+ * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
+ * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
+ * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
+ * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
+ * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
+ *
+ * Google Author(s): Behdad Esfahbod
+ */
+
+#ifndef HB_UTF_PRIVATE_HH
+#define HB_UTF_PRIVATE_HH
+
+#include "hb-private.hh"
+
+
+/* UTF-8 */
+
+#define HB_UTF8_COMPUTE(Char, Mask, Len) \
+  if (Char < 128) { Len = 1; Mask = 0x7f; } \
+  else if ((Char & 0xe0) == 0xc0) { Len = 2; Mask = 0x1f; } \
+  else if ((Char & 0xf0) == 0xe0) { Len = 3; Mask = 0x0f; } \
+  else if ((Char & 0xf8) == 0xf0) { Len = 4; Mask = 0x07; } \
+  else Len = 0;
+
+static inline const uint8_t *
+hb_utf_next (const uint8_t *text,
+	     const uint8_t *end,
+	     hb_codepoint_t *unicode)
+{
+  hb_codepoint_t c = *text, mask;
+  unsigned int len;
+
+  /* TODO check for overlong sequences? */
+
+  HB_UTF8_COMPUTE (c, mask, len);
+  if (unlikely (!len || (unsigned int) (end - text) < len)) {
+    *unicode = -1;
+    return text + 1;
+  } else {
+    hb_codepoint_t result;
+    unsigned int i;
+    result = c & mask;
+    for (i = 1; i < len; i++)
+      {
+	if (unlikely ((text[i] & 0xc0) != 0x80))
+	  {
+	    *unicode = -1;
+	    return text + 1;
+	  }
+	result <<= 6;
+	result |= (text[i] & 0x3f);
+      }
+    *unicode = result;
+    return text + len;
+  }
+}
+
+static inline const uint8_t *
+hb_utf_prev (const uint8_t *text,
+	     const uint8_t *start,
+	     hb_codepoint_t *unicode)
+{
+  const uint8_t *end = text;
+  while (start < text && (*--text & 0xc0) == 0x80 && end - text < 4)
+    text--;
+
+  hb_codepoint_t c = *text, mask;
+  unsigned int len;
+
+  /* TODO check for overlong sequences? */
+
+  HB_UTF8_COMPUTE (c, mask, len);
+  if (unlikely (!len || (unsigned int) (end - text) != len)) {
+    *unicode = -1;
+    return end - 1;
+  } else {
+    hb_codepoint_t result;
+    unsigned int i;
+    result = c & mask;
+    for (i = 1; i < len; i++)
+      {
+	result <<= 6;
+	result |= (text[i] & 0x3f);
+      }
+    *unicode = result;
+    return text;
+  }
+}
+
+
+static inline unsigned int
+hb_utf_strlen (const uint8_t *text)
+{
+  return strlen ((const char *) text);
+}
+
+
+/* UTF-16 */
+
+static inline const uint16_t *
+hb_utf_next (const uint16_t *text,
+	     const uint16_t *end,
+	     hb_codepoint_t *unicode)
+{
+  hb_codepoint_t c = *text++;
+
+  if (unlikely (hb_in_range<hb_codepoint_t> (c, 0xd800, 0xdbff)))
+  {
+    /* high surrogate */
+    hb_codepoint_t l;
+    if (text < end && ((l = *text), likely (hb_in_range<hb_codepoint_t> (l, 0xdc00, 0xdfff))))
+    {
+      /* low surrogate */
+      *unicode = (c << 10) + l - ((0xd800 << 10) - 0x10000 + 0xdc00);
+       text++;
+    } else
+      *unicode = -1;
+  } else
+    *unicode = c;
+
+  return text;
+}
+
+static inline const uint16_t *
+hb_utf_prev (const uint16_t *text,
+	     const uint16_t *start,
+	     hb_codepoint_t *unicode)
+{
+  hb_codepoint_t c = *--text;
+
+  if (unlikely (hb_in_range<hb_codepoint_t> (c, 0xdc00, 0xdfff)))
+  {
+    /* low surrogate */
+    hb_codepoint_t h;
+    if (start < text && ((h = *(text - 1)), likely (hb_in_range<hb_codepoint_t> (h, 0xd800, 0xdbff))))
+    {
+      /* high surrogate */
+      *unicode = (h << 10) + c - ((0xd800 << 10) - 0x10000 + 0xdc00);
+       text--;
+    } else
+      *unicode = -1;
+  } else
+    *unicode = c;
+
+  return text;
+}
+
+
+static inline unsigned int
+hb_utf_strlen (const uint16_t *text)
+{
+  unsigned int l = 0;
+  while (*text++) l++;
+  return l;
+}
+
+
+/* UTF-32 */
+
+static inline const uint32_t *
+hb_utf_next (const uint32_t *text,
+	     const uint32_t *end,
+	     hb_codepoint_t *unicode)
+{
+  *unicode = *text++;
+  return text;
+}
+
+static inline const uint32_t *
+hb_utf_prev (const uint32_t *text,
+	     const uint32_t *start,
+	     hb_codepoint_t *unicode)
+{
+  *unicode = *--text;
+  return text;
+}
+
+static inline unsigned int
+hb_utf_strlen (const uint32_t *text)
+{
+  unsigned int l = 0;
+  while (*text++) l++;
+  return l;
+}
+
+
+#endif /* HB_UTF_PRIVATE_HH */
--- 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 0
+#define HB_VERSION_MICRO 4
 
-#define HB_VERSION_STRING "0.9.0"
+#define HB_VERSION_STRING "0.9.4"
 
 #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,
--- a/gfx/harfbuzz/src/main.cc
+++ b/gfx/harfbuzz/src/main.cc
@@ -31,16 +31,18 @@
 
 #ifdef HAVE_GLIB
 #include <glib.h>
 #endif
 #include <stdlib.h>
 #include <stdio.h>
 
 
+using namespace OT;
+
 
 int
 main (int argc, char **argv)
 {
   if (argc != 2) {
     fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]);
     exit (1);
   }
--- a/gfx/harfbuzz/src/test-would-substitute.cc
+++ b/gfx/harfbuzz/src/test-would-substitute.cc
@@ -94,10 +94,10 @@ main (int argc, char **argv)
 #endif
 
   unsigned int len = argc - 3;
   hb_codepoint_t glyphs[2];
   if (!hb_font_glyph_from_string (font, argv[3], -1, &glyphs[0]) ||
       (argc > 4 &&
        !hb_font_glyph_from_string (font, argv[4], -1, &glyphs[1])))
     return 2;
-  return !hb_ot_layout_would_substitute_lookup (face, glyphs, len, strtol (argv[2], NULL, 0));
+  return !hb_ot_layout_would_substitute_lookup (face, strtol (argv[2], NULL, 0), glyphs, len, false);
 }