Merge "Merge from upstream harfbuzz"
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..a7285ae
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,14 @@
+language: cpp
+compiler:
+  - clang
+  - gcc
+install:
+  - sudo apt-get install pkg-config ragel   # for autogen.sh
+  - sudo apt-get install libfreetype6-dev   # for font functions
+  - sudo apt-get install libglib2.0-dev     # for font functions / tests / utils
+  - sudo apt-get install libcairo2-dev      # for utils
+  - sudo apt-get install libicu-dev         # for extra unicode functions
+script: ./autogen.sh && make && make check
+notifications:
+  irc: "irc.freenode.org#harfbuzz"
+  email: harfbuzz@lists.freedesktop.org
diff --git a/Android.mk b/Android.mk
index 832d55e..3ada5f2 100644
--- a/Android.mk
+++ b/Android.mk
@@ -28,6 +28,7 @@
 
 LOCAL_SRC_FILES:= \
 	src/hb-blob.cc \
+	src/hb-buffer-serialize.cc \
 	src/hb-buffer.cc \
 	src/hb-common.cc \
 	src/hb-fallback-shape.cc \
@@ -46,6 +47,9 @@
 	src/hb-ot-shape-complex-arabic.cc \
 	src/hb-ot-shape-complex-default.cc \
 	src/hb-ot-shape-complex-indic.cc \
+	src/hb-ot-shape-complex-indic-table.cc \
+	src/hb-ot-shape-complex-myanmar.cc \
+	src/hb-ot-shape-complex-sea.cc \
 	src/hb-ot-shape-complex-thai.cc \
 	src/hb-ot-shape-normalize.cc \
 	src/hb-ot-shape-fallback.cc \
diff --git a/Makefile.am b/Makefile.am
index d718126..19c2095 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -16,20 +16,12 @@
 	$(NULL)
 
 MAINTAINERCLEANFILES = \
+	$(GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL) \
+	$(GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL) \
+	$(GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN) \
 	$(srcdir)/INSTALL \
-	$(srcdir)/aclocal.m4 \
-	$(srcdir)/autoscan.log \
-	$(srcdir)/compile \
-	$(srcdir)/config.guess \
-	$(srcdir)/config.sub \
-	$(srcdir)/configure.scan \
-	$(srcdir)/depcomp \
-	$(srcdir)/install-sh \
-	$(srcdir)/ltmain.sh \
-	$(srcdir)/missing \
-	$(srcdir)/mkinstalldirs \
 	$(srcdir)/ChangeLog \
-	`find "$(srcdir)" -type f -name Makefile.in -print`
+	$(NULL)
 
 
 #
diff --git a/NEWS b/NEWS
index ef643f0..858a916 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,67 @@
+Overview of changes leading to 0.9.14
+Thursday, March 21, 2013
+=====================================
+
+- Build fixes.
+- Fix time-consuming sanitize with malicious fonts.
+- Implement hb_buffer_deserialize_glyphs() for both json and text.
+- Do not ignore Hangul filler characters.
+- Indic fixes:
+  * Fix Malayalam pre-base reordering interaction with post-forms.
+  * Further adjust ZWJ handling.  Should fix known regressions from
+    0.9.13.
+
+
+Overview of changes leading to 0.9.13
+Thursday, February 25, 2013
+=====================================
+
+- Build fixes.
+- Ngapi HarfBuzz Hackfest in London (February 2013):
+  * Fixed all known Indic bugs,
+  * New Win8-style Myanmar shaper,
+  * New South-East Asian shaper for Tai Tham, Cham, and New Tai Lue,
+  * Smartly ignore Default_Ignorable characters (joiners, etc) wheb
+    matching GSUB/GPOS lookups,
+  * Fix 'Phags-Pa U+A872 shaping,
+  * Fix partial disabling of default-on features,
+  * Allow disabling of TrueType kerning.
+- Fix possible crasher with broken fonts with overlapping tables.
+- Removed generated files from git again.  So, one needs ragel to
+  bootstrap from the git tree.
+
+API changes:
+- hb_shape() and related APIs now abort if buffer direction is
+  HB_DIRECTION_INVALID.  Previously, hb_shape() was calling
+  hb_buffer_guess_segment_properties() on the buffer before
+  shaping.  The heuristics in that function are fragile.  If the
+  user really wants the old behvaior, they can call that function
+  right before calling hb_shape() to get the old behavior.
+- hb_blob_create_sub_blob() always creates sub-blob with
+  HB_MEMORY_MODE_READONLY.  See comments for the reason.
+
+
+Overview of changes leading to 0.9.12
+Thursday, January 18, 2013
+=====================================
+
+- Build fixes for Sun compiler.
+- Minor bug fix.
+
+Overview of changes leading to 0.9.11
+Thursday, January 10, 2013
+=====================================
+
+- Build fixes.
+- Fix GPOS mark attachment with null Anchor offsets.
+- [Indic] Fix old-spec reordering of viramas if sequence ends in one.
+- Fix multi-threaded shaper data creation crash.
+- Add atomic ops for Solaris.
+
+API changes:
+- Rename hb_buffer_clear() to hb_buffer_clear_contents().
+
+
 Overview of changes leading to 0.9.10
 Thursday, January 3, 2013
 =====================================
diff --git a/TODO b/TODO
index c93b33e..31b44b8 100644
--- a/TODO
+++ b/TODO
@@ -7,8 +7,6 @@
 
 - Disable 'vert' if 'vrt2' is available (eg. Motoya fonts with arrow chars).
 
-- Fix TT 'kern' on/off and GPOS interaction (move kerning before GPOS).
-
 - Implement 'rand' feature.
 
 - mask propagation? (when ligation, "or" the masks).
@@ -20,8 +18,6 @@
 
 - Misc features:
   * init/medi/fina/isol for non-cursive scripts
-  * vkna,hkna etc for kana, etc
-  * smpl,trad for ZHS / ZHT
 
 
 API issues to fix before 1.0:
diff --git a/autogen.sh b/autogen.sh
index 47640c3..833a621 100755
--- a/autogen.sh
+++ b/autogen.sh
@@ -7,6 +7,12 @@
 olddir=`pwd`
 cd $srcdir
 
+echo -n "checking for ragel... "
+which ragel || {
+	echo "You need to install ragel... See http://www.complang.org/ragel/"
+	exit 1
+}
+
 echo -n "checking for pkg-config... "
 which pkg-config || {
 	echo "*** No pkg-config found, please install it ***"
diff --git a/config.h.in b/config.h.in
deleted file mode 100644
index 0973c93..0000000
--- a/config.h.in
+++ /dev/null
@@ -1,150 +0,0 @@
-/* config.h.in.  Generated from configure.ac by autoheader.  */
-
-/* Define to 1 if you have the `atexit' function. */
-#undef HAVE_ATEXIT
-
-/* Have cairo graphics library */
-#undef HAVE_CAIRO
-
-/* Have cairo-ft support in cairo graphics library */
-#undef HAVE_CAIRO_FT
-
-/* Have Core Text backend */
-#undef HAVE_CORETEXT
-
-/* Define to 1 if you have the <dlfcn.h> header file. */
-#undef HAVE_DLFCN_H
-
-/* Have FreeType 2 library */
-#undef HAVE_FREETYPE
-
-/* Define to 1 if you have the `FT_Face_GetCharVariantIndex' function. */
-#undef HAVE_FT_FACE_GETCHARVARIANTINDEX
-
-/* Define to 1 if you have the `getpagesize' function. */
-#undef HAVE_GETPAGESIZE
-
-/* Have glib2 library */
-#undef HAVE_GLIB
-
-/* Have gobject2 library */
-#undef HAVE_GOBJECT
-
-/* Have Graphite library */
-#undef HAVE_GRAPHITE2
-
-/* Have gthread2 library */
-#undef HAVE_GTHREAD
-
-/* Have Old HarfBuzz backend */
-#undef HAVE_HB_OLD
-
-/* Have ICU library */
-#undef HAVE_ICU
-
-/* Have ICU Layout Engine library */
-#undef HAVE_ICU_LE
-
-/* Have Intel __sync_* atomic primitives */
-#undef HAVE_INTEL_ATOMIC_PRIMITIVES
-
-/* Define to 1 if you have the <inttypes.h> header file. */
-#undef HAVE_INTTYPES_H
-
-/* Define to 1 if you have the <io.h> header file. */
-#undef HAVE_IO_H
-
-/* Define to 1 if you have the `isatty' function. */
-#undef HAVE_ISATTY
-
-/* Define to 1 if you have the <memory.h> header file. */
-#undef HAVE_MEMORY_H
-
-/* Define to 1 if you have the `mmap' function. */
-#undef HAVE_MMAP
-
-/* Define to 1 if you have the `mprotect' function. */
-#undef HAVE_MPROTECT
-
-/* Have native OpenType Layout backend */
-#undef HAVE_OT
-
-/* Have POSIX threads */
-#undef HAVE_PTHREAD
-
-/* Have PTHREAD_PRIO_INHERIT. */
-#undef HAVE_PTHREAD_PRIO_INHERIT
-
-/* Define to 1 if you have the <sched.h> header file. */
-#undef HAVE_SCHED_H
-
-/* Define to 1 if you have the `sched_yield' function. */
-#undef HAVE_SCHED_YIELD
-
-/* Define to 1 if you have the <stdint.h> header file. */
-#undef HAVE_STDINT_H
-
-/* Define to 1 if you have the <stdlib.h> header file. */
-#undef HAVE_STDLIB_H
-
-/* Define to 1 if you have the <strings.h> header file. */
-#undef HAVE_STRINGS_H
-
-/* Define to 1 if you have the <string.h> header file. */
-#undef HAVE_STRING_H
-
-/* Define to 1 if you have the `sysconf' function. */
-#undef HAVE_SYSCONF
-
-/* Define to 1 if you have the <sys/mman.h> header file. */
-#undef HAVE_SYS_MMAN_H
-
-/* Define to 1 if you have the <sys/stat.h> header file. */
-#undef HAVE_SYS_STAT_H
-
-/* Define to 1 if you have the <sys/types.h> header file. */
-#undef HAVE_SYS_TYPES_H
-
-/* Have UCDN Unicode functions */
-#undef HAVE_UCDN
-
-/* Have Uniscribe backend */
-#undef HAVE_UNISCRIBE
-
-/* Define to 1 if you have the <unistd.h> header file. */
-#undef HAVE_UNISTD_H
-
-/* Define to 1 if you have the `_setmode' function. */
-#undef HAVE__SETMODE
-
-/* Define to the sub-directory in which libtool stores uninstalled libraries.
-   */
-#undef LT_OBJDIR
-
-/* Define to 1 if your C compiler doesn't accept -c and -o together. */
-#undef NO_MINUS_C_MINUS_O
-
-/* Define to the address where bug reports for this package should be sent. */
-#undef PACKAGE_BUGREPORT
-
-/* Define to the full name of this package. */
-#undef PACKAGE_NAME
-
-/* Define to the full name and version of this package. */
-#undef PACKAGE_STRING
-
-/* Define to the one symbol short name of this package. */
-#undef PACKAGE_TARNAME
-
-/* Define to the home page for this package. */
-#undef PACKAGE_URL
-
-/* Define to the version of this package. */
-#undef PACKAGE_VERSION
-
-/* Define to necessary symbol if this constant uses a non-standard name on
-   your system. */
-#undef PTHREAD_CREATE_JOINABLE
-
-/* Define to 1 if you have the ANSI C header files. */
-#undef STDC_HEADERS
diff --git a/configure.ac b/configure.ac
index 91ea713..f05ca65 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,6 +1,6 @@
 AC_PREREQ([2.64])
 AC_INIT([HarfBuzz],
-        [0.9.10],
+        [0.9.14],
         [http://bugs.freedesktop.org/enter_bug.cgi?product=harfbuzz],
         [harfbuzz],
         [http://harfbuzz.org/])
@@ -9,10 +9,11 @@
 AC_CONFIG_SRCDIR([harfbuzz.pc.in])
 AC_CONFIG_HEADERS([config.h])
 
-AM_INIT_AUTOMAKE([1.11.1 gnits dist-bzip2 no-dist-gzip -Wall no-define])
+AM_INIT_AUTOMAKE([1.11.1 gnits dist-bzip2 no-dist-gzip -Wall no-define color-tests])
 AM_SILENT_RULES([yes])
 
 # Initialize libtool
+AM_PROG_AR
 LT_PREREQ([2.2])
 LT_INIT([disable-static])
 
@@ -55,8 +56,8 @@
 dnl GTK_DOC_CHECK([1.15],[--flavour no-tmpl])
 
 # Functions and headers
-AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize sched_yield mmap _setmode isatty)
-AC_CHECK_HEADERS(unistd.h sys/mman.h sched.h io.h)
+AC_CHECK_FUNCS(atexit mprotect sysconf getpagesize mmap isatty)
+AC_CHECK_HEADERS(unistd.h sys/mman.h)
 
 # Compiler flags
 AC_CANONICAL_HOST
@@ -88,6 +89,8 @@
 	esac
 fi
 
+AM_CONDITIONAL(HAVE_GCC, test "x$GCC" = "xyes")
+
 hb_os_win32=no
 AC_MSG_CHECKING([for native Win32])
 case "$host" in
@@ -170,7 +173,27 @@
 dnl ==========================================================================
 
 PKG_CHECK_MODULES(ICU, icu-uc, have_icu=true, have_icu=false)
+dnl Fallback to icu-config if ICU pkg-config files could not be found
+if test "$have_icu" != "true"; then
+	AC_PATH_PROG(icu_config, icu-config, no)
+	AC_MSG_CHECKING([for ICU by using icu-config fallback])
+	if test "$icu_config" != "no" && "$icu_config" --version >/dev/null; then
+		have_icu=true
+		# We don't use --cflags as this gives us a lot of things that we don't
+		# necessarily want, like debugging and optimization flags
+		# See man (1) icu-config for more info.
+		ICU_CFLAGS=`$icu_config --cppflags`
+		ICU_LIBS=`$icu_config --ldflags-libsonly`
+		AC_SUBST(ICU_CFLAGS)
+		AC_SUBST(ICU_LIBS)
+		AC_MSG_RESULT([yes])
+	else
+		AC_MSG_RESULT([no])
+	fi
+fi
+
 if $have_icu; then
+	CXXFLAGS="$CXXFLAGS `$PKG_CONFIG --variable=CXXFLAGS icu-uc`"
 	AC_DEFINE(HAVE_ICU, 1, [Have ICU library])
 fi
 AM_CONDITIONAL(HAVE_ICU, $have_icu)
@@ -178,6 +201,24 @@
 dnl ==========================================================================
 
 PKG_CHECK_MODULES(ICU_LE, icu-le icu-uc, have_icu_le=true, have_icu_le=false)
+dnl Fallback to icu-config if ICU pkg-config files could not be found
+if test "$have_icu_le" != "true"; then
+	AC_PATH_PROG(icu_config, icu-config, no)
+	AC_MSG_CHECKING([for ICU_LE by using icu-config fallback])
+	if test "$icu_config" != "no" && "$icu_config" --version >/dev/null; then
+		have_icu_le=true
+		# We don't use --cflags as this gives us a lot of things that we don't
+		# necessarily want, like debugging and optimization flags
+		# See man (1) icu-config for more info.
+		ICU_LE_CFLAGS=`$icu_config --cppflags`
+		ICU_LE_LIBS=`$icu_config --ldflags-libsonly --ldflags-layout`
+		AC_SUBST(ICU_LE_CFLAGS)
+		AC_SUBST(ICU_LE_LIBS)
+		AC_MSG_RESULT([yes])
+	else
+		AC_MSG_RESULT([no])
+	fi
+fi
 if $have_icu_le; then
 	AC_DEFINE(HAVE_ICU_LE, 1, [Have ICU Layout Engine library])
 fi
@@ -260,6 +301,29 @@
 
 dnl ===========================================================================
 
+AC_CACHE_CHECK([for Solaris atomic operations], hb_cv_have_solaris_atomic_ops, [
+	hb_cv_have_solaris_atomic_ops=false
+	AC_TRY_LINK([
+		#include <atomic.h>
+		/* This requires Solaris Studio 12.2 or newer: */
+		#include <mbarrier.h>
+		void memory_barrier (void) { __machine_rw_barrier (); }
+		int atomic_add (volatile unsigned *i) { return atomic_add_int_nv (i, 1); }
+		void *atomic_ptr_cmpxchg (volatile void **target, void *cmp, void *newval) { return atomic_cas_ptr (target, cmp, newval); }
+		], [], hb_cv_have_solaris_atomic_ops=true
+	)
+])
+if $hb_cv_have_solaris_atomic_ops; then
+	AC_DEFINE(HAVE_SOLARIS_ATOMIC_OPS, 1, [Have Solaris __machine_*_barrier and atomic_* operations])
+fi
+
+if test "$os_win32" = no && ! $have_pthread; then
+	AC_CHECK_HEADERS(sched.h)
+	AC_SEARCH_LIBS(sched_yield,rt,AC_DEFINE(HAVE_SCHED_YIELD, 1, [Have sched_yield]))
+fi
+
+dnl ===========================================================================
+
 AC_CONFIG_FILES([
 Makefile
 harfbuzz.pc
diff --git a/git.mk b/git.mk
index 4da8fe2..cdc7d51 100644
--- a/git.mk
+++ b/git.mk
@@ -1,14 +1,17 @@
 # git.mk
 #
 # Copyright 2009, Red Hat, Inc.
-# Copyright 2010,2011 Behdad Esfahbod
+# Copyright 2010,2011,2012,2013 Behdad Esfahbod
 # Written by Behdad Esfahbod
 #
 # Copying and distribution of this file, with or without modification,
 # is permitted in any medium without royalty provided the copyright
 # notice and this notice are preserved.
 #
-# The canonical source for this file is https://github.com/behdad/git.mk.
+# The latest version of this file can be downloaded from:
+#   https://raw.github.com/behdad/git.mk/master/git.mk
+# Bugs, etc, should be reported upstream at:
+#   https://github.com/behdad/git.mk
 #
 # To use in your project, import this file in your git repo's toplevel,
 # then do "make -f git.mk".  This modifies all Makefile.am files in
@@ -61,6 +64,53 @@
 #   example.
 #
 
+
+
+###############################################################################
+# Variables user modules may want to add to toplevel MAINTAINERCLEANFILES:
+###############################################################################
+
+#
+# Most autotools-using modules should be fine including this variable in their
+# toplevel MAINTAINERCLEANFILES:
+GITIGNORE_MAINTAINERCLEANFILES_TOPLEVEL = \
+	$(srcdir)/aclocal.m4 \
+	$(srcdir)/autoscan.log \
+	$(srcdir)/compile \
+	$(srcdir)/config.guess \
+	$(srcdir)/config.h.in \
+	$(srcdir)/config.sub \
+	$(srcdir)/configure.scan \
+	$(srcdir)/depcomp \
+	$(srcdir)/install-sh \
+	$(srcdir)/ltmain.sh \
+	$(srcdir)/missing \
+	$(srcdir)/mkinstalldirs
+#
+# All modules should also be fine including the following variable, which
+# removes automake-generated Makefile.in files:
+GITIGNORE_MAINTAINERCLEANFILES_MAKEFILE_IN = \
+	`$(AUTOCONF) --trace 'AC_CONFIG_FILES:$$1' $(srcdir)/configure.ac | \
+	while read f; do \
+	  case $$f in Makefile|*/Makefile) \
+	    test -f "$(srcdir)/$$f.am" && echo "$(srcdir)/$$f.in";; esac; \
+	done`
+#
+# Modules that use libtool /and/ use  AC_CONFIG_MACRO_DIR([m4]) may also
+# include this:
+GITIGNORE_MAINTAINERCLEANFILES_M4_LIBTOOL = \
+	$(srcdir)/m4/libtool.m4 \
+	$(srcdir)/m4/ltoptions.m4 \
+	$(srcdir)/m4/ltsugar.m4 \
+	$(srcdir)/m4/ltversion.m4 \
+	$(srcdir)/m4/lt~obsolete.m4
+
+
+
+###############################################################################
+# Default rule is to install ourselves in all Makefile.am files:
+###############################################################################
+
 git-all: git-mk-install
 
 git-mk-install:
@@ -88,7 +138,10 @@
 .PHONY: git-all git-mk-install
 
 
-### .gitignore generation
+
+###############################################################################
+# Actual .gitignore generation:
+###############################################################################
 
 $(srcdir)/.gitignore: Makefile.am $(top_srcdir)/git.mk
 	$(AM_V_GEN) \
diff --git a/src/Makefile.am b/src/Makefile.am
index 558ce08..342a3fb 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -20,7 +20,10 @@
 HBSOURCES =  \
 	hb-atomic-private.hh \
 	hb-blob.cc \
+	hb-buffer-deserialize-json.hh \
+	hb-buffer-deserialize-text.hh \
 	hb-buffer-private.hh \
+	hb-buffer-serialize.cc \
 	hb-buffer.cc \
 	hb-cache-private.hh \
 	hb-common.cc \
@@ -63,6 +66,8 @@
 	hb-shape.h \
 	hb-shape-plan.h \
 	hb-unicode.h \
+	$(NULL)
+HBNODISTHEADERS = \
 	hb-version.h \
 	$(NULL)
 
@@ -85,7 +90,11 @@
 	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-indic-table.cc \
+	hb-ot-shape-complex-myanmar.cc \
+	hb-ot-shape-complex-myanmar-machine.hh \
+	hb-ot-shape-complex-sea.cc \
+	hb-ot-shape-complex-sea-machine.hh \
 	hb-ot-shape-complex-thai.cc \
 	hb-ot-shape-complex-private.hh \
 	hb-ot-shape-normalize-private.hh \
@@ -198,22 +207,26 @@
 if HAVE_ICU
 libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS)
 else
-# Use a C linker, not C++; Don't link to libstdc++
+# Use a C linker for GCC, not C++; Don't link to libstdc++
+if HAVE_GCC
 libharfbuzz_la_LINK = $(LINK) $(libharfbuzz_la_LDFLAGS)
+else
+libharfbuzz_la_LINK = $(CXXLINK) $(libharfbuzz_la_LDFLAGS)
+endif
 endif
 endif
 
-libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS)
+libharfbuzz_la_SOURCES = $(HBSOURCES) $(HBHEADERS) $(HBNODISTHEADERS)
 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)
 EXTRA_libharfbuzz_la_DEPENDENCIES = $(harfbuzz_def_dependency)
 pkginclude_HEADERS = $(HBHEADERS)
-nodist_pkginclude_HEADERS = hb-version.h
+nodist_pkginclude_HEADERS = $(HBNODISTHEADERS)
 
 CLEANFILES += harfbuzz.def
-harfbuzz.def: $(HBHEADERS)
+harfbuzz.def: $(HBHEADERS) $(HBNODISTHEADERS)
 	$(AM_V_GEN) (echo EXPORTS; \
 	(cat $^ || echo 'hb_ERROR ()' ) | \
 	$(EGREP) '^hb_.* \(' | \
@@ -234,9 +247,9 @@
 unicode-tables: arabic-table indic-table
 
 indic-table: gen-indic-table.py IndicSyllabicCategory.txt IndicMatraCategory.txt Blocks.txt
-	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.hh.tmp && \
-	mv hb-ot-shape-complex-indic-table.hh.tmp $(srcdir)/hb-ot-shape-complex-indic-table.hh || \
-	($(RM) hb-ot-shape-complex-indic-table.hh.tmp; false)
+	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-indic-table.cc.tmp && \
+	mv hb-ot-shape-complex-indic-table.cc.tmp $(srcdir)/hb-ot-shape-complex-indic-table.cc || \
+	($(RM) hb-ot-shape-complex-indic-table.cc.tmp; false)
 
 arabic-table: gen-arabic-table.py ArabicShaping.txt UnicodeData.txt
 	$(AM_V_GEN) $(builddir)/$^ > hb-ot-shape-complex-arabic-table.hh.tmp && \
@@ -246,12 +259,31 @@
 
 .PHONY: unicode-tables arabic-table indic-table
 
-EXTRA_DIST += hb-ot-shape-complex-indic-machine.rl
-$(srcdir)/hb-ot-shape-complex-indic-machine.hh: hb-ot-shape-complex-indic-machine.rl
+BUILT_SOURCES += \
+	hb-buffer-deserialize-json.hh \
+	hb-buffer-deserialize-text.hh \
+	hb-ot-shape-complex-indic-machine.hh \
+	hb-ot-shape-complex-myanmar-machine.hh \
+	hb-ot-shape-complex-sea-machine.hh \
+	$(NULL)
+EXTRA_DIST += \
+	hb-buffer-deserialize-json.rl \
+	hb-buffer-deserialize-text.rl \
+	hb-ot-shape-complex-indic-machine.rl \
+	hb-ot-shape-complex-myanmar-machine.rl \
+	hb-ot-shape-complex-sea-machine.rl \
+	$(NULL)
+%.hh: %.rl
 	$(AM_V_GEN)$(top_srcdir)/missing --run ragel -e -F1 -o "$@.tmp" "$<" && \
 	mv "$@.tmp" "$@" || ( $(RM) "$@.tmp" && false )
 
-noinst_PROGRAMS = main test test-would-substitute test-size-params
+noinst_PROGRAMS = \
+	main \
+	test \
+	test-buffer-serialize \
+	test-size-params \
+	test-would-substitute \
+	$(NULL)
 bin_PROGRAMS =
 
 main_SOURCES = main.cc
@@ -270,12 +302,15 @@
 test_size_params_CPPFLAGS = $(HBCFLAGS)
 test_size_params_LDADD = libharfbuzz.la $(HBLIBS)
 
+test_buffer_serialize_SOURCES = test-buffer-serialize.cc
+test_buffer_serialize_CPPFLAGS = $(HBCFLAGS)
+test_buffer_serialize_LDADD = libharfbuzz.la $(HBLIBS)
+
 dist_check_SCRIPTS = \
 	check-c-linkage-decls.sh \
 	check-header-guards.sh \
-	check-exported-symbols.sh \
 	check-includes.sh \
-	check-internal-symbols.sh \
+	check-symbols.sh \
 	$(NULL)
 
 if HAVE_ICU
@@ -293,7 +328,7 @@
 	srcdir="$(srcdir)" \
 	MAKE="$(MAKE) $(AM_MAKEFLAGS)" \
 	HBSOURCES="$(HBSOURCES)" \
-	HBHEADERS="$(HBHEADERS)" \
+	HBHEADERS="$(HBHEADERS) $(HBNODISTHEADERS)" \
 	$(NULL)
 
 #-include $(INTROSPECTION_MAKEFILE)
@@ -307,7 +342,7 @@
 #hb_1_0_gir_INCLUDES = GObject-2.0
 #hb_1_0_gir_CFLAGS = $(INCLUDES) $(HBCFLAGS) -DHB_H -DHB_H_IN -DHB_OT_H -DHB_OT_H_IN
 #hb_1_0_gir_LIBS = libharfbuzz.la
-#hb_1_0_gir_FILES = $(HBHEADERS)
+#hb_1_0_gir_FILES = $(HBHEADERS) $(HBNODISTHEADERS)
 #
 #girdir = $(datadir)/gir-1.0
 #gir_DATA = $(INTROSPECTION_GIRS)
diff --git a/src/check-exported-symbols.sh b/src/check-exported-symbols.sh
deleted file mode 100755
index 6f0bf7f..0000000
--- a/src/check-exported-symbols.sh
+++ /dev/null
@@ -1,40 +0,0 @@
-#!/bin/sh
-
-LC_ALL=C
-export LC_ALL
-
-test -z "$srcdir" && srcdir=.
-test -z "$MAKE" && MAKE=make
-stat=0
-
-if which nm 2>/dev/null >/dev/null; then
-	:
-else
-	echo "check-exported-symbols.sh: 'nm' not found; skipping test"
-	exit 77
-fi
-
-defs="harfbuzz.def"
-$MAKE $defs > /dev/null
-tested=false
-for def in $defs; do
-	lib=`echo "$def" | sed 's/[.]def$//;s@.*/@@'`
-	so=.libs/lib${lib}.so
-	if test -f "$so"; then
-		echo "Checking that $so has the same symbol list as $def"
-		{
-			echo EXPORTS
-			nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' T _fini\>\| T _init\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>' | cut -d' ' -f3
-			stat=1
-			# cheat: copy the last line from the def file!
-			tail -n1 "$def"
-		} | diff "$def" - >&2 || stat=1
-		tested=true
-	fi
-done
-if ! $tested; then
-	echo "check-exported-symbols.sh: libharfbuzz shared library not found; skipping test"
-	exit 77
-fi
-
-exit $stat
diff --git a/src/check-internal-symbols.sh b/src/check-internal-symbols.sh
deleted file mode 100755
index a8fdc53..0000000
--- a/src/check-internal-symbols.sh
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/bin/sh
-
-LC_ALL=C
-export LC_ALL
-
-test -z "$srcdir" && srcdir=.
-stat=0
-
-
-if which nm 2>/dev/null >/dev/null; then
-	:
-else
-	echo "check-internal-symbols.sh: 'nm' not found; skipping test"
-	exit 77
-fi
-
-tested=false
-for suffix in .so; do
-	so=`echo .libs/libharfbuzz$suffix`
-	if test -f "$so"; then
-		echo "Checking that we are not exposing internal symbols"
-		if nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' T _fini\>\| T _init\>\| T hb_\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>'; then
-			echo "Ouch, internal symbols exposed"
-			stat=1
-		fi
-		tested=true
-	fi
-done
-if ! $tested; then
-	echo "check-internal-symbols.sh: libharfbuzz shared library not found; skipping test"
-	exit 77
-fi
-
-exit $stat
diff --git a/src/check-symbols.sh b/src/check-symbols.sh
new file mode 100755
index 0000000..4c03c13
--- /dev/null
+++ b/src/check-symbols.sh
@@ -0,0 +1,50 @@
+#!/bin/sh
+
+LC_ALL=C
+export LC_ALL
+
+test -z "$srcdir" && srcdir=.
+test -z "$MAKE" && MAKE=make
+stat=0
+
+if which nm 2>/dev/null >/dev/null; then
+	:
+else
+	echo "check-symbols.sh: 'nm' not found; skipping test"
+	exit 77
+fi
+
+defs="harfbuzz.def"
+$MAKE $defs > /dev/null
+tested=false
+for def in $defs; do
+	lib=`echo "$def" | sed 's/[.]def$//;s@.*/@@'`
+	so=.libs/lib${lib}.so
+
+	EXPORTED_SYMBOLS="`nm "$so" | grep ' [BCDGINRSTVW] ' | grep -v ' T _fini\>\| T _init\>\| _fdata\>\| _ftext\>\| __bss_start\>\| __bss_start__\>\| __bss_end__\>\| _edata\>\| _end\>\| _bss_end__\>\| __end__\>' | cut -d' ' -f3`"
+
+	if test -f "$so"; then
+
+		echo "Checking that $so has the same symbol list as $def"
+		{
+			echo EXPORTS
+			echo "$EXPORTED_SYMBOLS"
+			# cheat: copy the last line from the def file!
+			tail -n1 "$def"
+		} | diff "$def" - >&2 || stat=1
+
+		echo "Checking that we are not exposing internal symbols"
+		if echo "$EXPORTED_SYMBOLS" | grep -v 'hb_'; then
+			echo "Ouch, internal symbols exposed"
+			stat=1
+		fi
+
+		tested=true
+	fi
+done
+if ! $tested; then
+	echo "check-exported-symbols.sh: libharfbuzz shared library not found; skipping test"
+	exit 77
+fi
+
+exit $stat
diff --git a/src/gen-indic-table.py b/src/gen-indic-table.py
index 94aa2ab..9ed3fd6 100755
--- a/src/gen-indic-table.py
+++ b/src/gen-indic-table.py
@@ -75,8 +75,7 @@
 		print " * %s" % (l.strip())
 print " */"
 print
-print "#ifndef HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH"
-print "#define HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH"
+print '#include "hb-ot-shape-complex-indic-private.hh"'
 print
 
 # Shorten values
@@ -182,8 +181,8 @@
 occupancy = used * 100. / total
 print "}; /* Table occupancy: %d%% */" % occupancy
 print
-print "static INDIC_TABLE_ELEMENT_TYPE"
-print "get_indic_categories (hb_codepoint_t u)"
+print "INDIC_TABLE_ELEMENT_TYPE"
+print "hb_indic_get_categories (hb_codepoint_t u)"
 print "{"
 for (start,end) in zip (starts, ends):
 	offset = "indic_offset_0x%04x" % start
@@ -202,8 +201,6 @@
 		print "#undef %s_%s" % \
 			(what_short[i], short[i][v])
 print
-print "#endif /* HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH */"
-print
 print "/* == End of generated table == */"
 
 # Maintain at least 30% occupancy in the table */
diff --git a/src/hb-atomic-private.hh b/src/hb-atomic-private.hh
index 67579cd..7047e21 100644
--- a/src/hb-atomic-private.hh
+++ b/src/hb-atomic-private.hh
@@ -42,27 +42,15 @@
 #if 0
 
 
-#elif !defined(HB_NO_MT) && defined(_MSC_VER) || defined(__MINGW32__)
+#elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__))
 
 #define WIN32_LEAN_AND_MEAN
 #include <windows.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_ptr_get(P)		(HBMemoryBarrier (), (void *) *(P))
+#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))
 
 
@@ -99,6 +87,18 @@
 #define hb_atomic_ptr_cmpexch(P,O,N)	__sync_bool_compare_and_swap ((P), (O), (N))
 
 
+#elif !defined(HB_NO_MT) && defined(HAVE_SOLARIS_ATOMIC_OPS)
+
+#include <atomic.h>
+#include <mbarrier.h>
+
+typedef unsigned int hb_atomic_int_t;
+#define hb_atomic_int_add(AI, V)	( ({__machine_rw_barrier ();}), atomic_add_int_nv (&(AI), (V)) - (V))
+
+#define hb_atomic_ptr_get(P)		( ({__machine_rw_barrier ();}), (void *) *(P))
+#define hb_atomic_ptr_cmpexch(P,O,N)	( ({__machine_rw_barrier ();}), atomic_cas_ptr ((void **) (P), (void *) (O), (void *) (N)) == (void *) (O) ? true : false)
+
+
 #elif !defined(HB_NO_MT)
 
 #define HB_ATOMIC_INT_NIL 1 /* Warn that fallback implementation is in use. */
diff --git a/src/hb-blob.cc b/src/hb-blob.cc
index b6e696b..dfd134b 100644
--- a/src/hb-blob.cc
+++ b/src/hb-blob.cc
@@ -24,6 +24,9 @@
  * Red Hat Author(s): Behdad Esfahbod
  */
 
+/* http://www.oracle.com/technetwork/articles/servers-storage-dev/standardheaderfiles-453865.html */
+#define _POSIX_C_SOURCE 199309L
+
 #include "hb-private.hh"
 
 #include "hb-blob.h"
@@ -120,7 +123,7 @@
 
   blob = hb_blob_create (parent->data + offset,
 			 MIN (length, parent->length - offset),
-			 parent->mode,
+			 HB_MEMORY_MODE_READONLY,
 			 hb_blob_reference (parent),
 			 (hb_destroy_func_t) hb_blob_destroy);
 
diff --git a/src/hb-blob.h b/src/hb-blob.h
index 1a93baa..d3d0f41 100644
--- a/src/hb-blob.h
+++ b/src/hb-blob.h
@@ -36,6 +36,26 @@
 HB_BEGIN_DECLS
 
 
+/*
+ * Note re various memory-modes:
+ *
+ * - In no case shall the HarfBuzz client modify memory
+ *   that is passed to HarfBuzz in a blob.  If there is
+ *   any such possibility, MODE_DUPLICATE should be used
+ *   such that HarfBuzz makes a copy immediately,
+ *
+ * - Use MODE_READONLY otherse, unless you really really
+ *   really know what you are doing,
+ *
+ * - MODE_WRITABLE is appropriate if you relaly made a
+ *   copy of data solely for the purpose of passing to
+ *   HarfBuzz and doing that just once (no reuse!),
+ *
+ * - If the font is mmap()ed, it's ok to use
+ *   READONLY_MAY_MAKE_WRITABLE, however, there were
+ *   design problems with that mode, so HarfBuzz do not
+ *   really use it anymore.  If not sure, use MODE_READONLY.
+ */
 typedef enum {
   HB_MEMORY_MODE_DUPLICATE,
   HB_MEMORY_MODE_READONLY,
@@ -52,6 +72,12 @@
 		void              *user_data,
 		hb_destroy_func_t  destroy);
 
+/* Always creates with MEMORY_MODE_READONLY.
+ * Even if the parent blob is writable, we don't
+ * want the user of the sub-blob to be able to
+ * modify the parent data as that data may be
+ * shared among multiple sub-blobs.
+ */
 hb_blob_t *
 hb_blob_create_sub_blob (hb_blob_t    *parent,
 			 unsigned int  offset,
diff --git a/src/hb-buffer-deserialize-json.hh b/src/hb-buffer-deserialize-json.hh
new file mode 100644
index 0000000..b120eaf
--- /dev/null
+++ b/src/hb-buffer-deserialize-json.hh
@@ -0,0 +1,643 @@
+
+#line 1 "hb-buffer-deserialize-json.rl"
+/*
+ * Copyright © 2013  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_BUFFER_DESERIALIZE_JSON_HH
+#define HB_BUFFER_DESERIALIZE_JSON_HH
+
+#include "hb-private.hh"
+
+
+#line 36 "hb-buffer-deserialize-json.hh.tmp"
+static const unsigned char _deserialize_json_trans_keys[] = {
+	0u, 0u, 9u, 123u, 9u, 34u, 97u, 103u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 
+	48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 
+	9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 
+	120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 
+	9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 
+	65u, 122u, 34u, 122u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 123u, 0u, 0u, 0
+};
+
+static const char _deserialize_json_key_spans[] = {
+	0, 115, 26, 7, 2, 1, 50, 49, 
+	10, 117, 117, 117, 1, 50, 49, 10, 
+	117, 117, 1, 1, 50, 49, 117, 117, 
+	2, 1, 50, 49, 10, 117, 117, 1, 
+	50, 49, 10, 117, 117, 1, 50, 49, 
+	58, 89, 117, 117, 85, 115, 0
+};
+
+static const short _deserialize_json_index_offsets[] = {
+	0, 0, 116, 143, 151, 154, 156, 207, 
+	257, 268, 386, 504, 622, 624, 675, 725, 
+	736, 854, 972, 974, 976, 1027, 1077, 1195, 
+	1313, 1316, 1318, 1369, 1419, 1430, 1548, 1666, 
+	1668, 1719, 1769, 1780, 1898, 2016, 2018, 2069, 
+	2119, 2178, 2268, 2386, 2504, 2590, 2706
+};
+
+static const char _deserialize_json_indicies[] = {
+	0, 0, 0, 0, 0, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	0, 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, 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, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 2, 1, 3, 3, 3, 
+	3, 3, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 3, 1, 4, 1, 
+	5, 1, 6, 7, 1, 1, 8, 1, 
+	9, 10, 1, 11, 1, 11, 11, 11, 
+	11, 11, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 11, 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, 12, 1, 
+	12, 12, 12, 12, 12, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 12, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 13, 1, 1, 14, 
+	15, 15, 15, 15, 15, 15, 15, 15, 
+	15, 1, 16, 17, 17, 17, 17, 17, 
+	17, 17, 17, 17, 1, 18, 18, 18, 
+	18, 18, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 18, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	19, 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, 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, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 20, 1, 21, 21, 21, 21, 21, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 21, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 3, 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, 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, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 22, 
+	1, 18, 18, 18, 18, 18, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	18, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 19, 1, 1, 1, 
+	17, 17, 17, 17, 17, 17, 17, 17, 
+	17, 17, 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, 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, 20, 1, 23, 
+	1, 23, 23, 23, 23, 23, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	23, 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, 24, 1, 24, 24, 24, 24, 
+	24, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 24, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	25, 1, 1, 26, 27, 27, 27, 27, 
+	27, 27, 27, 27, 27, 1, 28, 29, 
+	29, 29, 29, 29, 29, 29, 29, 29, 
+	1, 30, 30, 30, 30, 30, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	30, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 31, 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, 
+	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, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 32, 1, 30, 
+	30, 30, 30, 30, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 30, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 31, 1, 1, 1, 29, 29, 
+	29, 29, 29, 29, 29, 29, 29, 29, 
+	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, 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, 32, 1, 33, 1, 34, 
+	1, 34, 34, 34, 34, 34, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	34, 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, 35, 1, 35, 35, 35, 35, 
+	35, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 35, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 36, 37, 37, 37, 37, 
+	37, 37, 37, 37, 37, 1, 38, 38, 
+	38, 38, 38, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 38, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 39, 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, 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, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 40, 1, 38, 38, 38, 38, 
+	38, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 38, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 39, 
+	1, 1, 1, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 41, 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, 
+	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, 
+	40, 1, 42, 43, 1, 44, 1, 44, 
+	44, 44, 44, 44, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 44, 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, 
+	45, 1, 45, 45, 45, 45, 45, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 45, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 46, 1, 
+	1, 47, 48, 48, 48, 48, 48, 48, 
+	48, 48, 48, 1, 49, 50, 50, 50, 
+	50, 50, 50, 50, 50, 50, 1, 51, 
+	51, 51, 51, 51, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 51, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 52, 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, 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, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 53, 1, 51, 51, 51, 
+	51, 51, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 51, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	52, 1, 1, 1, 50, 50, 50, 50, 
+	50, 50, 50, 50, 50, 50, 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, 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, 53, 1, 54, 1, 54, 54, 54, 
+	54, 54, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 54, 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, 55, 1, 
+	55, 55, 55, 55, 55, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 55, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 56, 1, 1, 57, 
+	58, 58, 58, 58, 58, 58, 58, 58, 
+	58, 1, 59, 60, 60, 60, 60, 60, 
+	60, 60, 60, 60, 1, 61, 61, 61, 
+	61, 61, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 61, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	62, 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, 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, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 63, 1, 61, 61, 61, 61, 61, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 61, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 62, 1, 
+	1, 1, 60, 60, 60, 60, 60, 60, 
+	60, 60, 60, 60, 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, 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, 63, 
+	1, 64, 1, 64, 64, 64, 64, 64, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 64, 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, 65, 1, 65, 65, 
+	65, 65, 65, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 65, 1, 66, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 67, 68, 68, 
+	68, 68, 68, 68, 68, 68, 68, 1, 
+	69, 69, 69, 69, 69, 69, 69, 69, 
+	69, 69, 69, 69, 69, 69, 69, 69, 
+	69, 69, 69, 69, 69, 69, 69, 69, 
+	69, 69, 1, 1, 1, 1, 1, 1, 
+	69, 69, 69, 69, 69, 69, 69, 69, 
+	69, 69, 69, 69, 69, 69, 69, 69, 
+	69, 69, 69, 69, 69, 69, 69, 69, 
+	69, 69, 1, 70, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 71, 71, 
+	1, 71, 71, 71, 71, 71, 71, 71, 
+	71, 71, 71, 1, 1, 1, 1, 1, 
+	1, 1, 71, 71, 71, 71, 71, 71, 
+	71, 71, 71, 71, 71, 71, 71, 71, 
+	71, 71, 71, 71, 71, 71, 71, 71, 
+	71, 71, 71, 71, 1, 1, 1, 1, 
+	71, 1, 71, 71, 71, 71, 71, 71, 
+	71, 71, 71, 71, 71, 71, 71, 71, 
+	71, 71, 71, 71, 71, 71, 71, 71, 
+	71, 71, 71, 71, 1, 72, 72, 72, 
+	72, 72, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 72, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	73, 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, 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, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 74, 1, 72, 72, 72, 72, 72, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 72, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 73, 1, 
+	1, 1, 75, 75, 75, 75, 75, 75, 
+	75, 75, 75, 75, 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, 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, 74, 
+	1, 76, 76, 76, 76, 76, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	76, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 77, 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, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 78, 1, 0, 
+	0, 0, 0, 0, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 0, 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, 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, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 2, 1, 1, 0
+};
+
+static const char _deserialize_json_trans_targs[] = {
+	1, 0, 2, 2, 3, 4, 18, 24, 
+	37, 5, 12, 6, 7, 8, 9, 11, 
+	9, 11, 10, 2, 44, 10, 44, 13, 
+	14, 15, 16, 17, 16, 17, 10, 2, 
+	44, 19, 20, 21, 22, 23, 10, 2, 
+	44, 23, 25, 31, 26, 27, 28, 29, 
+	30, 29, 30, 10, 2, 44, 32, 33, 
+	34, 35, 36, 35, 36, 10, 2, 44, 
+	38, 39, 40, 42, 43, 41, 10, 41, 
+	10, 2, 44, 43, 44, 45, 46
+};
+
+static const char _deserialize_json_trans_actions[] = {
+	0, 0, 1, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 2, 2, 2, 
+	0, 0, 3, 3, 4, 0, 5, 0, 
+	0, 2, 2, 2, 0, 0, 6, 6, 
+	7, 0, 0, 0, 2, 2, 8, 8, 
+	9, 0, 0, 0, 0, 0, 2, 2, 
+	2, 0, 0, 10, 10, 11, 0, 0, 
+	2, 2, 2, 0, 0, 12, 12, 13, 
+	0, 0, 0, 2, 2, 2, 14, 0, 
+	15, 15, 16, 0, 0, 0, 0
+};
+
+static const int deserialize_json_start = 1;
+static const int deserialize_json_first_final = 44;
+static const int deserialize_json_error = 0;
+
+static const int deserialize_json_en_main = 1;
+
+
+#line 97 "hb-buffer-deserialize-json.rl"
+
+
+static hb_bool_t
+_hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer,
+				    const char *buf,
+				    unsigned int buf_len,
+				    const char **end_ptr,
+				    hb_font_t *font)
+{
+  const char *p = buf, *pe = buf + buf_len;
+
+  /* Ensure we have positions. */
+  (void) hb_buffer_get_glyph_positions (buffer, NULL);
+
+  while (p < pe && ISSPACE (*p))
+    p++;
+  if (p < pe && *p == (buffer->len ? ',' : '['))
+  {
+    *end_ptr = ++p;
+  }
+
+  const char *tok = NULL;
+  int cs;
+  hb_glyph_info_t info;
+  hb_glyph_position_t pos;
+  
+#line 466 "hb-buffer-deserialize-json.hh.tmp"
+	{
+	cs = deserialize_json_start;
+	}
+
+#line 471 "hb-buffer-deserialize-json.hh.tmp"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+	if ( cs == 0 )
+		goto _out;
+_resume:
+	_keys = _deserialize_json_trans_keys + (cs<<1);
+	_inds = _deserialize_json_indicies + _deserialize_json_index_offsets[cs];
+
+	_slen = _deserialize_json_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=(*p) &&
+		(*p) <= _keys[1] ?
+		(*p) - _keys[0] : _slen ];
+
+	cs = _deserialize_json_trans_targs[_trans];
+
+	if ( _deserialize_json_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _deserialize_json_trans_actions[_trans] ) {
+	case 1:
+#line 38 "hb-buffer-deserialize-json.rl"
+	{
+	memset (&info, 0, sizeof (info));
+	memset (&pos , 0, sizeof (pos ));
+}
+	break;
+	case 5:
+#line 43 "hb-buffer-deserialize-json.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 2:
+#line 51 "hb-buffer-deserialize-json.rl"
+	{
+	tok = p;
+}
+	break;
+	case 14:
+#line 55 "hb-buffer-deserialize-json.rl"
+	{
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+	break;
+	case 15:
+#line 62 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_uint (tok, p, &info.codepoint)) return false; }
+	break;
+	case 8:
+#line 63 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+	break;
+	case 10:
+#line 64 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.x_offset )) return false; }
+	break;
+	case 12:
+#line 65 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+	break;
+	case 3:
+#line 66 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+	break;
+	case 6:
+#line 67 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+	break;
+	case 16:
+#line 62 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_uint (tok, p, &info.codepoint)) return false; }
+#line 43 "hb-buffer-deserialize-json.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 9:
+#line 63 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+#line 43 "hb-buffer-deserialize-json.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 11:
+#line 64 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.x_offset )) return false; }
+#line 43 "hb-buffer-deserialize-json.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 13:
+#line 65 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+#line 43 "hb-buffer-deserialize-json.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 4:
+#line 66 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+#line 43 "hb-buffer-deserialize-json.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 7:
+#line 67 "hb-buffer-deserialize-json.rl"
+	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+#line 43 "hb-buffer-deserialize-json.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+#line 624 "hb-buffer-deserialize-json.hh.tmp"
+	}
+
+_again:
+	if ( cs == 0 )
+		goto _out;
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	_out: {}
+	}
+
+#line 125 "hb-buffer-deserialize-json.rl"
+
+
+  *end_ptr = p;
+
+  return p == pe && *(p-1) != ']';
+}
+
+#endif /* HB_BUFFER_DESERIALIZE_JSON_HH */
diff --git a/src/hb-buffer-deserialize-json.rl b/src/hb-buffer-deserialize-json.rl
new file mode 100644
index 0000000..7351b2a
--- /dev/null
+++ b/src/hb-buffer-deserialize-json.rl
@@ -0,0 +1,132 @@
+/*
+ * Copyright © 2013  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_BUFFER_DESERIALIZE_JSON_HH
+#define HB_BUFFER_DESERIALIZE_JSON_HH
+
+#include "hb-private.hh"
+
+%%{
+
+machine deserialize_json;
+alphtype unsigned char;
+write data;
+
+action clear_item {
+	memset (&info, 0, sizeof (info));
+	memset (&pos , 0, sizeof (pos ));
+}
+
+action add_item {
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+
+action tok {
+	tok = p;
+}
+
+action parse_glyph {
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+
+action parse_gid       { if (!parse_uint (tok, p, &info.codepoint)) return false; }
+action parse_cluster   { if (!parse_uint (tok, p, &info.cluster )) return false; }
+action parse_x_offset  { if (!parse_int  (tok, p, &pos.x_offset )) return false; }
+action parse_y_offset  { if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+action parse_x_advance { if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+action parse_y_advance { if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+
+unum	= '0' | [1-9] digit*;
+num	= '-'? unum;
+
+comma = space* ',' space*;
+colon = space* ':' space*;
+
+glyph_id = unum;
+glyph_name = alpha (alnum|'_'|'.'|'-')*;
+
+glyph_string   = '"' (glyph_name >tok %parse_glyph) '"';
+glyph_number = (glyph_id >tok %parse_gid);
+
+glyph	= "\"g\""  colon (glyph_string | glyph_number);
+cluster	= "\"cl\"" colon (unum >tok %parse_cluster);
+xoffset	= "\"dx\"" colon (num >tok %parse_x_offset);
+yoffset	= "\"dy\"" colon (num >tok %parse_y_offset);
+xadvance= "\"ax\"" colon (num >tok %parse_x_advance);
+yadvance= "\"ay\"" colon (num >tok %parse_y_advance);
+
+element = glyph | cluster | xoffset | yoffset | xadvance | yadvance;
+item	=
+	( '{' space* element (comma element)* space* '}')
+	>clear_item
+	@add_item
+	;
+
+main := space* item (comma item)* space* (','|']')?;
+
+}%%
+
+static hb_bool_t
+_hb_buffer_deserialize_glyphs_json (hb_buffer_t *buffer,
+				    const char *buf,
+				    unsigned int buf_len,
+				    const char **end_ptr,
+				    hb_font_t *font)
+{
+  const char *p = buf, *pe = buf + buf_len;
+
+  /* Ensure we have positions. */
+  (void) hb_buffer_get_glyph_positions (buffer, NULL);
+
+  while (p < pe && ISSPACE (*p))
+    p++;
+  if (p < pe && *p == (buffer->len ? ',' : '['))
+  {
+    *end_ptr = ++p;
+  }
+
+  const char *tok = NULL;
+  int cs;
+  hb_glyph_info_t info;
+  hb_glyph_position_t pos;
+  %%{
+    write init;
+    write exec;
+  }%%
+
+  *end_ptr = p;
+
+  return p == pe && *(p-1) != ']';
+}
+
+#endif /* HB_BUFFER_DESERIALIZE_JSON_HH */
diff --git a/src/hb-buffer-deserialize-text.hh b/src/hb-buffer-deserialize-text.hh
new file mode 100644
index 0000000..f95704b
--- /dev/null
+++ b/src/hb-buffer-deserialize-text.hh
@@ -0,0 +1,571 @@
+
+#line 1 "hb-buffer-deserialize-text.rl"
+/*
+ * Copyright © 2013  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_BUFFER_DESERIALIZE_TEXT_HH
+#define HB_BUFFER_DESERIALIZE_TEXT_HH
+
+#include "hb-private.hh"
+
+
+#line 36 "hb-buffer-deserialize-text.hh.tmp"
+static const unsigned char _deserialize_text_trans_keys[] = {
+	0u, 0u, 9u, 122u, 45u, 57u, 48u, 57u, 45u, 57u, 48u, 57u, 48u, 57u, 45u, 57u, 
+	48u, 57u, 44u, 44u, 45u, 57u, 48u, 57u, 44u, 57u, 9u, 124u, 9u, 124u, 0u, 0u, 
+	9u, 122u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 9u, 124u, 
+	9u, 124u, 9u, 124u, 9u, 124u, 0
+};
+
+static const char _deserialize_text_key_spans[] = {
+	0, 114, 13, 10, 13, 10, 10, 13, 
+	10, 1, 13, 10, 14, 116, 116, 0, 
+	114, 116, 116, 116, 116, 116, 116, 116, 
+	116, 116, 116
+};
+
+static const short _deserialize_text_index_offsets[] = {
+	0, 0, 115, 129, 140, 154, 165, 176, 
+	190, 201, 203, 217, 228, 243, 360, 477, 
+	478, 593, 710, 827, 944, 1061, 1178, 1295, 
+	1412, 1529, 1646
+};
+
+static const char _deserialize_text_indicies[] = {
+	0, 0, 0, 0, 0, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	0, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	2, 3, 3, 3, 3, 3, 3, 3, 
+	3, 3, 1, 1, 1, 1, 1, 1, 
+	1, 4, 4, 4, 4, 4, 4, 4, 
+	4, 4, 4, 4, 4, 4, 4, 4, 
+	4, 4, 4, 4, 4, 4, 4, 4, 
+	4, 4, 4, 1, 1, 1, 1, 1, 
+	1, 4, 4, 4, 4, 4, 4, 4, 
+	4, 4, 4, 4, 4, 4, 4, 4, 
+	4, 4, 4, 4, 4, 4, 4, 4, 
+	4, 4, 4, 1, 5, 1, 1, 6, 
+	7, 7, 7, 7, 7, 7, 7, 7, 
+	7, 1, 8, 9, 9, 9, 9, 9, 
+	9, 9, 9, 9, 1, 10, 1, 1, 
+	11, 12, 12, 12, 12, 12, 12, 12, 
+	12, 12, 1, 13, 14, 14, 14, 14, 
+	14, 14, 14, 14, 14, 1, 15, 16, 
+	16, 16, 16, 16, 16, 16, 16, 16, 
+	1, 17, 1, 1, 18, 19, 19, 19, 
+	19, 19, 19, 19, 19, 19, 1, 20, 
+	21, 21, 21, 21, 21, 21, 21, 21, 
+	21, 1, 22, 1, 23, 1, 1, 24, 
+	25, 25, 25, 25, 25, 25, 25, 25, 
+	25, 1, 26, 27, 27, 27, 27, 27, 
+	27, 27, 27, 27, 1, 22, 1, 1, 
+	1, 21, 21, 21, 21, 21, 21, 21, 
+	21, 21, 21, 1, 28, 28, 28, 28, 
+	28, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 28, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 29, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	30, 1, 1, 31, 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, 
+	32, 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, 33, 
+	1, 34, 34, 34, 34, 34, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	34, 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, 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, 35, 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, 36, 1, 1, 0, 
+	0, 0, 0, 0, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 0, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 2, 3, 
+	3, 3, 3, 3, 3, 3, 3, 3, 
+	1, 1, 1, 1, 1, 1, 1, 4, 
+	4, 4, 4, 4, 4, 4, 4, 4, 
+	4, 4, 4, 4, 4, 4, 4, 4, 
+	4, 4, 4, 4, 4, 4, 4, 4, 
+	4, 1, 1, 1, 1, 1, 1, 4, 
+	4, 4, 4, 4, 4, 4, 4, 4, 
+	4, 4, 4, 4, 4, 4, 4, 4, 
+	4, 4, 4, 4, 4, 4, 4, 4, 
+	4, 1, 28, 28, 28, 28, 28, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 28, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 29, 1, 1, 1, 
+	1, 37, 37, 37, 37, 37, 37, 37, 
+	37, 37, 37, 1, 1, 1, 30, 1, 
+	1, 31, 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, 32, 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, 33, 1, 38, 
+	38, 38, 38, 38, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 38, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 39, 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, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 40, 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, 41, 1, 42, 42, 42, 42, 
+	42, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 42, 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, 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, 
+	43, 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, 44, 
+	1, 42, 42, 42, 42, 42, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	42, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	14, 14, 14, 14, 14, 14, 14, 14, 
+	14, 14, 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, 43, 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, 44, 1, 38, 38, 
+	38, 38, 38, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 38, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 39, 1, 1, 1, 9, 9, 9, 
+	9, 9, 9, 9, 9, 9, 9, 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, 40, 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, 41, 1, 45, 45, 45, 45, 45, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 45, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 46, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 47, 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, 48, 
+	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, 49, 1, 
+	50, 50, 50, 50, 50, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 50, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 51, 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, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 52, 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, 53, 1, 50, 50, 50, 
+	50, 50, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 50, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 51, 
+	1, 1, 1, 1, 27, 27, 27, 27, 
+	27, 27, 27, 27, 27, 27, 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, 52, 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, 
+	53, 1, 45, 45, 45, 45, 45, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 45, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 46, 1, 1, 1, 
+	1, 54, 54, 54, 54, 54, 54, 54, 
+	54, 54, 54, 1, 1, 1, 1, 1, 
+	1, 47, 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, 48, 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, 49, 1, 28, 
+	28, 28, 28, 28, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 1, 1, 1, 1, 1, 28, 1, 
+	1, 1, 1, 1, 1, 1, 1, 1, 
+	1, 29, 1, 55, 55, 1, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	1, 1, 1, 30, 1, 1, 31, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 1, 1, 32, 1, 55, 1, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 55, 55, 55, 55, 55, 55, 55, 
+	55, 1, 33, 1, 0
+};
+
+static const char _deserialize_text_trans_targs[] = {
+	1, 0, 13, 17, 26, 3, 18, 21, 
+	18, 21, 5, 19, 20, 19, 20, 22, 
+	25, 8, 9, 12, 9, 12, 10, 11, 
+	23, 24, 23, 24, 14, 2, 6, 7, 
+	15, 16, 14, 15, 16, 17, 14, 4, 
+	15, 16, 14, 15, 16, 14, 2, 7, 
+	15, 16, 14, 2, 15, 16, 25, 26
+};
+
+static const char _deserialize_text_trans_actions[] = {
+	0, 0, 1, 1, 1, 2, 2, 2, 
+	0, 0, 2, 2, 2, 0, 0, 2, 
+	2, 2, 2, 2, 0, 0, 3, 2, 
+	2, 2, 0, 0, 4, 5, 5, 5, 
+	4, 4, 0, 0, 0, 0, 6, 7, 
+	6, 6, 8, 8, 8, 9, 10, 10, 
+	9, 9, 11, 12, 11, 11, 0, 0
+};
+
+static const char _deserialize_text_eof_actions[] = {
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 4, 0, 0, 
+	0, 4, 6, 8, 8, 6, 9, 11, 
+	11, 9, 4
+};
+
+static const int deserialize_text_start = 1;
+static const int deserialize_text_first_final = 13;
+static const int deserialize_text_error = 0;
+
+static const int deserialize_text_en_main = 1;
+
+
+#line 91 "hb-buffer-deserialize-text.rl"
+
+
+static hb_bool_t
+_hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer,
+				    const char *buf,
+				    unsigned int buf_len,
+				    const char **end_ptr,
+				    hb_font_t *font)
+{
+  const char *p = buf, *pe = buf + buf_len;
+
+  /* Ensure we have positions. */
+  (void) hb_buffer_get_glyph_positions (buffer, NULL);
+
+  while (p < pe && ISSPACE (*p))
+    p++;
+  if (p < pe && *p == (buffer->len ? '|' : '['))
+  {
+    *end_ptr = ++p;
+  }
+
+  const char *eof = pe, *tok = NULL;
+  int cs;
+  hb_glyph_info_t info;
+  hb_glyph_position_t pos;
+  
+#line 343 "hb-buffer-deserialize-text.hh.tmp"
+	{
+	cs = deserialize_text_start;
+	}
+
+#line 348 "hb-buffer-deserialize-text.hh.tmp"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+	if ( cs == 0 )
+		goto _out;
+_resume:
+	_keys = _deserialize_text_trans_keys + (cs<<1);
+	_inds = _deserialize_text_indicies + _deserialize_text_index_offsets[cs];
+
+	_slen = _deserialize_text_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=(*p) &&
+		(*p) <= _keys[1] ?
+		(*p) - _keys[0] : _slen ];
+
+	cs = _deserialize_text_trans_targs[_trans];
+
+	if ( _deserialize_text_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _deserialize_text_trans_actions[_trans] ) {
+	case 2:
+#line 51 "hb-buffer-deserialize-text.rl"
+	{
+	tok = p;
+}
+	break;
+	case 5:
+#line 55 "hb-buffer-deserialize-text.rl"
+	{
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+	break;
+	case 10:
+#line 62 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+	break;
+	case 3:
+#line 63 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.x_offset )) return false; }
+	break;
+	case 12:
+#line 64 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+	break;
+	case 7:
+#line 65 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+	break;
+	case 1:
+#line 38 "hb-buffer-deserialize-text.rl"
+	{
+	memset (&info, 0, sizeof (info));
+	memset (&pos , 0, sizeof (pos ));
+}
+#line 51 "hb-buffer-deserialize-text.rl"
+	{
+	tok = p;
+}
+	break;
+	case 4:
+#line 55 "hb-buffer-deserialize-text.rl"
+	{
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 9:
+#line 62 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 11:
+#line 64 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 6:
+#line 65 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 8:
+#line 66 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+#line 480 "hb-buffer-deserialize-text.hh.tmp"
+	}
+
+_again:
+	if ( cs == 0 )
+		goto _out;
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	switch ( _deserialize_text_eof_actions[cs] ) {
+	case 4:
+#line 55 "hb-buffer-deserialize-text.rl"
+	{
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 9:
+#line 62 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_uint (tok, p, &info.cluster )) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 11:
+#line 64 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 6:
+#line 65 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+	case 8:
+#line 66 "hb-buffer-deserialize-text.rl"
+	{ if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+#line 43 "hb-buffer-deserialize-text.rl"
+	{
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+	break;
+#line 557 "hb-buffer-deserialize-text.hh.tmp"
+	}
+	}
+
+	_out: {}
+	}
+
+#line 119 "hb-buffer-deserialize-text.rl"
+
+
+  *end_ptr = p;
+
+  return p == pe && *(p-1) != ']';
+}
+
+#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */
diff --git a/src/hb-buffer-deserialize-text.rl b/src/hb-buffer-deserialize-text.rl
new file mode 100644
index 0000000..8856580
--- /dev/null
+++ b/src/hb-buffer-deserialize-text.rl
@@ -0,0 +1,126 @@
+/*
+ * Copyright © 2013  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_BUFFER_DESERIALIZE_TEXT_HH
+#define HB_BUFFER_DESERIALIZE_TEXT_HH
+
+#include "hb-private.hh"
+
+%%{
+
+machine deserialize_text;
+alphtype unsigned char;
+write data;
+
+action clear_item {
+	memset (&info, 0, sizeof (info));
+	memset (&pos , 0, sizeof (pos ));
+}
+
+action add_item {
+	buffer->add_info (info);
+	if (buffer->in_error)
+	  return false;
+	buffer->pos[buffer->len - 1] = pos;
+	*end_ptr = p;
+}
+
+action tok {
+	tok = p;
+}
+
+action parse_glyph {
+	if (!hb_font_glyph_from_string (font,
+					tok, p - tok,
+					&info.codepoint))
+	  return false;
+}
+
+action parse_cluster   { if (!parse_uint (tok, p, &info.cluster )) return false; }
+action parse_x_offset  { if (!parse_int  (tok, p, &pos.x_offset )) return false; }
+action parse_y_offset  { if (!parse_int  (tok, p, &pos.y_offset )) return false; }
+action parse_x_advance { if (!parse_int  (tok, p, &pos.x_advance)) return false; }
+action parse_y_advance { if (!parse_int  (tok, p, &pos.y_advance)) return false; }
+
+unum	= '0' | [1-9] digit*;
+num	= '-'? unum;
+
+glyph_id = unum;
+glyph_name = alpha (alnum|'_'|'.'|'-')*;
+
+glyph	= (glyph_id | glyph_name) >tok %parse_glyph;
+cluster	= '=' (unum >tok %parse_cluster);
+offsets	= '@' (num >tok %parse_x_offset)   ',' (num >tok %parse_y_offset );
+advances= '+' (num >tok %parse_x_advance) (',' (num >tok %parse_y_advance))?;
+item	=
+	(
+		glyph
+		cluster?
+		offsets?
+		advances?
+	)
+	>clear_item
+	%add_item
+	;
+
+main := space* item (space* '|' space* item)* space* ('|'|']')?;
+
+}%%
+
+static hb_bool_t
+_hb_buffer_deserialize_glyphs_text (hb_buffer_t *buffer,
+				    const char *buf,
+				    unsigned int buf_len,
+				    const char **end_ptr,
+				    hb_font_t *font)
+{
+  const char *p = buf, *pe = buf + buf_len;
+
+  /* Ensure we have positions. */
+  (void) hb_buffer_get_glyph_positions (buffer, NULL);
+
+  while (p < pe && ISSPACE (*p))
+    p++;
+  if (p < pe && *p == (buffer->len ? '|' : '['))
+  {
+    *end_ptr = ++p;
+  }
+
+  const char *eof = pe, *tok = NULL;
+  int cs;
+  hb_glyph_info_t info;
+  hb_glyph_position_t pos;
+  %%{
+    write init;
+    write exec;
+  }%%
+
+  *end_ptr = p;
+
+  return p == pe && *(p-1) != ']';
+}
+
+#endif /* HB_BUFFER_DESERIALIZE_TEXT_HH */
diff --git a/src/hb-buffer-private.hh b/src/hb-buffer-private.hh
index 13cf4bb..387ebd9 100644
--- a/src/hb-buffer-private.hh
+++ b/src/hb-buffer-private.hh
@@ -110,6 +110,7 @@
 
   HB_INTERNAL void add (hb_codepoint_t  codepoint,
 			unsigned int    cluster);
+  HB_INTERNAL void add_info (const hb_glyph_info_t &glyph_info);
 
   HB_INTERNAL void reverse_range (unsigned int start, unsigned int end);
   HB_INTERNAL void reverse (void);
@@ -128,7 +129,7 @@
   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);
+  HB_INTERNAL void output_info (const 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.
diff --git a/src/hb-buffer-serialize.cc b/src/hb-buffer-serialize.cc
new file mode 100644
index 0000000..dc47ba7
--- /dev/null
+++ b/src/hb-buffer-serialize.cc
@@ -0,0 +1,336 @@
+/*
+ * Copyright © 2012,2013  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
+ */
+
+#include "hb-buffer-private.hh"
+
+
+static const char *serialize_formats[] = {
+  "text",
+  "json",
+  NULL
+};
+
+const char **
+hb_buffer_serialize_list_formats (void)
+{
+  return serialize_formats;
+}
+
+hb_buffer_serialize_format_t
+hb_buffer_serialize_format_from_string (const char *str, int len)
+{
+  /* Upper-case it. */
+  return (hb_buffer_serialize_format_t) (hb_tag_from_string (str, len) & ~0x20202020);
+}
+
+const char *
+hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format)
+{
+  switch (format)
+  {
+    case HB_BUFFER_SERIALIZE_FORMAT_TEXT:	return serialize_formats[0];
+    case HB_BUFFER_SERIALIZE_FORMAT_JSON:	return serialize_formats[1];
+    default:
+    case HB_BUFFER_SERIALIZE_FORMAT_INVALID:	return NULL;
+  }
+}
+
+static unsigned int
+_hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer,
+				  unsigned int start,
+				  unsigned int end,
+				  char *buf,
+				  unsigned int buf_size,
+				  unsigned int *buf_consumed,
+				  hb_font_t *font,
+				  hb_buffer_serialize_flags_t flags)
+{
+  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+  hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL);
+
+  *buf_consumed = 0;
+  for (unsigned int i = start; i < end; i++)
+  {
+    char b[1024];
+    char *p = b;
+
+    /* In the following code, we know b is large enough that no overflow can happen. */
+
+#define APPEND(s) HB_STMT_START { strcpy (p, s); p += strlen (s); } HB_STMT_END
+
+    if (i)
+      *p++ = ',';
+
+    *p++ = '{';
+
+    APPEND ("\"g\":");
+    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES))
+    {
+      char g[128];
+      hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g));
+      *p++ = '"';
+      for (char *q = g; *q; q++) {
+        if (*q == '"')
+	  *p++ = '\\';
+	*p++ = *q;
+      }
+      *p++ = '"';
+    }
+    else
+      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint);
+
+    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
+      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster);
+    }
+
+    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
+    {
+      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
+		     pos[i].x_offset, pos[i].y_offset);
+      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d",
+		     pos[i].x_advance, pos[i].y_advance);
+    }
+
+    *p++ = '}';
+
+    if (buf_size > (p - b))
+    {
+      unsigned int l = p - b;
+      memcpy (buf, b, l);
+      buf += l;
+      buf_size -= l;
+      *buf_consumed += l;
+      *buf = '\0';
+    } else
+      return i - start;
+  }
+
+  return end - start;
+}
+
+static unsigned int
+_hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer,
+				  unsigned int start,
+				  unsigned int end,
+				  char *buf,
+				  unsigned int buf_size,
+				  unsigned int *buf_consumed,
+				  hb_font_t *font,
+				  hb_buffer_serialize_flags_t flags)
+{
+  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
+  hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL);
+
+  *buf_consumed = 0;
+  for (unsigned int i = start; i < end; i++)
+  {
+    char b[1024];
+    char *p = b;
+
+    /* In the following code, we know b is large enough that no overflow can happen. */
+
+    if (i)
+      *p++ = '|';
+
+    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES))
+    {
+      hb_font_glyph_to_string (font, info[i].codepoint, p, 128);
+      p += strlen (p);
+    }
+    else
+      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint);
+
+    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
+      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster);
+    }
+
+    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
+    {
+      if (pos[i].x_offset || pos[i].y_offset)
+	p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset);
+
+      *p++ = '+';
+      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance);
+      if (pos->y_advance)
+	p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance);
+    }
+
+    if (buf_size > (p - b))
+    {
+      unsigned int l = p - b;
+      memcpy (buf, b, l);
+      buf += l;
+      buf_size -= l;
+      *buf_consumed += l;
+      *buf = '\0';
+    } else
+      return i - start;
+  }
+
+  return end - start;
+}
+
+/* Returns number of items, starting at start, that were serialized. */
+unsigned int
+hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
+			    unsigned int start,
+			    unsigned int end,
+			    char *buf,
+			    unsigned int buf_size,
+			    unsigned int *buf_consumed, /* May be NULL */
+			    hb_font_t *font, /* May be NULL */
+			    hb_buffer_serialize_format_t format,
+			    hb_buffer_serialize_flags_t flags)
+{
+  assert (start <= end && end <= buffer->len);
+
+  unsigned int sconsumed;
+  if (!buf_consumed)
+    buf_consumed = &sconsumed;
+  *buf_consumed = 0;
+
+  assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) ||
+	  buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS);
+
+  if (unlikely (start == end))
+    return 0;
+
+  if (!font)
+    font = hb_font_get_empty ();
+
+  switch (format)
+  {
+    case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
+      return _hb_buffer_serialize_glyphs_text (buffer, start, end,
+					       buf, buf_size, buf_consumed,
+					       font, flags);
+
+    case HB_BUFFER_SERIALIZE_FORMAT_JSON:
+      return _hb_buffer_serialize_glyphs_json (buffer, start, end,
+					       buf, buf_size, buf_consumed,
+					       font, flags);
+
+    default:
+    case HB_BUFFER_SERIALIZE_FORMAT_INVALID:
+      return 0;
+
+  }
+}
+
+
+static hb_bool_t
+parse_uint (const char *pp, const char *end, uint32_t *pv)
+{
+  char buf[32];
+  unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp));
+  strncpy (buf, pp, len);
+  buf[len] = '\0';
+
+  char *p = buf;
+  char *pend = p;
+  uint32_t v;
+
+  errno = 0;
+  v = strtol (p, &pend, 10);
+  if (errno || p == pend || pend - p != end - pp)
+    return false;
+
+  *pv = v;
+  return true;
+}
+
+static hb_bool_t
+parse_int (const char *pp, const char *end, int32_t *pv)
+{
+  char buf[32];
+  unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - pp));
+  strncpy (buf, pp, len);
+  buf[len] = '\0';
+
+  char *p = buf;
+  char *pend = p;
+  int32_t v;
+
+  errno = 0;
+  v = strtol (p, &pend, 10);
+  if (errno || p == pend || pend - p != end - pp)
+    return false;
+
+  *pv = v;
+  return true;
+}
+
+#include "hb-buffer-deserialize-json.hh"
+#include "hb-buffer-deserialize-text.hh"
+
+hb_bool_t
+hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
+			      const char *buf,
+			      int buf_len, /* -1 means nul-terminated */
+			      const char **end_ptr, /* May be NULL */
+			      hb_font_t *font, /* May be NULL */
+			      hb_buffer_serialize_format_t format)
+{
+  const char *end;
+  if (!end_ptr)
+    end_ptr = &end;
+  *end_ptr = buf;
+
+  assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) ||
+	  buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS);
+
+  if (buf_len == -1)
+    buf_len = strlen (buf);
+
+  if (!buf_len)
+  {
+    *end_ptr = buf;
+    return false;
+  }
+
+  hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_GLYPHS);
+
+  if (!font)
+    font = hb_font_get_empty ();
+
+  switch (format)
+  {
+    case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
+      return _hb_buffer_deserialize_glyphs_text (buffer,
+						 buf, buf_len, end_ptr,
+						 font);
+
+    case HB_BUFFER_SERIALIZE_FORMAT_JSON:
+      return _hb_buffer_deserialize_glyphs_json (buffer,
+						 buf, buf_len, end_ptr,
+						 font);
+
+    default:
+    case HB_BUFFER_SERIALIZE_FORMAT_INVALID:
+      return false;
+
+  }
+}
diff --git a/src/hb-buffer.cc b/src/hb-buffer.cc
index c7860e9..4e26250 100644
--- a/src/hb-buffer.cc
+++ b/src/hb-buffer.cc
@@ -215,6 +215,17 @@
 }
 
 void
+hb_buffer_t::add_info (const hb_glyph_info_t &glyph_info)
+{
+  if (unlikely (!ensure (len + 1))) return;
+
+  info[len] = glyph_info;
+
+  len++;
+}
+
+
+void
 hb_buffer_t::remove_output (void)
 {
   if (unlikely (hb_object_is_inert (this)))
@@ -315,7 +326,7 @@
 }
 
 void
-hb_buffer_t::output_info (hb_glyph_info_t &glyph_info)
+hb_buffer_t::output_info (const hb_glyph_info_t &glyph_info)
 {
   if (unlikely (!make_room_for (0, 1))) return;
 
@@ -1064,231 +1075,3 @@
     }
   normalize_glyphs_cluster (buffer, start, end, backward);
 }
-
-
-/*
- * Serialize
- */
-
-static const char *serialize_formats[] = {
-  "text",
-  "json",
-  NULL
-};
-
-const char **
-hb_buffer_serialize_list_formats (void)
-{
-  return serialize_formats;
-}
-
-hb_buffer_serialize_format_t
-hb_buffer_serialize_format_from_string (const char *str, int len)
-{
-  /* Upper-case it. */
-  return (hb_buffer_serialize_format_t) (hb_tag_from_string (str, len) & ~0x20202020);
-}
-
-const char *
-hb_buffer_serialize_format_to_string (hb_buffer_serialize_format_t format)
-{
-  switch (format)
-  {
-    case HB_BUFFER_SERIALIZE_FORMAT_TEXT:	return serialize_formats[0];
-    case HB_BUFFER_SERIALIZE_FORMAT_JSON:	return serialize_formats[1];
-    default:
-    case HB_BUFFER_SERIALIZE_FORMAT_INVALID:	return NULL;
-  }
-}
-
-static unsigned int
-_hb_buffer_serialize_glyphs_json (hb_buffer_t *buffer,
-				  unsigned int start,
-				  unsigned int end,
-				  char *buf,
-				  unsigned int buf_size,
-				  unsigned int *buf_consumed,
-				  hb_font_t *font,
-				  hb_buffer_serialize_flags_t flags)
-{
-  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
-  hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL);
-
-  *buf_consumed = 0;
-  for (unsigned int i = start; i < end; i++)
-  {
-    char b[1024];
-    char *p = b;
-
-    /* In the following code, we know b is large enough that no overflow can happen. */
-
-#define APPEND(s) HB_STMT_START { strcpy (p, s); p += strlen (s); } HB_STMT_END
-
-    if (i)
-      *p++ = ',';
-
-    *p++ = '{';
-
-    APPEND ("\"g\":");
-    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES))
-    {
-      char g[128];
-      hb_font_glyph_to_string (font, info[i].codepoint, g, sizeof (g));
-      *p++ = '"';
-      for (char *q = g; *q; q++) {
-        if (*q == '"')
-	  *p++ = '\\';
-	*p++ = *q;
-      }
-      *p++ = '"';
-    }
-    else
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint);
-
-    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"cl\":%u", info[i].cluster);
-    }
-
-    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
-    {
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"dx\":%d,\"dy\":%d",
-		     pos[i].x_offset, pos[i].y_offset);
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",\"ax\":%d,\"ay\":%d",
-		     pos[i].x_advance, pos[i].y_advance);
-    }
-
-    *p++ = '}';
-
-    if (buf_size > (p - b))
-    {
-      unsigned int l = p - b;
-      memcpy (buf, b, l);
-      buf += l;
-      buf_size -= l;
-      *buf_consumed += l;
-      *buf = '\0';
-    } else
-      return i - start;
-  }
-
-  return end - start;
-}
-
-static unsigned int
-_hb_buffer_serialize_glyphs_text (hb_buffer_t *buffer,
-				  unsigned int start,
-				  unsigned int end,
-				  char *buf,
-				  unsigned int buf_size,
-				  unsigned int *buf_consumed,
-				  hb_font_t *font,
-				  hb_buffer_serialize_flags_t flags)
-{
-  hb_glyph_info_t *info = hb_buffer_get_glyph_infos (buffer, NULL);
-  hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, NULL);
-  hb_direction_t direction = hb_buffer_get_direction (buffer);
-
-  *buf_consumed = 0;
-  for (unsigned int i = start; i < end; i++)
-  {
-    char b[1024];
-    char *p = b;
-
-    /* In the following code, we know b is large enough that no overflow can happen. */
-
-    if (i)
-      *p++ = '|';
-
-    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_GLYPH_NAMES))
-    {
-      hb_font_glyph_to_string (font, info[i].codepoint, p, 128);
-      p += strlen (p);
-    }
-    else
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "%u", info[i].codepoint);
-
-    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_CLUSTERS)) {
-      p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "=%u", info[i].cluster);
-    }
-
-    if (!(flags & HB_BUFFER_SERIALIZE_FLAG_NO_POSITIONS))
-    {
-      if (pos[i].x_offset || pos[i].y_offset)
-	p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "@%d,%d", pos[i].x_offset, pos[i].y_offset);
-
-      *p++ = '+';
-      if (HB_DIRECTION_IS_HORIZONTAL (direction) || pos[i].x_advance)
-	p += snprintf (p, ARRAY_LENGTH (b) - (p - b), "%d", pos[i].x_advance);
-      if (HB_DIRECTION_IS_VERTICAL (direction) || pos->y_advance)
-	p += snprintf (p, ARRAY_LENGTH (b) - (p - b), ",%d", pos[i].y_advance);
-    }
-
-    if (buf_size > (p - b))
-    {
-      unsigned int l = p - b;
-      memcpy (buf, b, l);
-      buf += l;
-      buf_size -= l;
-      *buf_consumed += l;
-      *buf = '\0';
-    } else
-      return i - start;
-  }
-
-  return end - start;
-}
-
-/* Returns number of items, starting at start, that were serialized. */
-unsigned int
-hb_buffer_serialize_glyphs (hb_buffer_t *buffer,
-			    unsigned int start,
-			    unsigned int end,
-			    char *buf,
-			    unsigned int buf_size,
-			    unsigned int *buf_consumed,
-			    hb_font_t *font, /* May be NULL */
-			    hb_buffer_serialize_format_t format,
-			    hb_buffer_serialize_flags_t flags)
-{
-  assert (start <= end && end <= buffer->len);
-
-  *buf_consumed = 0;
-
-  assert ((!buffer->len && buffer->content_type == HB_BUFFER_CONTENT_TYPE_INVALID) ||
-	  buffer->content_type == HB_BUFFER_CONTENT_TYPE_GLYPHS);
-
-  if (unlikely (start == end))
-    return 0;
-
-  if (!font)
-    font = hb_font_get_empty ();
-
-  switch (format)
-  {
-    case HB_BUFFER_SERIALIZE_FORMAT_TEXT:
-      return _hb_buffer_serialize_glyphs_text (buffer, start, end,
-					       buf, buf_size, buf_consumed,
-					       font, flags);
-
-    case HB_BUFFER_SERIALIZE_FORMAT_JSON:
-      return _hb_buffer_serialize_glyphs_json (buffer, start, end,
-					       buf, buf_size, buf_consumed,
-					       font, flags);
-
-    default:
-    case HB_BUFFER_SERIALIZE_FORMAT_INVALID:
-      return 0;
-
-  }
-}
-
-hb_bool_t
-hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
-			      const char *buf,
-			      unsigned int buf_len,
-			      unsigned int *buf_consumed,
-			      hb_font_t *font, /* May be NULL */
-			      hb_buffer_serialize_format_t format)
-{
-  return false;
-}
diff --git a/src/hb-buffer.h b/src/hb-buffer.h
index 5386e36..55a4045 100644
--- a/src/hb-buffer.h
+++ b/src/hb-buffer.h
@@ -304,7 +304,7 @@
 			    unsigned int end,
 			    char *buf,
 			    unsigned int buf_size,
-			    unsigned int *buf_consumed,
+			    unsigned int *buf_consumed, /* May be NULL */
 			    hb_font_t *font, /* May be NULL */
 			    hb_buffer_serialize_format_t format,
 			    hb_buffer_serialize_flags_t flags);
@@ -312,8 +312,8 @@
 hb_bool_t
 hb_buffer_deserialize_glyphs (hb_buffer_t *buffer,
 			      const char *buf,
-			      unsigned int buf_len,
-			      unsigned int *buf_consumed,
+			      int buf_len, /* -1 means nul-terminated */
+			      const char **end_ptr, /* May be NULL */
 			      hb_font_t *font, /* May be NULL */
 			      hb_buffer_serialize_format_t format);
 
diff --git a/src/hb-common.cc b/src/hb-common.cc
index 9422555..540d252 100644
--- a/src/hb-common.cc
+++ b/src/hb-common.cc
@@ -36,6 +36,24 @@
 #include <locale.h>
 
 
+/* hb_options_t */
+
+hb_options_union_t _hb_options;
+
+void
+_hb_options_init (void)
+{
+  hb_options_union_t u;
+  u.i = 0;
+  u.opts.initialized = 1;
+
+  char *c = getenv ("HB_OPTIONS");
+  u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
+
+  /* This is idempotent and threadsafe. */
+  _hb_options = u;
+}
+
 
 /* hb_tag_t */
 
@@ -177,7 +195,7 @@
 
 static hb_language_item_t *langs;
 
-static
+static inline
 void free_langs (void)
 {
   while (langs) {
@@ -414,5 +432,3 @@
 {
   return HB_VERSION_CHECK (major, minor, micro);
 }
-
-
diff --git a/src/hb-fallback-shape.cc b/src/hb-fallback-shape.cc
index bdc8a80..1a1fcfb 100644
--- a/src/hb-fallback-shape.cc
+++ b/src/hb-fallback-shape.cc
@@ -98,7 +98,6 @@
   hb_codepoint_t space;
   font->get_glyph (' ', 0, &space);
 
-  buffer->guess_segment_properties ();
   buffer->clear_positions ();
 
   unsigned int count = buffer->len;
diff --git a/src/hb-ft.cc b/src/hb-ft.cc
index 6198185..978230c 100644
--- a/src/hb-ft.cc
+++ b/src/hb-ft.cc
@@ -53,14 +53,7 @@
  *
  *   - We don't handle / allow for emboldening / obliqueing.
  *
- *   - Rounding, etc?
- *
- *   - In the future, we should add constructors to create fonts in font space.
- *
- *   - I believe transforms are not correctly implemented.  FreeType does not
- *     provide any API to get to the transform/delta set on the face. :(
- *
- *   - Always use FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH?
+ *   - In the future, we should add constructors to create fonts in font space?
  *
  *   - FT_Load_Glyph() is exteremely costly.  Do something about it?
  */
@@ -242,8 +235,8 @@
   FT_Face ft_face = (FT_Face) font_data;
 
   hb_bool_t ret = !FT_Get_Glyph_Name (ft_face, glyph, name, size);
-  if (!ret || (size && !*name))
-    snprintf (name, size, "gid%u", glyph);
+  if (ret && (size && !*name))
+    ret = false;
 
   return ret;
 }
@@ -403,7 +396,7 @@
 
 static FT_Library ft_library;
 
-static
+static inline
 void free_ft_library (void)
 {
   FT_Done_FreeType (ft_library);
diff --git a/src/hb-mutex-private.hh b/src/hb-mutex-private.hh
index 5b3a17e..0fb21c2 100644
--- a/src/hb-mutex-private.hh
+++ b/src/hb-mutex-private.hh
@@ -42,7 +42,7 @@
 #if 0
 
 
-#elif !defined(HB_NO_MT) && defined(_MSC_VER) || defined(__MINGW32__)
+#elif !defined(HB_NO_MT) && (defined(_WIN32) || defined(__CYGWIN__))
 
 #define WIN32_LEAN_AND_MEAN
 #include <windows.h>
diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index 5bfeb16..cd1163e 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -171,6 +171,10 @@
 	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
 	 "");
 
+/* This limits sanitizing time on really broken fonts. */
+#ifndef HB_SANITIZE_MAX_EDITS
+#define HB_SANITIZE_MAX_EDITS 100
+#endif
 
 struct hb_sanitize_context_t
 {
@@ -178,7 +182,7 @@
   static const unsigned int max_debug_depth = HB_DEBUG_SANITIZE;
   typedef bool return_t;
   template <typename T>
-  inline return_t process (const T &obj) { return obj.sanitize (this); }
+  inline return_t dispatch (const T &obj) { return obj.sanitize (this); }
   static return_t default_return_value (void) { return true; }
   bool stop_sublookup_iteration (const return_t r HB_UNUSED) const { return false; }
 
@@ -247,6 +251,9 @@
 
   inline bool may_edit (const void *base HB_UNUSED, unsigned int len HB_UNUSED)
   {
+    if (this->edit_count >= HB_SANITIZE_MAX_EDITS)
+      return false;
+
     const char *p = (const char *) base;
     this->edit_count++;
 
@@ -404,7 +411,7 @@
   template <typename Type>
   inline Type *allocate_size (unsigned int size)
   {
-    if (unlikely (this->ran_out_of_room || this->end - this->head < size)) {
+    if (unlikely (this->ran_out_of_room || this->end - this->head < ptrdiff_t (size))) {
       this->ran_out_of_room = true;
       return NULL;
     }
@@ -616,10 +623,20 @@
 DEFINE_NULL_DATA (Index, "\xff\xff");
 
 /* Offset to a table, same as uint16 (length = 16 bits), Null offset = 0x0000 */
-typedef USHORT Offset;
+struct Offset : USHORT
+{
+  inline bool is_null (void) const { return 0 == *this; }
+  public:
+  DEFINE_SIZE_STATIC (2);
+};
 
 /* LongOffset to a table, same as uint32 (length = 32 bits), Null offset = 0x00000000 */
-typedef ULONG LongOffset;
+struct LongOffset : ULONG
+{
+  inline bool is_null (void) const { return 0 == *this; }
+  public:
+  DEFINE_SIZE_STATIC (4);
+};
 
 
 /* CheckSum */
@@ -674,11 +691,6 @@
     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)
   {
diff --git a/src/hb-ot-layout-gpos-table.hh b/src/hb-ot-layout-gpos-table.hh
index d27ce4f..4413927 100644
--- a/src/hb-ot-layout-gpos-table.hh
+++ b/src/hb-ot-layout-gpos-table.hh
@@ -336,8 +336,10 @@
 
 struct AnchorMatrix
 {
-  inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols) const {
+  inline const Anchor& get_anchor (unsigned int row, unsigned int col, unsigned int cols, bool *found) const {
+    *found = false;
     if (unlikely (row >= rows || col >= cols)) return Null(Anchor);
+    *found = !matrix[row * cols + col].is_null ();
     return this+matrix[row * cols + col];
   }
 
@@ -392,7 +394,11 @@
     unsigned int mark_class = record.klass;
 
     const Anchor& mark_anchor = this + record.markAnchor;
-    const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count);
+    bool found;
+    const Anchor& glyph_anchor = anchors.get_anchor (glyph_index, mark_class, class_count, &found);
+    /* If this subtable doesn't have an anchor for this base and this class,
+     * return false such that the subsequent subtables have a chance at it. */
+    if (unlikely (!found)) return TRACE_RETURN (false);
 
     hb_position_t mark_x, mark_y, base_x, base_y;
 
@@ -513,12 +519,12 @@
 struct SinglePos
 {
   template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
+  inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_PROCESS (this);
+    TRACE_DISPATCH (this);
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->process (u.format1));
-    case 2: return TRACE_RETURN (c->process (u.format2));
+    case 1: return TRACE_RETURN (c->dispatch (u.format1));
+    case 2: return TRACE_RETURN (c->dispatch (u.format2));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
@@ -590,6 +596,7 @@
     unsigned int count = len;
     for (unsigned int i = 0; i < count; i++)
     {
+      /* TODO bsearch */
       if (c->buffer->info[pos].codepoint == record->secondGlyph)
       {
 	valueFormats[0].apply_value (c->font, c->direction, this,
@@ -652,7 +659,7 @@
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
+    hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
     if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
     unsigned int index = (this+coverage).get_coverage  (c->buffer->cur().codepoint);
@@ -724,7 +731,7 @@
   inline bool apply (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
+    hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
     if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
     unsigned int index = (this+coverage).get_coverage  (c->buffer->cur().codepoint);
@@ -803,12 +810,12 @@
 struct PairPos
 {
   template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
+  inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_PROCESS (this);
+    TRACE_DISPATCH (this);
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->process (u.format1));
-    case 2: return TRACE_RETURN (c->process (u.format2));
+    case 1: return TRACE_RETURN (c->dispatch (u.format1));
+    case 2: return TRACE_RETURN (c->dispatch (u.format2));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
@@ -872,9 +879,9 @@
     TRACE_APPLY (this);
 
     /* We don't handle mark glyphs here. */
-    if (c->property & HB_OT_LAYOUT_GLYPH_PROPS_MARK) return TRACE_RETURN (false);
+    if (c->buffer->cur().glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MARK) return TRACE_RETURN (false);
 
-    hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
+    hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, 1);
     if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
     const EntryExitRecord &this_record = entryExitRecord[(this+coverage).get_coverage  (c->buffer->cur().codepoint)];
@@ -969,11 +976,11 @@
 struct CursivePos
 {
   template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
+  inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_PROCESS (this);
+    TRACE_DISPATCH (this);
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->process (u.format1));
+    case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
@@ -1022,17 +1029,17 @@
     if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a non-mark glyph */
-    unsigned int property;
-    hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
+    hb_apply_context_t::skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
+    skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
     do {
-      if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false);
+      if (!skippy_iter.prev ()) return TRACE_RETURN (false);
       /* We only want to attach to the first of a MultipleSubst sequence.  Reject others. */
       if (0 == get_lig_comp (c->buffer->info[skippy_iter.idx])) break;
       skippy_iter.reject ();
     } while (1);
 
     /* The following assertion is too strong, so we've disabled it. */
-    if (!(property & HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH)) {/*return TRACE_RETURN (false);*/}
+    if (!(c->buffer->info[skippy_iter.idx].glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH)) {/*return TRACE_RETURN (false);*/}
 
     unsigned int base_index = (this+baseCoverage).get_coverage  (c->buffer->info[skippy_iter.idx].codepoint);
     if (base_index == NOT_COVERED) return TRACE_RETURN (false);
@@ -1068,11 +1075,11 @@
 struct MarkBasePos
 {
   template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
+  inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_PROCESS (this);
+    TRACE_DISPATCH (this);
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->process (u.format1));
+    case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
@@ -1126,12 +1133,12 @@
     if (likely (mark_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a non-mark glyph */
-    unsigned int property;
-    hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
-    if (!skippy_iter.prev (&property, LookupFlag::IgnoreMarks)) return TRACE_RETURN (false);
+    hb_apply_context_t::skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
+    skippy_iter.set_lookup_props (LookupFlag::IgnoreMarks);
+    if (!skippy_iter.prev ()) return TRACE_RETURN (false);
 
     /* The following assertion is too strong, so we've disabled it. */
-    if (!(property & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE)) {/*return TRACE_RETURN (false);*/}
+    if (!(c->buffer->info[skippy_iter.idx].glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE)) {/*return TRACE_RETURN (false);*/}
 
     unsigned int j = skippy_iter.idx;
     unsigned int lig_index = (this+ligatureCoverage).get_coverage  (c->buffer->info[j].codepoint);
@@ -1189,11 +1196,11 @@
 struct MarkLigPos
 {
   template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
+  inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_PROCESS (this);
+    TRACE_DISPATCH (this);
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->process (u.format1));
+    case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
@@ -1242,11 +1249,11 @@
     if (likely (mark1_index == NOT_COVERED)) return TRACE_RETURN (false);
 
     /* now we search backwards for a suitable mark glyph until a non-mark glyph */
-    unsigned int property;
-    hb_apply_context_t::mark_skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
-    if (!skippy_iter.prev (&property)) return TRACE_RETURN (false);
+    hb_apply_context_t::skipping_backward_iterator_t skippy_iter (c, c->buffer->idx, 1);
+    skippy_iter.set_lookup_props (c->lookup_props & ~LookupFlag::IgnoreFlags);
+    if (!skippy_iter.prev ()) return TRACE_RETURN (false);
 
-    if (!(property & HB_OT_LAYOUT_GLYPH_PROPS_MARK)) return TRACE_RETURN (false);
+    if (!(c->buffer->info[skippy_iter.idx].glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MARK)) { return TRACE_RETURN (false); }
 
     unsigned int j = skippy_iter.idx;
 
@@ -1308,11 +1315,11 @@
 struct MarkMarkPos
 {
   template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
+  inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_PROCESS (this);
+    TRACE_DISPATCH (this);
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->process (u.format1));
+    case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
@@ -1367,19 +1374,19 @@
   };
 
   template <typename context_t>
-  inline typename context_t::return_t process (context_t *c, unsigned int lookup_type) const
+  inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
   {
-    TRACE_PROCESS (this);
+    TRACE_DISPATCH (this);
     switch (lookup_type) {
-    case Single:		return TRACE_RETURN (u.single.process (c));
-    case Pair:			return TRACE_RETURN (u.pair.process (c));
-    case Cursive:		return TRACE_RETURN (u.cursive.process (c));
-    case MarkBase:		return TRACE_RETURN (u.markBase.process (c));
-    case MarkLig:		return TRACE_RETURN (u.markLig.process (c));
-    case MarkMark:		return TRACE_RETURN (u.markMark.process (c));
-    case Context:		return TRACE_RETURN (u.context.process (c));
-    case ChainContext:		return TRACE_RETURN (u.chainContext.process (c));
-    case Extension:		return TRACE_RETURN (u.extension.process (c));
+    case Single:		return TRACE_RETURN (u.single.dispatch (c));
+    case Pair:			return TRACE_RETURN (u.pair.dispatch (c));
+    case Cursive:		return TRACE_RETURN (u.cursive.dispatch (c));
+    case MarkBase:		return TRACE_RETURN (u.markBase.dispatch (c));
+    case MarkLig:		return TRACE_RETURN (u.markLig.dispatch (c));
+    case MarkMark:		return TRACE_RETURN (u.markMark.dispatch (c));
+    case Context:		return TRACE_RETURN (u.context.dispatch (c));
+    case ChainContext:		return TRACE_RETURN (u.chainContext.dispatch (c));
+    case Extension:		return TRACE_RETURN (u.extension.dispatch (c));
     default:			return TRACE_RETURN (c->default_return_value ());
     }
   }
@@ -1427,27 +1434,11 @@
   inline const PosLookupSubTable& get_subtable (unsigned int i) const
   { return this+CastR<OffsetArrayOf<PosLookupSubTable> > (subTable)[i]; }
 
-  template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
-  {
-    TRACE_PROCESS (this);
-    unsigned int lookup_type = get_type ();
-    unsigned int count = get_subtable_count ();
-    for (unsigned int i = 0; i < count; i++) {
-      typename context_t::return_t r = get_subtable (i).process (c, lookup_type);
-      if (c->stop_sublookup_iteration (r))
-        return TRACE_RETURN (r);
-    }
-    return TRACE_RETURN (c->default_return_value ());
-  }
-  template <typename context_t>
-  static inline typename context_t::return_t process_recurse_func (context_t *c, unsigned int lookup_index);
-
   inline hb_collect_glyphs_context_t::return_t collect_glyphs_lookup (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
     c->set_recurse_func (NULL);
-    return TRACE_RETURN (process (c));
+    return TRACE_RETURN (dispatch (c));
   }
 
   template <typename set_t>
@@ -1457,7 +1448,7 @@
     const Coverage *last = NULL;
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++) {
-      const Coverage *coverage = &get_subtable (i).process (&c, get_type ());
+      const Coverage *coverage = &get_subtable (i).dispatch (&c, get_type ());
       if (coverage != last) {
         coverage->add_coverage (glyphs);
         last = coverage;
@@ -1468,9 +1459,9 @@
   inline bool apply_once (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props, &c->property))
+    if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props))
       return TRACE_RETURN (false);
-    return TRACE_RETURN (process (c));
+    return TRACE_RETURN (dispatch (c));
   }
 
   static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index);
@@ -1500,6 +1491,23 @@
     return ret;
   }
 
+  template <typename context_t>
+  static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
+
+  template <typename context_t>
+  inline typename context_t::return_t dispatch (context_t *c) const
+  {
+    TRACE_DISPATCH (this);
+    unsigned int lookup_type = get_type ();
+    unsigned int count = get_subtable_count ();
+    for (unsigned int i = 0; i < count; i++) {
+      typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type);
+      if (c->stop_sublookup_iteration (r))
+        return TRACE_RETURN (r);
+    }
+    return TRACE_RETURN (c->default_return_value ());
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE (this);
     if (unlikely (!Lookup::sanitize (c))) return TRACE_RETURN (false);
@@ -1522,7 +1530,7 @@
   { return CastR<PosLookup> (GSUBGPOS::get_lookup (i)); }
 
   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);
+  static inline void position_finish (hb_font_t *font, hb_buffer_t *buffer);
 
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE (this);
@@ -1555,17 +1563,13 @@
 }
 
 static void
-fix_mark_attachment (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, hb_bool_t zero_width_attached_marks)
+fix_mark_attachment (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction)
 {
   if (likely (!(pos[i].attach_lookback())))
     return;
 
   unsigned int j = i - pos[i].attach_lookback();
 
-  if (zero_width_attached_marks) {
-    pos[i].x_advance = 0;
-    pos[i].y_advance = 0;
-  }
   pos[i].x_offset += pos[j].x_offset;
   pos[i].y_offset += pos[j].y_offset;
 
@@ -1592,7 +1596,7 @@
 }
 
 void
-GPOS::position_finish (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer, hb_bool_t zero_width_attached_marks)
+GPOS::position_finish (hb_font_t *font HB_UNUSED, hb_buffer_t *buffer)
 {
   unsigned int len;
   hb_glyph_position_t *pos = hb_buffer_get_glyph_positions (buffer, &len);
@@ -1604,7 +1608,7 @@
 
   /* Handle attachments */
   for (unsigned int i = 0; i < len; i++)
-    fix_mark_attachment (pos, i, direction, zero_width_attached_marks);
+    fix_mark_attachment (pos, i, direction);
 
   HB_BUFFER_DEALLOCATE_VAR (buffer, syllable);
   HB_BUFFER_DEALLOCATE_VAR (buffer, lig_props);
@@ -1615,11 +1619,11 @@
 /* Out-of-class implementation for methods recursing */
 
 template <typename context_t>
-inline typename context_t::return_t PosLookup::process_recurse_func (context_t *c, unsigned int lookup_index)
+inline typename context_t::return_t PosLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
 {
   const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos);
   const PosLookup &l = gpos.get_lookup (lookup_index);
-  return l.process (c);
+  return l.dispatch (c);
 }
 
 inline bool PosLookup::apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index)
@@ -1627,11 +1631,9 @@
   const GPOS &gpos = *(hb_ot_layout_from_face (c->face)->gpos);
   const PosLookup &l = gpos.get_lookup (lookup_index);
   unsigned int saved_lookup_props = c->lookup_props;
-  unsigned int saved_property = c->property;
   c->set_lookup (l);
   bool ret = l.apply_once (c);
   c->lookup_props = saved_lookup_props;
-  c->property = saved_property;
   return ret;
 }
 
diff --git a/src/hb-ot-layout-gsub-table.hh b/src/hb-ot-layout-gsub-table.hh
index 2642acb..35d0729 100644
--- a/src/hb-ot-layout-gsub-table.hh
+++ b/src/hb-ot-layout-gsub-table.hh
@@ -192,17 +192,6 @@
 
 struct SingleSubst
 {
-  template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
-  {
-    TRACE_PROCESS (this);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (c->process (u.format1));
-    case 2: return TRACE_RETURN (c->process (u.format2));
-    default:return TRACE_RETURN (c->default_return_value ());
-    }
-  }
-
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &glyphs,
 			 Supplier<GlyphID> &substitutes,
@@ -230,6 +219,17 @@
     }
   }
 
+  template <typename context_t>
+  inline typename context_t::return_t dispatch (context_t *c) const
+  {
+    TRACE_DISPATCH (this);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (c->dispatch (u.format1));
+    case 2: return TRACE_RETURN (c->dispatch (u.format2));
+    default:return TRACE_RETURN (c->default_return_value ());
+    }
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
@@ -272,7 +272,8 @@
     TRACE_APPLY (this);
     if (unlikely (!substitute.len)) return TRACE_RETURN (false);
 
-    unsigned int klass = c->property & HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE ? HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
+    unsigned int klass = c->buffer->cur().glyph_props() &
+			 HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE ? HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH : 0;
     unsigned int count = substitute.len;
     for (unsigned int i = 0; i < count; i++) {
       set_lig_props_for_component (c->buffer->cur(), i);
@@ -384,16 +385,6 @@
 
 struct MultipleSubst
 {
-  template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
-  {
-    TRACE_PROCESS (this);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (c->process (u.format1));
-    default:return TRACE_RETURN (c->default_return_value ());
-    }
-  }
-
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &glyphs,
 			 Supplier<unsigned int> &substitute_len_list,
@@ -410,6 +401,16 @@
     }
   }
 
+  template <typename context_t>
+  inline typename context_t::return_t dispatch (context_t *c) const
+  {
+    TRACE_DISPATCH (this);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (c->dispatch (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
+    }
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
@@ -535,16 +536,6 @@
 
 struct AlternateSubst
 {
-  template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
-  {
-    TRACE_PROCESS (this);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (c->process (u.format1));
-    default:return TRACE_RETURN (c->default_return_value ());
-    }
-  }
-
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &glyphs,
 			 Supplier<unsigned int> &alternate_len_list,
@@ -561,6 +552,16 @@
     }
   }
 
+  template <typename context_t>
+  inline typename context_t::return_t dispatch (context_t *c) const
+  {
+    TRACE_DISPATCH (this);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (c->dispatch (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
+    }
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
@@ -637,9 +638,9 @@
     ligate_input (c,
 		  count,
 		  &component[1],
-		  ligGlyph,
 		  match_glyph,
 		  NULL,
+		  ligGlyph,
 		  is_mark_ligature,
 		  total_component_count);
 
@@ -840,16 +841,6 @@
 
 struct LigatureSubst
 {
-  template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
-  {
-    TRACE_PROCESS (this);
-    switch (u.format) {
-    case 1: return TRACE_RETURN (c->process (u.format1));
-    default:return TRACE_RETURN (c->default_return_value ());
-    }
-  }
-
   inline bool serialize (hb_serialize_context_t *c,
 			 Supplier<GlyphID> &first_glyphs,
 			 Supplier<unsigned int> &ligature_per_first_glyph_count_list,
@@ -869,6 +860,16 @@
     }
   }
 
+  template <typename context_t>
+  inline typename context_t::return_t dispatch (context_t *c) const
+  {
+    TRACE_DISPATCH (this);
+    switch (u.format) {
+    case 1: return TRACE_RETURN (c->dispatch (u.format1));
+    default:return TRACE_RETURN (c->default_return_value ());
+    }
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c) {
     TRACE_SANITIZE (this);
     if (!u.format.sanitize (c)) return TRACE_RETURN (false);
@@ -1022,11 +1023,11 @@
 struct ReverseChainSingleSubst
 {
   template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
+  inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_PROCESS (this);
+    TRACE_DISPATCH (this);
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->process (u.format1));
+    case 1: return TRACE_RETURN (c->dispatch (u.format1));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
@@ -1069,18 +1070,18 @@
   };
 
   template <typename context_t>
-  inline typename context_t::return_t process (context_t *c, unsigned int lookup_type) const
+  inline typename context_t::return_t dispatch (context_t *c, unsigned int lookup_type) const
   {
-    TRACE_PROCESS (this);
+    TRACE_DISPATCH (this);
     switch (lookup_type) {
-    case Single:		return TRACE_RETURN (u.single.process (c));
-    case Multiple:		return TRACE_RETURN (u.multiple.process (c));
-    case Alternate:		return TRACE_RETURN (u.alternate.process (c));
-    case Ligature:		return TRACE_RETURN (u.ligature.process (c));
-    case Context:		return TRACE_RETURN (u.context.process (c));
-    case ChainContext:		return TRACE_RETURN (u.chainContext.process (c));
-    case Extension:		return TRACE_RETURN (u.extension.process (c));
-    case ReverseChainSingle:	return TRACE_RETURN (u.reverseChainContextSingle.process (c));
+    case Single:		return TRACE_RETURN (u.single.dispatch (c));
+    case Multiple:		return TRACE_RETURN (u.multiple.dispatch (c));
+    case Alternate:		return TRACE_RETURN (u.alternate.dispatch (c));
+    case Ligature:		return TRACE_RETURN (u.ligature.dispatch (c));
+    case Context:		return TRACE_RETURN (u.context.dispatch (c));
+    case ChainContext:		return TRACE_RETURN (u.chainContext.dispatch (c));
+    case Extension:		return TRACE_RETURN (u.extension.dispatch (c));
+    case ReverseChainSingle:	return TRACE_RETURN (u.reverseChainContextSingle.dispatch (c));
     default:			return TRACE_RETURN (c->default_return_value ());
     }
   }
@@ -1137,34 +1138,18 @@
     return lookup_type_is_reverse (type);
   }
 
-  template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
-  {
-    TRACE_PROCESS (this);
-    unsigned int lookup_type = get_type ();
-    unsigned int count = get_subtable_count ();
-    for (unsigned int i = 0; i < count; i++) {
-      typename context_t::return_t r = get_subtable (i).process (c, lookup_type);
-      if (c->stop_sublookup_iteration (r))
-        return TRACE_RETURN (r);
-    }
-    return TRACE_RETURN (c->default_return_value ());
-  }
-  template <typename context_t>
-  static inline typename context_t::return_t process_recurse_func (context_t *c, unsigned int lookup_index);
-
   inline hb_closure_context_t::return_t closure (hb_closure_context_t *c) const
   {
     TRACE_CLOSURE (this);
-    c->set_recurse_func (process_recurse_func<hb_closure_context_t>);
-    return TRACE_RETURN (process (c));
+    c->set_recurse_func (dispatch_recurse_func<hb_closure_context_t>);
+    return TRACE_RETURN (dispatch (c));
   }
 
   inline hb_collect_glyphs_context_t::return_t collect_glyphs_lookup (hb_collect_glyphs_context_t *c) const
   {
     TRACE_COLLECT_GLYPHS (this);
-    c->set_recurse_func (process_recurse_func<hb_collect_glyphs_context_t>);
-    return TRACE_RETURN (process (c));
+    c->set_recurse_func (dispatch_recurse_func<hb_collect_glyphs_context_t>);
+    return TRACE_RETURN (dispatch (c));
   }
 
   template <typename set_t>
@@ -1174,7 +1159,7 @@
     const Coverage *last = NULL;
     unsigned int count = get_subtable_count ();
     for (unsigned int i = 0; i < count; i++) {
-      const Coverage *coverage = &get_subtable (i).process (&c, get_type ());
+      const Coverage *coverage = &get_subtable (i).dispatch (&c, get_type ());
       if (coverage != last) {
         coverage->add_coverage (glyphs);
         last = coverage;
@@ -1187,15 +1172,15 @@
     TRACE_WOULD_APPLY (this);
     if (unlikely (!c->len))  return TRACE_RETURN (false);
     if (!digest->may_have (c->glyphs[0]))  return TRACE_RETURN (false);
-      return TRACE_RETURN (process (c));
+      return TRACE_RETURN (dispatch (c));
   }
 
   inline bool apply_once (hb_apply_context_t *c) const
   {
     TRACE_APPLY (this);
-    if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props, &c->property))
+    if (!c->check_glyph_property (&c->buffer->cur(), c->lookup_props))
       return TRACE_RETURN (false);
-    return TRACE_RETURN (process (c));
+    return TRACE_RETURN (dispatch (c));
   }
 
   static bool apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index);
@@ -1304,6 +1289,23 @@
 									 ligatures_list, component_count_list, component_list));
   }
 
+  template <typename context_t>
+  static inline typename context_t::return_t dispatch_recurse_func (context_t *c, unsigned int lookup_index);
+
+  template <typename context_t>
+  inline typename context_t::return_t dispatch (context_t *c) const
+  {
+    TRACE_DISPATCH (this);
+    unsigned int lookup_type = get_type ();
+    unsigned int count = get_subtable_count ();
+    for (unsigned int i = 0; i < count; i++) {
+      typename context_t::return_t r = get_subtable (i).dispatch (c, lookup_type);
+      if (c->stop_sublookup_iteration (r))
+        return TRACE_RETURN (r);
+    }
+    return TRACE_RETURN (c->default_return_value ());
+  }
+
   inline bool sanitize (hb_sanitize_context_t *c)
   {
     TRACE_SANITIZE (this);
@@ -1315,9 +1317,7 @@
     {
       /* The spec says all subtables of an Extension lookup should
        * have the same type.  This is specially important if one has
-       * a reverse type!
-       *
-       * We just check that they are all either forward, or reverse. */
+       * a reverse type! */
       unsigned int type = get_subtable (0).u.extension.get_type ();
       unsigned int count = get_subtable_count ();
       for (unsigned int i = 1; i < count; i++)
@@ -1387,11 +1387,11 @@
 }
 
 template <typename context_t>
-inline typename context_t::return_t SubstLookup::process_recurse_func (context_t *c, unsigned int lookup_index)
+inline typename context_t::return_t SubstLookup::dispatch_recurse_func (context_t *c, unsigned int lookup_index)
 {
   const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub);
   const SubstLookup &l = gsub.get_lookup (lookup_index);
-  return l.process (c);
+  return l.dispatch (c);
 }
 
 inline bool SubstLookup::apply_recurse_func (hb_apply_context_t *c, unsigned int lookup_index)
@@ -1399,11 +1399,9 @@
   const GSUB &gsub = *(hb_ot_layout_from_face (c->face)->gsub);
   const SubstLookup &l = gsub.get_lookup (lookup_index);
   unsigned int saved_lookup_props = c->lookup_props;
-  unsigned int saved_property = c->property;
   c->set_lookup (l);
   bool ret = l.apply_once (c);
   c->lookup_props = saved_lookup_props;
-  c->property = saved_property;
   return ret;
 }
 
diff --git a/src/hb-ot-layout-gsubgpos-private.hh b/src/hb-ot-layout-gsubgpos-private.hh
index 0b00005..f46b378 100644
--- a/src/hb-ot-layout-gsubgpos-private.hh
+++ b/src/hb-ot-layout-gsubgpos-private.hh
@@ -38,7 +38,7 @@
 
 
 
-#define TRACE_PROCESS(this) \
+#define TRACE_DISPATCH(this) \
 	hb_auto_trace_t<context_t::max_debug_depth, typename context_t::return_t> trace \
 	(&c->debug_depth, c->get_name (), this, HB_FUNC, \
 	 "");
@@ -60,7 +60,7 @@
   typedef hb_void_t return_t;
   typedef return_t (*recurse_func_t) (hb_closure_context_t *c, unsigned int lookup_index);
   template <typename T>
-  inline return_t process (const T &obj) { obj.closure (this); return HB_VOID; }
+  inline return_t dispatch (const T &obj) { obj.closure (this); return HB_VOID; }
   static return_t default_return_value (void) { return HB_VOID; }
   bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return false; }
   return_t recurse (unsigned int lookup_index)
@@ -109,7 +109,7 @@
   static const unsigned int max_debug_depth = HB_DEBUG_WOULD_APPLY;
   typedef bool return_t;
   template <typename T>
-  inline return_t process (const T &obj) { return obj.would_apply (this); }
+  inline return_t dispatch (const T &obj) { return obj.would_apply (this); }
   static return_t default_return_value (void) { return false; }
   bool stop_sublookup_iteration (return_t r) const { return r; }
 
@@ -148,7 +148,7 @@
   typedef hb_void_t return_t;
   typedef return_t (*recurse_func_t) (hb_collect_glyphs_context_t *c, unsigned int lookup_index);
   template <typename T>
-  inline return_t process (const T &obj) { obj.collect_glyphs (this); return HB_VOID; }
+  inline return_t dispatch (const T &obj) { obj.collect_glyphs (this); return HB_VOID; }
   static return_t default_return_value (void) { return HB_VOID; }
   bool stop_sublookup_iteration (return_t r HB_UNUSED) const { return false; }
   return_t recurse (unsigned int lookup_index)
@@ -214,7 +214,7 @@
   static const unsigned int max_debug_depth = 0;
   typedef const Coverage &return_t;
   template <typename T>
-  inline return_t process (const T &obj) { return obj.get_coverage (); }
+  inline return_t dispatch (const T &obj) { return obj.get_coverage (); }
   static return_t default_return_value (void) { return Null(Coverage); }
 
   hb_get_coverage_context_t (void) :
@@ -241,7 +241,7 @@
   typedef bool return_t;
   typedef return_t (*recurse_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
   template <typename T>
-  inline return_t process (const T &obj) { return obj.apply (this); }
+  inline return_t dispatch (const T &obj) { return obj.apply (this); }
   static return_t default_return_value (void) { return false; }
   bool stop_sublookup_iteration (return_t r) const { return r; }
   return_t recurse (unsigned int lookup_index)
@@ -255,132 +255,260 @@
     return ret;
   }
 
+  unsigned int table_index; /* GSUB/GPOS */
   hb_font_t *font;
   hb_face_t *face;
   hb_buffer_t *buffer;
   hb_direction_t direction;
   hb_mask_t lookup_mask;
+  bool auto_zwj;
   recurse_func_t recurse_func;
   unsigned int nesting_level_left;
   unsigned int lookup_props;
-  unsigned int property; /* propety of first glyph */
   const GDEF &gdef;
   bool has_glyph_classes;
   unsigned int debug_depth;
 
 
-  hb_apply_context_t (hb_font_t *font_,
+  hb_apply_context_t (unsigned int table_index_,
+		      hb_font_t *font_,
 		      hb_buffer_t *buffer_,
-		      hb_mask_t lookup_mask_) :
+		      hb_mask_t lookup_mask_,
+		      bool auto_zwj_) :
+			table_index (table_index_),
 			font (font_), face (font->face), buffer (buffer_),
 			direction (buffer_->props.direction),
 			lookup_mask (lookup_mask_),
+			auto_zwj (auto_zwj_),
 			recurse_func (NULL),
 			nesting_level_left (MAX_NESTING_LEVEL),
-			lookup_props (0), property (0),
+			lookup_props (0),
 			gdef (*hb_ot_layout_from_face (face)->gdef),
 			has_glyph_classes (gdef.has_glyph_classes ()),
 			debug_depth (0) {}
 
-  void set_recurse_func (recurse_func_t func) { recurse_func = func; }
-  void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; }
-  void set_lookup (const Lookup &l) { lookup_props = l.get_props (); }
+  inline void set_recurse_func (recurse_func_t func) { recurse_func = func; }
+  inline void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; }
+  inline void set_lookup (const Lookup &l) { lookup_props = l.get_props (); }
 
-  struct mark_skipping_forward_iterator_t
+  struct matcher_t
   {
-    inline mark_skipping_forward_iterator_t (hb_apply_context_t *c_,
-					     unsigned int start_index_,
-					     unsigned int num_items_,
-					     bool context_match = false)
+    inline matcher_t (void) :
+	     lookup_props (0),
+	     ignore_zwnj (false),
+	     ignore_zwj (false),
+	     mask (-1),
+#define arg1(arg) (arg) /* Remove the macro to see why it's needed! */
+	     syllable arg1(0),
+#undef arg1
+	     match_func (NULL),
+	     match_data (NULL) {};
+
+    typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data);
+
+    inline void set_ignore_zwnj (bool ignore_zwnj_) { ignore_zwnj = ignore_zwnj_; }
+    inline void set_ignore_zwj (bool ignore_zwj_) { ignore_zwj = ignore_zwj_; }
+    inline void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; }
+    inline void set_mask (hb_mask_t mask_) { mask = mask_; }
+    inline void set_syllable (uint8_t syllable_)  { syllable = syllable_; }
+    inline void set_match_func (match_func_t match_func_,
+				const void *match_data_)
+    { match_func = match_func_; match_data = match_data_; }
+
+    enum may_match_t {
+      MATCH_NO,
+      MATCH_YES,
+      MATCH_MAYBE
+    };
+
+    inline may_match_t may_match (const hb_glyph_info_t &info,
+				  const USHORT          *glyph_data) const
     {
-      c = c_;
-      idx = start_index_;
-      num_items = num_items_;
-      mask = context_match ? -1 : c->lookup_mask;
-      syllable = context_match ? 0 : c->buffer->cur().syllable ();
-      end = c->buffer->len;
+      if (!(info.mask & mask) ||
+	  (syllable && syllable != info.syllable ()))
+	return MATCH_NO;
+
+      if (match_func)
+        return match_func (info.codepoint, *glyph_data, match_data) ? MATCH_YES : MATCH_NO;
+
+      return MATCH_MAYBE;
     }
-    inline bool has_no_chance (void) const
+
+    enum may_skip_t {
+      SKIP_NO,
+      SKIP_YES,
+      SKIP_MAYBE
+    };
+
+    inline may_skip_t
+    may_skip (const hb_apply_context_t *c,
+	      const hb_glyph_info_t    &info) const
     {
-      return unlikely (num_items && idx + num_items >= end);
+      unsigned int property;
+
+      property = info.glyph_props();
+
+      if (!c->match_properties (info.codepoint, property, lookup_props))
+	return SKIP_YES;
+
+      if (unlikely (_hb_glyph_info_is_default_ignorable (&info) &&
+		    (ignore_zwnj || !_hb_glyph_info_is_zwnj (&info)) &&
+		    (ignore_zwj || !_hb_glyph_info_is_zwj (&info)) &&
+		    !is_a_ligature (info)))
+	return SKIP_MAYBE;
+
+      return SKIP_NO;
     }
-    inline void reject (void)
+
+    protected:
+    unsigned int lookup_props;
+    bool ignore_zwnj;
+    bool ignore_zwj;
+    hb_mask_t mask;
+    uint8_t syllable;
+    match_func_t match_func;
+    const void *match_data;
+  };
+
+  struct skipping_forward_iterator_t
+  {
+    inline skipping_forward_iterator_t (hb_apply_context_t *c_,
+					unsigned int start_index_,
+					unsigned int num_items_,
+					bool context_match = false) :
+					 idx (start_index_),
+					 c (c_),
+					 match_glyph_data (NULL),
+					 num_items (num_items_),
+					 end (c->buffer->len)
     {
-      num_items++;
+      matcher.set_lookup_props (c->lookup_props);
+      /* Ignore ZWNJ if we are matching GSUB context, or matching GPOS. */
+      matcher.set_ignore_zwnj (context_match || c->table_index == 1);
+      /* Ignore ZWJ if we are matching GSUB context, or matching GPOS, or if asked to. */
+      matcher.set_ignore_zwj (context_match || c->table_index == 1 || c->auto_zwj);
+      if (!context_match)
+	matcher.set_mask (c->lookup_mask);
+      matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0);
     }
-    inline bool next (unsigned int *property_out,
-		      unsigned int  lookup_props)
+    inline void set_lookup_props (unsigned int lookup_props) { matcher.set_lookup_props (lookup_props); }
+    inline void set_syllable (unsigned int syllable) { matcher.set_syllable (syllable); }
+    inline void set_match_func (matcher_t::match_func_t match_func,
+				const void *match_data,
+				const USHORT glyph_data[])
+    {
+      matcher.set_match_func (match_func, match_data);
+      match_glyph_data = glyph_data;
+    }
+
+    inline bool has_no_chance (void) const { return unlikely (num_items && idx + num_items >= end); }
+    inline void reject (void) { num_items++; match_glyph_data--; }
+    inline bool next (void)
     {
       assert (num_items > 0);
-      do
+      while (!has_no_chance ())
       {
-	if (has_no_chance ())
-	  return false;
 	idx++;
-      } while (c->should_skip_mark (&c->buffer->info[idx], lookup_props, property_out));
-      num_items--;
-      return (c->buffer->info[idx].mask & mask) && (!syllable || syllable == c->buffer->info[idx].syllable ());
-    }
-    inline bool next (unsigned int *property_out = NULL)
-    {
-      return next (property_out, c->lookup_props);
+	const hb_glyph_info_t &info = c->buffer->info[idx];
+
+	matcher_t::may_skip_t skip = matcher.may_skip (c, info);
+	if (unlikely (skip == matcher_t::SKIP_YES))
+	  continue;
+
+	matcher_t::may_match_t match = matcher.may_match (info, match_glyph_data);
+	if (match == matcher_t::MATCH_YES ||
+	    (match == matcher_t::MATCH_MAYBE &&
+	     skip == matcher_t::SKIP_NO))
+	{
+	  num_items--;
+	  match_glyph_data++;
+	  return true;
+	}
+
+	if (skip == matcher_t::SKIP_NO)
+	  return false;
+      }
+      return false;
     }
 
     unsigned int idx;
     protected:
     hb_apply_context_t *c;
+    matcher_t matcher;
+    const USHORT *match_glyph_data;
+
     unsigned int num_items;
-    hb_mask_t mask;
-    uint8_t syllable;
     unsigned int end;
   };
 
-  struct mark_skipping_backward_iterator_t
+  struct skipping_backward_iterator_t
   {
-    inline mark_skipping_backward_iterator_t (hb_apply_context_t *c_,
-					      unsigned int start_index_,
-					      unsigned int num_items_,
-					      hb_mask_t mask_ = 0,
-					      bool match_syllable_ = true)
+    inline skipping_backward_iterator_t (hb_apply_context_t *c_,
+					 unsigned int start_index_,
+					 unsigned int num_items_,
+					 bool context_match = false) :
+					  idx (start_index_),
+					  c (c_),
+					  match_glyph_data (NULL),
+					  num_items (num_items_)
     {
-      c = c_;
-      idx = start_index_;
-      num_items = num_items_;
-      mask = mask_ ? mask_ : c->lookup_mask;
-      syllable = match_syllable_ ? c->buffer->cur().syllable () : 0;
+      matcher.set_lookup_props (c->lookup_props);
+      /* Ignore ZWNJ if we are matching GSUB context, or matching GPOS. */
+      matcher.set_ignore_zwnj (context_match || c->table_index == 1);
+      /* Ignore ZWJ if we are matching GSUB context, or matching GPOS, or if asked to. */
+      matcher.set_ignore_zwj (context_match || c->table_index == 1 || c->auto_zwj);
+      if (!context_match)
+	matcher.set_mask (c->lookup_mask);
+      matcher.set_syllable (start_index_ == c->buffer->idx ? c->buffer->cur().syllable () : 0);
     }
-    inline bool has_no_chance (void) const
+    inline void set_lookup_props (unsigned int lookup_props) { matcher.set_lookup_props (lookup_props); }
+    inline void set_syllable (unsigned int syllable) { matcher.set_syllable (syllable); }
+    inline void set_match_func (matcher_t::match_func_t match_func,
+				const void *match_data,
+				const USHORT glyph_data[])
     {
-      return unlikely (idx < num_items);
+      matcher.set_match_func (match_func, match_data);
+      match_glyph_data = glyph_data;
     }
-    inline void reject (void)
-    {
-      num_items++;
-    }
-    inline bool prev (unsigned int *property_out,
-		      unsigned int  lookup_props)
+
+    inline bool has_no_chance (void) const { return unlikely (idx < num_items); }
+    inline void reject (void) { num_items++; }
+    inline bool prev (void)
     {
       assert (num_items > 0);
-      do
+      while (!has_no_chance ())
       {
-	if (has_no_chance ())
-	  return false;
 	idx--;
-      } while (c->should_skip_mark (&c->buffer->out_info[idx], lookup_props, property_out));
-      num_items--;
-      return (c->buffer->out_info[idx].mask & mask) && (!syllable || syllable == c->buffer->out_info[idx].syllable ());
-    }
-    inline bool prev (unsigned int *property_out = NULL)
-    {
-      return prev (property_out, c->lookup_props);
+	const hb_glyph_info_t &info = c->buffer->out_info[idx];
+
+	matcher_t::may_skip_t skip = matcher.may_skip (c, info);
+
+	if (unlikely (skip == matcher_t::SKIP_YES))
+	  continue;
+
+	matcher_t::may_match_t match = matcher.may_match (info, match_glyph_data);
+	if (match == matcher_t::MATCH_YES ||
+	    (match == matcher_t::MATCH_MAYBE &&
+	     skip == matcher_t::SKIP_NO))
+	{
+	  num_items--;
+	  match_glyph_data++;
+	  return true;
+	}
+
+	if (skip == matcher_t::SKIP_NO)
+	  return false;
+      }
+      return false;
     }
 
     unsigned int idx;
     protected:
     hb_apply_context_t *c;
+    matcher_t matcher;
+    const USHORT *match_glyph_data;
+
     unsigned int num_items;
-    hb_mask_t mask;
-    uint8_t syllable;
   };
 
   inline bool
@@ -423,42 +551,15 @@
 
   inline bool
   check_glyph_property (hb_glyph_info_t *info,
-			unsigned int  lookup_props,
-			unsigned int *property_out) const
+			unsigned int  lookup_props) const
   {
     unsigned int property;
 
     property = info->glyph_props();
-    *property_out = property;
 
     return match_properties (info->codepoint, property, lookup_props);
   }
 
-  inline bool
-  should_skip_mark (hb_glyph_info_t *info,
-		   unsigned int  lookup_props,
-		   unsigned int *property_out) const
-  {
-    unsigned int property;
-
-    property = info->glyph_props();
-    if (property_out)
-      *property_out = property;
-
-    /* If it's a mark, skip it if we don't accept it. */
-    if (unlikely (property & HB_OT_LAYOUT_GLYPH_PROPS_MARK))
-      return !match_properties (info->codepoint, property, lookup_props);
-
-    /* If not a mark, don't skip. */
-    return false;
-  }
-
-
-  inline bool should_mark_skip_current_glyph (void) const
-  {
-    return should_skip_mark (&buffer->cur(), lookup_props, NULL);
-  }
-
   inline void set_class (hb_codepoint_t glyph_index, unsigned int class_guess) const
   {
     if (likely (has_glyph_classes))
@@ -602,7 +703,8 @@
 {
   TRACE_APPLY (NULL);
 
-  hb_apply_context_t::mark_skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
+  hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
+  skippy_iter.set_match_func (match_func, match_data, input);
   if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
   /*
@@ -623,7 +725,7 @@
    *   ligate with a conjunct...)
    */
 
-  bool is_mark_ligature = !!(c->property & HB_OT_LAYOUT_GLYPH_PROPS_MARK);
+  bool is_mark_ligature = !!(c->buffer->cur().glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MARK);
 
   unsigned int total_component_count = 0;
   total_component_count += get_lig_num_comps (c->buffer->cur());
@@ -633,11 +735,7 @@
 
   for (unsigned int i = 1; i < count; i++)
   {
-    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 TRACE_RETURN (false);
+    if (!skippy_iter.next ()) 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]);
@@ -656,7 +754,7 @@
 	return TRACE_RETURN (false);
     }
 
-    is_mark_ligature = is_mark_ligature && (property & HB_OT_LAYOUT_GLYPH_PROPS_MARK);
+    is_mark_ligature = is_mark_ligature && (c->buffer->info[skippy_iter.idx].glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MARK);
     total_component_count += get_lig_num_comps (c->buffer->info[skippy_iter.idx]);
   }
 
@@ -673,13 +771,17 @@
 }
 static inline void ligate_input (hb_apply_context_t *c,
 				 unsigned int count, /* Including the first glyph (not matched) */
-				 const USHORT input[] HB_UNUSED, /* Array of input values--start with second glyph */
+				 const USHORT input[], /* Array of input values--start with second glyph */
+				 match_func_t match_func,
+				 const void *match_data,
 				 hb_codepoint_t lig_glyph,
-				 match_func_t match_func HB_UNUSED,
-				 const void *match_data HB_UNUSED,
 				 bool is_mark_ligature,
 				 unsigned int total_component_count)
 {
+  hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
+  skippy_iter.set_match_func (match_func, match_data, input);
+  if (skippy_iter.has_no_chance ()) return;
+
   /*
    * - 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
@@ -720,7 +822,9 @@
 
   for (unsigned int i = 1; i < count; i++)
   {
-    while (c->should_mark_skip_current_glyph ())
+    if (!skippy_iter.next ()) return;
+
+    while (c->buffer->idx < skippy_iter.idx)
     {
       if (!is_mark_ligature) {
 	unsigned int new_lig_comp = components_so_far - last_num_components +
@@ -759,19 +863,14 @@
 {
   TRACE_APPLY (NULL);
 
-  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 TRACE_RETURN (false);
+  hb_apply_context_t::skipping_backward_iterator_t skippy_iter (c, c->buffer->backtrack_len (), count, true);
+  skippy_iter.set_match_func (match_func, match_data, backtrack);
+  if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
   for (unsigned int i = 0; i < count; i++)
-  {
     if (!skippy_iter.prev ())
       return TRACE_RETURN (false);
 
-    if (likely (!match_func (c->buffer->out_info[skippy_iter.idx].codepoint, backtrack[i], match_data)))
-      return TRACE_RETURN (false);
-  }
-
   return TRACE_RETURN (true);
 }
 
@@ -784,19 +883,14 @@
 {
   TRACE_APPLY (NULL);
 
-  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 TRACE_RETURN (false);
+  hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, c->buffer->idx + offset - 1, count, true);
+  skippy_iter.set_match_func (match_func, match_data, lookahead);
+  if (skippy_iter.has_no_chance ()) return TRACE_RETURN (false);
 
   for (unsigned int i = 0; i < count; i++)
-  {
     if (!skippy_iter.next ())
       return TRACE_RETURN (false);
 
-    if (likely (!match_func (c->buffer->info[skippy_iter.idx].codepoint, lookahead[i], match_data)))
-      return TRACE_RETURN (false);
-  }
-
   return TRACE_RETURN (true);
 }
 
@@ -829,6 +923,9 @@
 
 static inline bool apply_lookup (hb_apply_context_t *c,
 				 unsigned int count, /* Including the first glyph */
+				 const USHORT input[], /* Array of input values--start with second glyph */
+				 match_func_t match_func,
+				 const void *match_data,
 				 unsigned int lookupCount,
 				 const LookupRecord lookupRecord[] /* Array of LookupRecords--in design order */)
 {
@@ -845,17 +942,46 @@
    * and we jump out of it.  Not entirely disastrous.  So we don't check
    * for reverse lookup here.
    */
-  for (unsigned int i = 0; i < count; /* NOP */)
+
+  hb_apply_context_t::skipping_forward_iterator_t skippy_iter (c, c->buffer->idx, count - 1);
+  skippy_iter.set_match_func (match_func, match_data, input);
+  uint8_t syllable = c->buffer->cur().syllable();
+
+  unsigned int i = 0;
+  if (lookupCount && 0 == lookupRecord->sequenceIndex)
   {
-    if (unlikely (c->buffer->idx == end))
-      return TRACE_RETURN (true);
-    while (c->should_mark_skip_current_glyph ())
+    unsigned int old_pos = c->buffer->idx;
+
+    /* Apply a lookup */
+    bool done = c->recurse (lookupRecord->lookupListIndex);
+
+    lookupRecord++;
+    lookupCount--;
+    /* Err, this is wrong if the lookup jumped over some glyphs */
+    i += c->buffer->idx - old_pos;
+
+    if (!done)
+      goto not_applied;
+    else
     {
-      /* No lookup applied for this index */
-      c->buffer->next_glyph ();
-      if (unlikely (c->buffer->idx == end))
-	return TRACE_RETURN (true);
+      /* Reinitialize iterator. */
+      hb_apply_context_t::skipping_forward_iterator_t tmp (c, c->buffer->idx - 1, count - i);
+      tmp.set_syllable (syllable);
+      skippy_iter = tmp;
     }
+  }
+  else
+  {
+  not_applied:
+    /* No lookup applied for this index */
+    c->buffer->next_glyph ();
+    i++;
+  }
+  while (i < count)
+  {
+    if (!skippy_iter.next ()) return TRACE_RETURN (true);
+    while (c->buffer->idx < skippy_iter.idx)
+      c->buffer->next_glyph ();
 
     if (lookupCount && i == lookupRecord->sequenceIndex)
     {
@@ -868,15 +994,20 @@
       lookupCount--;
       /* Err, this is wrong if the lookup jumped over some glyphs */
       i += c->buffer->idx - old_pos;
-      if (unlikely (c->buffer->idx == end))
-	return TRACE_RETURN (true);
 
       if (!done)
-	goto not_applied;
+	goto not_applied2;
+      else
+      {
+        /* Reinitialize iterator. */
+	hb_apply_context_t::skipping_forward_iterator_t tmp (c, c->buffer->idx - 1, count - i);
+	tmp.set_syllable (syllable);
+	skippy_iter = tmp;
+      }
     }
     else
     {
-    not_applied:
+    not_applied2:
       /* No lookup applied for this index */
       c->buffer->next_glyph ();
       i++;
@@ -958,7 +1089,8 @@
 		      inputCount, input,
 		      lookup_context.funcs.match, lookup_context.match_data)
       && apply_lookup (c,
-		       inputCount,
+		       inputCount, input,
+		       lookup_context.funcs.match, lookup_context.match_data,
 		       lookupCount, lookupRecord);
 }
 
@@ -1353,13 +1485,13 @@
 struct Context
 {
   template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
+  inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_PROCESS (this);
+    TRACE_DISPATCH (this);
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->process (u.format1));
-    case 2: return TRACE_RETURN (c->process (u.format2));
-    case 3: return TRACE_RETURN (c->process (u.format3));
+    case 1: return TRACE_RETURN (c->dispatch (u.format1));
+    case 2: return TRACE_RETURN (c->dispatch (u.format2));
+    case 3: return TRACE_RETURN (c->dispatch (u.format3));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
@@ -1494,7 +1626,8 @@
 			  lookup_context.funcs.match, lookup_context.match_data[2],
 			  lookahead_offset)
       && apply_lookup (c,
-		       inputCount,
+		       inputCount, input,
+		       lookup_context.funcs.match, lookup_context.match_data[1],
 		       lookupCount, lookupRecord);
 }
 
@@ -1968,13 +2101,13 @@
 struct ChainContext
 {
   template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
+  inline typename context_t::return_t dispatch (context_t *c) const
   {
-    TRACE_PROCESS (this);
+    TRACE_DISPATCH (this);
     switch (u.format) {
-    case 1: return TRACE_RETURN (c->process (u.format1));
-    case 2: return TRACE_RETURN (c->process (u.format2));
-    case 3: return TRACE_RETURN (c->process (u.format3));
+    case 1: return TRACE_RETURN (c->dispatch (u.format1));
+    case 2: return TRACE_RETURN (c->dispatch (u.format2));
+    case 3: return TRACE_RETURN (c->dispatch (u.format3));
     default:return TRACE_RETURN (c->default_return_value ());
     }
   }
@@ -2048,9 +2181,9 @@
   }
 
   template <typename context_t>
-  inline typename context_t::return_t process (context_t *c) const
+  inline typename context_t::return_t dispatch (context_t *c) const
   {
-    return get_subtable<typename T::LookupSubTable> ().process (c, get_type ());
+    return get_subtable<typename T::LookupSubTable> ().dispatch (c, get_type ());
   }
 
   inline bool sanitize_self (hb_sanitize_context_t *c) {
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index 49093de..4866c41 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -43,6 +43,58 @@
 #define syllable()		var1.u8[2] /* GSUB/GPOS shaping boundaries */
 #define lig_props()		var1.u8[3] /* GSUB/GPOS ligature tracking */
 
+/* buffer var allocations, used during the entire shaping process */
+#define unicode_props0()	var2.u8[0]
+#define unicode_props1()	var2.u8[1]
+
+
+inline void
+_hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_unicode_funcs_t *unicode)
+{
+  info->unicode_props0() = ((unsigned int) unicode->general_category (info->codepoint)) |
+			   (unicode->is_default_ignorable (info->codepoint) ? 0x80 : 0) |
+			   (info->codepoint == 0x200C ? 0x40 : 0) |
+			   (info->codepoint == 0x200D ? 0x20 : 0);
+  info->unicode_props1() = unicode->modified_combining_class (info->codepoint);
+}
+
+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() & 0x1F);
+}
+
+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_default_ignorable (const hb_glyph_info_t *info)
+{
+  return !!(info->unicode_props0() & 0x80);
+}
+
+inline hb_bool_t
+_hb_glyph_info_is_zwnj (const hb_glyph_info_t *info)
+{
+  return !!(info->unicode_props0() & 0x40);
+}
+
+inline hb_bool_t
+_hb_glyph_info_is_zwj (const hb_glyph_info_t *info)
+{
+  return !!(info->unicode_props0() & 0x20);
+}
+
+
 #define hb_ot_layout_from_face(face) ((hb_ot_layout_t *) face->shaper_data.ot)
 
 /*
@@ -50,11 +102,11 @@
  */
 
 typedef enum {
-  HB_OT_LAYOUT_GLYPH_PROPS_UNCLASSIFIED	= 0x0001,
-  HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH	= 0x0002,
-  HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE	= 0x0004,
-  HB_OT_LAYOUT_GLYPH_PROPS_MARK		= 0x0008,
-  HB_OT_LAYOUT_GLYPH_PROPS_COMPONENT	= 0x0010
+  HB_OT_LAYOUT_GLYPH_PROPS_UNCLASSIFIED	= 1 << HB_OT_LAYOUT_GLYPH_CLASS_UNCLASSIFIED,
+  HB_OT_LAYOUT_GLYPH_PROPS_BASE_GLYPH	= 1 << HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH,
+  HB_OT_LAYOUT_GLYPH_PROPS_LIGATURE	= 1 << HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE,
+  HB_OT_LAYOUT_GLYPH_PROPS_MARK		= 1 << HB_OT_LAYOUT_GLYPH_CLASS_MARK,
+  HB_OT_LAYOUT_GLYPH_PROPS_COMPONENT	= 1 << HB_OT_LAYOUT_GLYPH_CLASS_COMPONENT
 } hb_ot_layout_glyph_class_mask_t;
 
 
@@ -154,7 +206,8 @@
 hb_ot_layout_substitute_lookup (hb_font_t    *font,
 				hb_buffer_t  *buffer,
 				unsigned int  lookup_index,
-				hb_mask_t     mask);
+				hb_mask_t     mask,
+				hb_bool_t     auto_zwj);
 
 /* Should be called after all the substitute_lookup's are done */
 HB_INTERNAL void
@@ -171,13 +224,13 @@
 hb_ot_layout_position_lookup (hb_font_t    *font,
 			      hb_buffer_t  *buffer,
 			      unsigned int  lookup_index,
-			      hb_mask_t     mask);
+			      hb_mask_t     mask,
+			      hb_bool_t     auto_zwj);
 
 /* Should be called after all the position_lookup's are done */
 HB_INTERNAL void
 hb_ot_layout_position_finish (hb_font_t    *font,
-			      hb_buffer_t  *buffer,
-			      hb_bool_t     zero_width_attached_marks);
+			      hb_buffer_t  *buffer);
 
 
 
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 5c266e6..8161ce3 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -185,6 +185,8 @@
   return g.get_script_tags (start_offset, script_count, script_tags);
 }
 
+#define HB_OT_TAG_LATIN_SCRIPT		HB_TAG ('l', 'a', 't', 'n')
+
 hb_bool_t
 hb_ot_layout_table_find_script (hb_face_t    *face,
 				hb_tag_t      table_tag,
@@ -206,6 +208,11 @@
   if (g.find_script_index (HB_OT_TAG_DEFAULT_LANGUAGE, script_index))
     return false;
 
+  /* try with 'latn'; some old fonts put their features there even though
+     they're really trying to support Thai, for example :( */
+  if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index))
+    return false;
+
   if (script_index) *script_index = HB_OT_LAYOUT_NO_SCRIPT_INDEX;
   return false;
 }
@@ -246,7 +253,6 @@
 
   /* try with 'latn'; some old fonts put their features there even though
      they're really trying to support Thai, for example :( */
-#define HB_OT_TAG_LATIN_SCRIPT		HB_TAG ('l', 'a', 't', 'n')
   if (g.find_script_index (HB_OT_TAG_LATIN_SCRIPT, script_index)) {
     if (chosen_script)
       *chosen_script = HB_OT_TAG_LATIN_SCRIPT;
@@ -660,11 +666,12 @@
 hb_ot_layout_substitute_lookup (hb_font_t    *font,
 				hb_buffer_t  *buffer,
 				unsigned int  lookup_index,
-				hb_mask_t     mask)
+				hb_mask_t     mask,
+				hb_bool_t     auto_zwj)
 {
   if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gsub_lookup_count)) return false;
 
-  OT::hb_apply_context_t c (font, buffer, mask);
+  OT::hb_apply_context_t c (0, font, buffer, mask, auto_zwj);
 
   const OT::SubstLookup& l = hb_ot_layout_from_face (font->face)->gsub->get_lookup (lookup_index);
 
@@ -709,11 +716,12 @@
 hb_ot_layout_position_lookup (hb_font_t    *font,
 			      hb_buffer_t  *buffer,
 			      unsigned int  lookup_index,
-			      hb_mask_t     mask)
+			      hb_mask_t     mask,
+			      hb_bool_t     auto_zwj)
 {
   if (unlikely (lookup_index >= hb_ot_layout_from_face (font->face)->gpos_lookup_count)) return false;
 
-  OT::hb_apply_context_t c (font, buffer, mask);
+  OT::hb_apply_context_t c (1, font, buffer, mask, auto_zwj);
 
   const OT::PosLookup& l = hb_ot_layout_from_face (font->face)->gpos->get_lookup (lookup_index);
 
@@ -721,9 +729,9 @@
 }
 
 void
-hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer, hb_bool_t zero_width_attached_marks)
+hb_ot_layout_position_finish (hb_font_t *font, hb_buffer_t *buffer)
 {
-  OT::GPOS::position_finish (font, buffer, zero_width_attached_marks);
+  OT::GPOS::position_finish (font, buffer);
 }
 
 hb_bool_t
diff --git a/src/hb-ot-map-private.hh b/src/hb-ot-map-private.hh
index b140207..a679fb5 100644
--- a/src/hb-ot-map-private.hh
+++ b/src/hb-ot-map-private.hh
@@ -49,14 +49,16 @@
     unsigned int shift;
     hb_mask_t mask;
     hb_mask_t _1_mask; /* mask for value=1, for quick access */
-    hb_bool_t needs_fallback;
+    unsigned int needs_fallback : 1;
+    unsigned int auto_zwj : 1;
 
     static int cmp (const feature_map_t *a, const feature_map_t *b)
     { return a->tag < b->tag ? -1 : a->tag > b->tag ? 1 : 0; }
   };
 
   struct lookup_map_t {
-    unsigned int index;
+    unsigned short index;
+    unsigned short auto_zwj : 1;
     hb_mask_t mask;
 
     static int cmp (const lookup_map_t *a, const lookup_map_t *b)
@@ -136,7 +138,8 @@
   HB_INTERNAL void add_lookups (hb_face_t    *face,
 				unsigned int  table_index,
 				unsigned int  feature_index,
-				hb_mask_t     mask);
+				hb_mask_t     mask,
+				bool          auto_zwj);
 
   hb_mask_t global_mask;
 
@@ -145,6 +148,30 @@
   hb_prealloced_array_t<pause_map_t, 1> pauses[2]; /* GSUB/GPOS */
 };
 
+enum hb_ot_map_feature_flags_t {
+  F_NONE		= 0x0000,
+  F_GLOBAL		= 0x0001,
+  F_HAS_FALLBACK	= 0x0002,
+  F_MANUAL_ZWJ		= 0x0004
+};
+/* Macro version for where const is desired. */
+#define F_COMBINE(l,r) (hb_ot_map_feature_flags_t ((unsigned int) (l) | (unsigned int) (r)))
+inline hb_ot_map_feature_flags_t
+operator | (hb_ot_map_feature_flags_t l, hb_ot_map_feature_flags_t r)
+{ return hb_ot_map_feature_flags_t ((unsigned int) l | (unsigned int) r); }
+inline hb_ot_map_feature_flags_t
+operator & (hb_ot_map_feature_flags_t l, hb_ot_map_feature_flags_t r)
+{ return hb_ot_map_feature_flags_t ((unsigned int) l & (unsigned int) r); }
+inline hb_ot_map_feature_flags_t
+operator ~ (hb_ot_map_feature_flags_t r)
+{ return hb_ot_map_feature_flags_t (~(unsigned int) r); }
+inline hb_ot_map_feature_flags_t&
+operator |= (hb_ot_map_feature_flags_t &l, hb_ot_map_feature_flags_t r)
+{ l = l | r; return l; }
+inline hb_ot_map_feature_flags_t&
+operator &= (hb_ot_map_feature_flags_t& l, hb_ot_map_feature_flags_t r)
+{ l = l & r; return l; }
+
 
 struct hb_ot_map_builder_t
 {
@@ -153,10 +180,11 @@
   HB_INTERNAL hb_ot_map_builder_t (hb_face_t *face_,
 				   const hb_segment_properties_t *props_);
 
-  HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value, bool global, bool has_fallback = false);
+  HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value,
+				hb_ot_map_feature_flags_t flags);
 
-  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_global_bool_feature (hb_tag_t tag)
+  { add_feature (tag, 1, F_GLOBAL); }
 
   inline void add_gsub_pause (hb_ot_map_t::pause_func_t pause_func)
   { add_pause (0, pause_func); }
@@ -177,8 +205,7 @@
     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 */
+    hb_ot_map_feature_flags_t flags;
     unsigned int default_value; /* for non-global features, what should the unset glyphs take */
     unsigned int stage[2]; /* GSUB/GPOS */
 
diff --git a/src/hb-ot-map.cc b/src/hb-ot-map.cc
index 62f7904..85e6e16 100644
--- a/src/hb-ot-map.cc
+++ b/src/hb-ot-map.cc
@@ -33,7 +33,8 @@
 hb_ot_map_t::add_lookups (hb_face_t    *face,
 			  unsigned int  table_index,
 			  unsigned int  feature_index,
-			  hb_mask_t     mask)
+			  hb_mask_t     mask,
+			  bool          auto_zwj)
 {
   unsigned int lookup_indices[32];
   unsigned int offset, len;
@@ -53,6 +54,7 @@
         return;
       lookup->mask = mask;
       lookup->index = lookup_indices[i];
+      lookup->auto_zwj = auto_zwj;
     }
 
     offset += len;
@@ -84,16 +86,16 @@
   }
 }
 
-void hb_ot_map_builder_t::add_feature (hb_tag_t tag, unsigned int value, bool global, bool has_fallback)
+void hb_ot_map_builder_t::add_feature (hb_tag_t tag, unsigned int value,
+				       hb_ot_map_feature_flags_t flags)
 {
   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->flags = flags;
+  info->default_value = (flags & F_GLOBAL) ? value : 0;
   info->stage[0] = current_stage[0];
   info->stage[1] = current_stage[1];
 }
@@ -108,7 +110,10 @@
   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_lookup (font, buffer, lookups[table_index][i].index, lookups[table_index][i].mask);
+      hb_ot_layout_substitute_lookup (font, buffer,
+				      lookups[table_index][i].index,
+				      lookups[table_index][i].mask,
+				      lookups[table_index][i].auto_zwj);
 
     buffer->clear_output ();
 
@@ -117,7 +122,9 @@
   }
 
   for (; i < lookups[table_index].len; i++)
-    hb_ot_layout_substitute_lookup (font, buffer, lookups[table_index][i].index, lookups[table_index][i].mask);
+    hb_ot_layout_substitute_lookup (font, buffer, lookups[table_index][i].index,
+				    lookups[table_index][i].mask,
+				    lookups[table_index][i].auto_zwj);
 }
 
 void hb_ot_map_t::position (const hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const
@@ -128,14 +135,18 @@
   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_position_lookup (font, buffer, lookups[table_index][i].index, lookups[table_index][i].mask);
+      hb_ot_layout_position_lookup (font, buffer, lookups[table_index][i].index,
+				    lookups[table_index][i].mask,
+				    lookups[table_index][i].auto_zwj);
 
     if (pause->callback)
       pause->callback (plan, font, buffer);
   }
 
   for (; i < lookups[table_index].len; i++)
-    hb_ot_layout_position_lookup (font, buffer, lookups[table_index][i].index, lookups[table_index][i].mask);
+    hb_ot_layout_position_lookup (font, buffer, lookups[table_index][i].index,
+				  lookups[table_index][i].mask,
+				  lookups[table_index][i].auto_zwj);
 }
 
 void hb_ot_map_t::collect_lookups (unsigned int table_index, hb_set_t *lookups_out) const
@@ -176,18 +187,18 @@
       if (feature_infos[i].tag != feature_infos[j].tag)
 	feature_infos[++j] = feature_infos[i];
       else {
-	if (feature_infos[i].global) {
-	  feature_infos[j].global = true;
+	if (feature_infos[i].flags & F_GLOBAL) {
+	  feature_infos[j].flags |= F_GLOBAL;
 	  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].flags &= ~F_GLOBAL;
 	  feature_infos[j].max_value = MAX (feature_infos[j].max_value, feature_infos[i].max_value);
+	  /* Inherit default_value from j */
 	}
-	feature_infos[j].has_fallback = feature_infos[j].has_fallback || feature_infos[i].has_fallback;
+	feature_infos[j].flags |= (feature_infos[i].flags & F_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);
   }
@@ -200,7 +211,7 @@
 
     unsigned int bits_needed;
 
-    if (info->global && info->max_value == 1)
+    if ((info->flags & F_GLOBAL) && info->max_value == 1)
       /* Uses the global bit */
       bits_needed = 0;
     else
@@ -219,7 +230,7 @@
 						   language_index[table_index],
 						   info->tag,
 						   &feature_index[table_index]);
-    if (!found && !info->has_fallback)
+    if (!found && !(info->flags & F_HAS_FALLBACK))
       continue;
 
 
@@ -232,7 +243,8 @@
     map->index[1] = feature_index[1];
     map->stage[0] = info->stage[0];
     map->stage[1] = info->stage[1];
-    if (info->global && info->max_value == 1) {
+    map->auto_zwj = !(info->flags & F_MANUAL_ZWJ);
+    if ((info->flags & F_GLOBAL) && info->max_value == 1) {
       /* Uses the global bit */
       map->shift = 0;
       map->mask = 1;
@@ -240,8 +252,7 @@
       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;
+      m.global_mask |= (info->default_value << map->shift) & map->mask;
     }
     map->_1_mask = (1 << map->shift) & map->mask;
     map->needs_fallback = !found;
@@ -264,7 +275,7 @@
 							  script_index[table_index],
 							  language_index[table_index],
 							  &required_feature_index))
-      m.add_lookups (face, table_index, required_feature_index, 1);
+      m.add_lookups (face, table_index, required_feature_index, 1, true);
 
     unsigned int pause_index = 0;
     unsigned int last_num_lookups = 0;
@@ -272,7 +283,10 @@
     {
       for (unsigned i = 0; i < m.features.len; i++)
         if (m.features[i].stage[table_index] == stage)
-	  m.add_lookups (face, table_index, m.features[i].index[table_index], m.features[i].mask);
+	  m.add_lookups (face, table_index,
+			 m.features[i].index[table_index],
+			 m.features[i].mask,
+			 m.features[i].auto_zwj);
 
       /* Sort lookups and merge duplicates */
       if (last_num_lookups < m.lookups[table_index].len)
@@ -284,7 +298,10 @@
 	  if (m.lookups[table_index][i].index != m.lookups[table_index][j].index)
 	    m.lookups[table_index][++j] = m.lookups[table_index][i];
 	  else
+	  {
 	    m.lookups[table_index][j].mask |= m.lookups[table_index][i].mask;
+	    m.lookups[table_index][j].auto_zwj &= m.lookups[table_index][i].auto_zwj;
+	  }
 	m.lookups[table_index].shrink (j + 1);
       }
 
diff --git a/src/hb-ot-shape-complex-arabic-fallback.hh b/src/hb-ot-shape-complex-arabic-fallback.hh
index 4fcd0a2..5e151f7 100644
--- a/src/hb-ot-shape-complex-arabic-fallback.hh
+++ b/src/hb-ot-shape-complex-arabic-fallback.hh
@@ -244,7 +244,7 @@
 {
   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]);
+      OT::hb_apply_context_t c (0, font, buffer, fallback_plan->mask_array[i], true/*auto_zwj*/);
       fallback_plan->lookup_array[i]->apply_string (&c, &fallback_plan->digest_array[i]);
     }
 }
diff --git a/src/hb-ot-shape-complex-arabic.cc b/src/hb-ot-shape-complex-arabic.cc
index 35356fe..18b3d02 100644
--- a/src/hb-ot-shape-complex-arabic.cc
+++ b/src/hb-ot-shape-complex-arabic.cc
@@ -37,17 +37,16 @@
  */
 enum {
   JOINING_TYPE_U		= 0,
-  JOINING_TYPE_R		= 1,
-  JOINING_TYPE_D		= 2,
+  JOINING_TYPE_L		= 1,
+  JOINING_TYPE_R		= 2,
+  JOINING_TYPE_D		= 3,
   JOINING_TYPE_C		= JOINING_TYPE_D,
-  JOINING_GROUP_ALAPH		= 3,
-  JOINING_GROUP_DALATH_RISH	= 4,
-  NUM_STATE_MACHINE_COLS	= 5,
+  JOINING_GROUP_ALAPH		= 4,
+  JOINING_GROUP_DALATH_RISH	= 5,
+  NUM_STATE_MACHINE_COLS	= 6,
 
-  /* We deliberately don't have a JOINING_TYPE_L since that's unused in Unicode. */
-
-  JOINING_TYPE_T = 6,
-  JOINING_TYPE_X = 7  /* means: use general-category to choose between U or T. */
+  JOINING_TYPE_T = 7,
+  JOINING_TYPE_X = 8  /* means: use general-category to choose between U or T. */
 };
 
 /*
@@ -81,8 +80,7 @@
   if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xA840, 0xA872)))
   {
       if (unlikely (u == 0xA872))
-	/* XXX Looks like this should be TYPE_L, but we don't support that yet! */
-	return JOINING_TYPE_R;
+	return JOINING_TYPE_L;
 
       return JOINING_TYPE_D;
   }
@@ -133,28 +131,28 @@
 	uint16_t next_state;
 } arabic_state_table[][NUM_STATE_MACHINE_COLS] =
 {
-  /*   jt_U,          jt_R,          jt_D,          jg_ALAPH,      jg_DALATH_RISH */
+  /*   jt_U,          jt_L,          jt_R,          jt_D,          jg_ALAPH,      jg_DALATH_RISH */
 
   /* State 0: prev was U, not willing to join. */
-  { {NONE,NONE,0}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,6}, },
+  { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,6}, },
 
   /* State 1: prev was R or ISOL/ALAPH, not willing to join. */
-  { {NONE,NONE,0}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN2,5}, {NONE,ISOL,6}, },
+  { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN2,5}, {NONE,ISOL,6}, },
 
-  /* State 2: prev was D/ISOL, willing to join. */
-  { {NONE,NONE,0}, {INIT,FINA,1}, {INIT,FINA,3}, {INIT,FINA,4}, {INIT,FINA,6}, },
+  /* State 2: prev was D/L in ISOL form, willing to join. */
+  { {NONE,NONE,0}, {NONE,ISOL,2}, {INIT,FINA,1}, {INIT,FINA,3}, {INIT,FINA,4}, {INIT,FINA,6}, },
 
-  /* State 3: prev was D/FINA, willing to join. */
-  { {NONE,NONE,0}, {MEDI,FINA,1}, {MEDI,FINA,3}, {MEDI,FINA,4}, {MEDI,FINA,6}, },
+  /* State 3: prev was D in FINA form, willing to join. */
+  { {NONE,NONE,0}, {NONE,ISOL,2}, {MEDI,FINA,1}, {MEDI,FINA,3}, {MEDI,FINA,4}, {MEDI,FINA,6}, },
 
   /* State 4: prev was FINA ALAPH, not willing to join. */
-  { {NONE,NONE,0}, {MED2,ISOL,1}, {MED2,ISOL,2}, {MED2,FIN2,5}, {MED2,ISOL,6}, },
+  { {NONE,NONE,0}, {NONE,ISOL,2}, {MED2,ISOL,1}, {MED2,ISOL,2}, {MED2,FIN2,5}, {MED2,ISOL,6}, },
 
   /* 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}, },
+  { {NONE,NONE,0}, {NONE,ISOL,2}, {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}, }
+  { {NONE,NONE,0}, {NONE,ISOL,2}, {NONE,ISOL,1}, {NONE,ISOL,2}, {NONE,FIN3,5}, {NONE,ISOL,6}, }
 };
 
 
@@ -178,25 +176,25 @@
    * TODO: Add test cases for these two.
    */
 
-  map->add_bool_feature (HB_TAG('c','c','m','p'));
-  map->add_bool_feature (HB_TAG('l','o','c','l'));
+  map->add_global_bool_feature (HB_TAG('c','c','m','p'));
+  map->add_global_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, i < 4); /* The first four features have fallback. */
+    map->add_feature (arabic_features[i], 1, i < 4 ? F_HAS_FALLBACK : F_NONE); /* The first four features have fallback. */
 
   map->add_gsub_pause (NULL);
 
-  map->add_bool_feature (HB_TAG('r','l','i','g'), true, true);
+  map->add_feature (HB_TAG('r','l','i','g'), 1, F_GLOBAL|F_HAS_FALLBACK);
   map->add_gsub_pause (arabic_fallback_shape);
 
-  map->add_bool_feature (HB_TAG('c','a','l','t'));
+  map->add_global_bool_feature (HB_TAG('c','a','l','t'));
   map->add_gsub_pause (NULL);
 
-  map->add_bool_feature (HB_TAG('c','s','w','h'));
-  map->add_bool_feature (HB_TAG('d','l','i','g'));
-  map->add_bool_feature (HB_TAG('m','s','e','t'));
+  map->add_global_bool_feature (HB_TAG('c','s','w','h'));
+  map->add_global_bool_feature (HB_TAG('d','l','i','g'));
+  map->add_global_bool_feature (HB_TAG('m','s','e','t'));
 }
 
 #include "hb-ot-shape-complex-arabic-fallback.hh"
@@ -354,6 +352,6 @@
   NULL, /* decompose */
   NULL, /* compose */
   setup_masks_arabic,
-  true, /* zero_width_attached_marks */
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE,
   true, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-default.cc b/src/hb-ot-shape-complex-default.cc
index 5340293..fad57f6 100644
--- a/src/hb-ot-shape-complex-default.cc
+++ b/src/hb-ot-shape-complex-default.cc
@@ -68,7 +68,7 @@
   }
 
   for (; script_features && *script_features; script_features++)
-    plan->map.add_bool_feature (*script_features);
+    plan->map.add_global_bool_feature (*script_features);
 }
 
 static hb_ot_shape_normalization_mode_t
@@ -221,6 +221,6 @@
   NULL, /* decompose */
   compose_default,
   NULL, /* setup_masks */
-  true, /* zero_width_attached_marks */
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE,
   true, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-indic-machine.hh b/src/hb-ot-shape-complex-indic-machine.hh
index ed40b96..ada8891 100644
--- a/src/hb-ot-shape-complex-indic-machine.hh
+++ b/src/hb-ot-shape-complex-indic-machine.hh
@@ -1,5 +1,5 @@
 
-#line 1 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 1 "hb-ot-shape-complex-indic-machine.rl"
 /*
  * Copyright © 2011,2012  Google, Inc.
  *
@@ -32,7 +32,7 @@
 #include "hb-private.hh"
 
 
-#line 36 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 36 "hb-ot-shape-complex-indic-machine.hh.tmp"
 static const unsigned char _indic_syllable_machine_trans_keys[] = {
 	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, 
@@ -1245,11 +1245,11 @@
 static const int indic_syllable_machine_en_main = 170;
 
 
-#line 36 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 36 "hb-ot-shape-complex-indic-machine.rl"
 
 
 
-#line 91 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 91 "hb-ot-shape-complex-indic-machine.rl"
 
 
 #define found_syllable(syllable_type) \
@@ -1265,11 +1265,11 @@
 static void
 find_syllables (hb_buffer_t *buffer)
 {
-  unsigned int p, pe, eof, ts HB_UNUSED, te, act;
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   
-#line 1273 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 1273 "hb-ot-shape-complex-indic-machine.hh.tmp"
 	{
 	cs = indic_syllable_machine_start;
 	ts = 0;
@@ -1277,7 +1277,7 @@
 	act = 0;
 	}
 
-#line 112 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 112 "hb-ot-shape-complex-indic-machine.rl"
 
 
   p = 0;
@@ -1286,7 +1286,7 @@
   unsigned int last = 0;
   unsigned int syllable_serial = 1;
   
-#line 1290 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 1290 "hb-ot-shape-complex-indic-machine.hh.tmp"
 	{
 	int _slen;
 	int _trans;
@@ -1300,7 +1300,7 @@
 #line 1 "NONE"
 	{ts = p;}
 	break;
-#line 1304 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 1304 "hb-ot-shape-complex-indic-machine.hh.tmp"
 	}
 
 	_keys = _indic_syllable_machine_trans_keys + (cs<<1);
@@ -1323,59 +1323,59 @@
 	{te = p+1;}
 	break;
 	case 14:
-#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 83 "hb-ot-shape-complex-indic-machine.rl"
 	{te = p+1;{ found_syllable (consonant_syllable); }}
 	break;
 	case 16:
-#line 84 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 84 "hb-ot-shape-complex-indic-machine.rl"
 	{te = p+1;{ found_syllable (vowel_syllable); }}
 	break;
 	case 21:
-#line 85 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 85 "hb-ot-shape-complex-indic-machine.rl"
 	{te = p+1;{ found_syllable (standalone_cluster); }}
 	break;
 	case 18:
-#line 86 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 86 "hb-ot-shape-complex-indic-machine.rl"
 	{te = p+1;{ found_syllable (broken_cluster); }}
 	break;
 	case 11:
-#line 87 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 87 "hb-ot-shape-complex-indic-machine.rl"
 	{te = p+1;{ found_syllable (non_indic_cluster); }}
 	break;
 	case 13:
-#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 83 "hb-ot-shape-complex-indic-machine.rl"
 	{te = p;p--;{ found_syllable (consonant_syllable); }}
 	break;
 	case 15:
-#line 84 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 84 "hb-ot-shape-complex-indic-machine.rl"
 	{te = p;p--;{ found_syllable (vowel_syllable); }}
 	break;
 	case 20:
-#line 85 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 85 "hb-ot-shape-complex-indic-machine.rl"
 	{te = p;p--;{ found_syllable (standalone_cluster); }}
 	break;
 	case 17:
-#line 86 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 86 "hb-ot-shape-complex-indic-machine.rl"
 	{te = p;p--;{ found_syllable (broken_cluster); }}
 	break;
 	case 19:
-#line 87 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 87 "hb-ot-shape-complex-indic-machine.rl"
 	{te = p;p--;{ found_syllable (non_indic_cluster); }}
 	break;
 	case 1:
-#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 83 "hb-ot-shape-complex-indic-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (consonant_syllable); }}
 	break;
 	case 3:
-#line 84 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 84 "hb-ot-shape-complex-indic-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (vowel_syllable); }}
 	break;
 	case 7:
-#line 85 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 85 "hb-ot-shape-complex-indic-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (standalone_cluster); }}
 	break;
 	case 4:
-#line 86 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 86 "hb-ot-shape-complex-indic-machine.rl"
 	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
 	break;
 	case 5:
@@ -1396,22 +1396,22 @@
 	case 8:
 #line 1 "NONE"
 	{te = p+1;}
-#line 83 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 83 "hb-ot-shape-complex-indic-machine.rl"
 	{act = 1;}
 	break;
 	case 6:
 #line 1 "NONE"
 	{te = p+1;}
-#line 86 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 86 "hb-ot-shape-complex-indic-machine.rl"
 	{act = 4;}
 	break;
 	case 12:
 #line 1 "NONE"
 	{te = p+1;}
-#line 87 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 87 "hb-ot-shape-complex-indic-machine.rl"
 	{act = 5;}
 	break;
-#line 1415 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 1415 "hb-ot-shape-complex-indic-machine.hh.tmp"
 	}
 
 _again:
@@ -1420,7 +1420,7 @@
 #line 1 "NONE"
 	{ts = 0;}
 	break;
-#line 1424 "../../src/hb-ot-shape-complex-indic-machine.hh.tmp"
+#line 1424 "hb-ot-shape-complex-indic-machine.hh.tmp"
 	}
 
 	if ( ++p != pe )
@@ -1436,7 +1436,7 @@
 
 	}
 
-#line 121 "../../src/hb-ot-shape-complex-indic-machine.rl"
+#line 121 "hb-ot-shape-complex-indic-machine.rl"
 
 }
 
diff --git a/src/hb-ot-shape-complex-indic-machine.rl b/src/hb-ot-shape-complex-indic-machine.rl
index 11115c9..f9f07d8 100644
--- a/src/hb-ot-shape-complex-indic-machine.rl
+++ b/src/hb-ot-shape-complex-indic-machine.rl
@@ -103,7 +103,7 @@
 static void
 find_syllables (hb_buffer_t *buffer)
 {
-  unsigned int p, pe, eof, ts HB_UNUSED, te, act;
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
   int cs;
   hb_glyph_info_t *info = buffer->info;
   %%{
diff --git a/src/hb-ot-shape-complex-indic-private.hh b/src/hb-ot-shape-complex-indic-private.hh
index e36090e..39268b1 100644
--- a/src/hb-ot-shape-complex-indic-private.hh
+++ b/src/hb-ot-shape-complex-indic-private.hh
@@ -34,11 +34,6 @@
 #include "hb-ot-shape-private.hh" /* XXX Remove */
 
 
-/* buffer var allocations */
-#define indic_category() complex_var_u8_0() /* indic_category_t */
-#define indic_position() complex_var_u8_1() /* indic_matra_category_t */
-
-
 #define INDIC_TABLE_ELEMENT_TYPE uint16_t
 
 /* Cateories used in the OpenType spec:
@@ -102,7 +97,7 @@
   INDIC_SYLLABIC_CATEGORY_BINDU			= OT_SM,
   INDIC_SYLLABIC_CATEGORY_CONSONANT		= OT_C,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_DEAD	= OT_C,
-  INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL	= OT_C,
+  INDIC_SYLLABIC_CATEGORY_CONSONANT_FINAL	= OT_CM,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_HEAD_LETTER	= OT_C,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_MEDIAL	= OT_CM,
   INDIC_SYLLABIC_CATEGORY_CONSONANT_PLACEHOLDER	= OT_NBSP,
@@ -150,239 +145,7 @@
    ASSERT_STATIC_EXPR_ZERO (S < 255 && M < 255) + \
    ((M << 8) | S))
 
-
-#include "hb-ot-shape-complex-indic-table.hh"
-
-
-#define IN_HALF_BLOCK(u, Base) (((u) & ~0x7F) == (Base))
-
-#define IS_DEVA(u) (IN_HALF_BLOCK (u, 0x0900))
-#define IS_BENG(u) (IN_HALF_BLOCK (u, 0x0980))
-#define IS_GURU(u) (IN_HALF_BLOCK (u, 0x0A00))
-#define IS_GUJR(u) (IN_HALF_BLOCK (u, 0x0A80))
-#define IS_ORYA(u) (IN_HALF_BLOCK (u, 0x0B00))
-#define IS_TAML(u) (IN_HALF_BLOCK (u, 0x0B80))
-#define IS_TELU(u) (IN_HALF_BLOCK (u, 0x0C00))
-#define IS_KNDA(u) (IN_HALF_BLOCK (u, 0x0C80))
-#define IS_MLYM(u) (IN_HALF_BLOCK (u, 0x0D00))
-#define IS_SINH(u) (IN_HALF_BLOCK (u, 0x0D80))
-#define IS_KHMR(u) (IN_HALF_BLOCK (u, 0x1780))
-
-
-#define MATRA_POS_LEFT(u)	POS_PRE_M
-#define MATRA_POS_RIGHT(u)	( \
-				  IS_DEVA(u) ? POS_AFTER_SUB  : \
-				  IS_BENG(u) ? POS_AFTER_POST : \
-				  IS_GURU(u) ? POS_AFTER_POST : \
-				  IS_GUJR(u) ? POS_AFTER_POST : \
-				  IS_ORYA(u) ? POS_AFTER_POST : \
-				  IS_TAML(u) ? POS_AFTER_POST : \
-				  IS_TELU(u) ? (u <= 0x0C42 ? POS_BEFORE_SUB : POS_AFTER_SUB) : \
-				  IS_KNDA(u) ? (u < 0x0CC3 || u > 0xCD6 ? POS_BEFORE_SUB : POS_AFTER_SUB) : \
-				  IS_MLYM(u) ? POS_AFTER_POST : \
-				  IS_SINH(u) ? POS_AFTER_SUB  : \
-				  IS_KHMR(u) ? POS_AFTER_POST : \
-				  /*default*/  POS_AFTER_SUB    \
-				)
-#define MATRA_POS_TOP(u)	( /* BENG and MLYM don't have top matras. */ \
-				  IS_DEVA(u) ? POS_AFTER_SUB  : \
-				  IS_GURU(u) ? POS_AFTER_POST : /* Deviate from spec */ \
-				  IS_GUJR(u) ? POS_AFTER_SUB  : \
-				  IS_ORYA(u) ? POS_AFTER_MAIN : \
-				  IS_TAML(u) ? POS_AFTER_SUB  : \
-				  IS_TELU(u) ? POS_BEFORE_SUB : \
-				  IS_KNDA(u) ? POS_BEFORE_SUB : \
-				  IS_SINH(u) ? POS_AFTER_SUB  : \
-				  IS_KHMR(u) ? POS_AFTER_POST : \
-				  /*default*/  POS_AFTER_SUB    \
-				)
-#define MATRA_POS_BOTTOM(u)	( \
-				  IS_DEVA(u) ? POS_AFTER_SUB  : \
-				  IS_BENG(u) ? POS_AFTER_SUB  : \
-				  IS_GURU(u) ? POS_AFTER_POST : \
-				  IS_GUJR(u) ? POS_AFTER_POST : \
-				  IS_ORYA(u) ? POS_AFTER_SUB  : \
-				  IS_TAML(u) ? POS_AFTER_POST : \
-				  IS_TELU(u) ? POS_BEFORE_SUB : \
-				  IS_KNDA(u) ? POS_BEFORE_SUB : \
-				  IS_MLYM(u) ? POS_AFTER_POST : \
-				  IS_SINH(u) ? POS_AFTER_SUB  : \
-				  IS_KHMR(u) ? POS_AFTER_POST : \
-				  /*default*/  POS_AFTER_SUB    \
-				)
-
-
-static inline indic_position_t
-matra_position (hb_codepoint_t u, indic_position_t side)
-{
-  switch ((int) side)
-  {
-    case POS_PRE_C:	return MATRA_POS_LEFT (u);
-    case POS_POST_C:	return MATRA_POS_RIGHT (u);
-    case POS_ABOVE_C:	return MATRA_POS_TOP (u);
-    case POS_BELOW_C:	return MATRA_POS_BOTTOM (u);
-  };
-  return side;
-}
-
-
-
-/* XXX
- * This is a hack for now.  We should move this data into the main Indic table.
- * Or completely remove it and just check in the tables.
- */
-static const hb_codepoint_t ra_chars[] = {
-  0x0930, /* Devanagari */
-  0x09B0, /* Bengali */
-  0x09F0, /* Bengali */
-  0x0A30, /* Gurmukhi */	/* No Reph */
-  0x0AB0, /* Gujarati */
-  0x0B30, /* Oriya */
-  0x0BB0, /* Tamil */		/* No Reph */
-  0x0C30, /* Telugu */		/* Reph formed only with ZWJ */
-  0x0CB0, /* Kannada */
-  0x0D30, /* Malayalam */	/* No Reph, Logical Repha */
-
-  0x0DBB, /* Sinhala */		/* Reph formed only with ZWJ */
-
-  0x179A, /* Khmer */		/* No Reph, Visual Repha */
-};
-
-static inline indic_position_t
-consonant_position (hb_codepoint_t  u)
-{
-  if ((u & ~0x007F) == 0x1780)
-    return POS_BELOW_C; /* In Khmer coeng model, post and below forms should not be reordered. */
-  return POS_BASE_C; /* Will recategorize later based on font lookups. */
-}
-
-static inline bool
-is_ra (hb_codepoint_t u)
-{
-  for (unsigned int i = 0; i < ARRAY_LENGTH (ra_chars); i++)
-    if (u == ra_chars[i])
-      return true;
-  return false;
-}
-
-
-static inline bool
-is_one_of (const hb_glyph_info_t &info, unsigned int flags)
-{
-  /* If it ligated, all bets are off. */
-  if (is_a_ligature (info)) return false;
-  return !!(FLAG (info.indic_category()) & flags);
-}
-
-#define JOINER_FLAGS (FLAG (OT_ZWJ) | FLAG (OT_ZWNJ))
-static inline bool
-is_joiner (const hb_glyph_info_t &info)
-{
-  return is_one_of (info, JOINER_FLAGS);
-}
-
-/* Note:
- *
- * We treat Vowels and placeholders as if they were consonants.  This is safe because Vowels
- * cannot happen in a consonant syllable.  The plus side however is, we can call the
- * consonant syllable logic from the vowel syllable function and get it all right! */
-#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CM) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP) | FLAG (OT_DOTTEDCIRCLE))
-static inline bool
-is_consonant (const hb_glyph_info_t &info)
-{
-  return is_one_of (info, CONSONANT_FLAGS);
-}
-
-#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)
-{
-  hb_codepoint_t u = info.codepoint;
-  unsigned int type = get_indic_categories (u);
-  indic_category_t cat = (indic_category_t) (type & 0x7F);
-  indic_position_t pos = (indic_position_t) (type >> 8);
-
-
-  /*
-   * Re-assign category
-   */
-
-
-  /* The spec says U+0952 is OT_A.  However, testing shows that Uniscribe
-   * treats U+0951..U+0952 all as OT_VD.
-   * TESTS:
-   * U+092E,U+0947,U+0952
-   * U+092E,U+0952,U+0947
-   * U+092E,U+0947,U+0951
-   * U+092E,U+0951,U+0947
-   * */
-  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x0951, 0x0954)))
-    cat = OT_VD;
-
-  if (unlikely (u == 0x17D1))
-    cat = OT_X;
-  if (cat == OT_X &&
-      unlikely (hb_in_range<hb_codepoint_t> (u, 0x17CB, 0x17D3))) /* Khmer Various signs */
-  {
-    /* These are like Top Matras. */
-    cat = OT_M;
-    pos = POS_ABOVE_C;
-  }
-  if (u == 0x17C6) /* Khmer Bindu doesn't like to be repositioned. */
-    cat = OT_N;
-
-  if (unlikely (u == 0x17D2)) cat = OT_Coeng; /* Khmer coeng */
-  else if (unlikely (u == 0x200C)) cat = OT_ZWNJ;
-  else if (unlikely (u == 0x200D)) cat = OT_ZWJ;
-  else if (unlikely (u == 0x25CC)) cat = OT_DOTTEDCIRCLE;
-  else if (unlikely (u == 0x0A71)) cat = OT_SM; /* GURMUKHI ADDAK.  More like consonant medial. like 0A75. */
-
-  if (cat == OT_Repha) {
-    /* There are two kinds of characters marked as Repha:
-     * - The ones that are GenCat=Mn are already positioned visually, ie. after base. (eg. Khmer)
-     * - The ones that are GenCat=Lo is encoded logically, ie. beginning of syllable. (eg. Malayalam)
-     *
-     * We recategorize the first kind to look like a Nukta and attached to the base directly.
-     */
-    if (_hb_glyph_info_get_general_category (&info) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
-      cat = OT_N;
-  }
-
-
-
-  /*
-   * Re-assign position.
-   */
-
-  if ((FLAG (cat) & CONSONANT_FLAGS))
-  {
-    pos = consonant_position (u);
-    if (is_ra (u))
-      cat = OT_Ra;
-  }
-  else if (cat == OT_M)
-  {
-    pos = matra_position (u, pos);
-  }
-  else if (cat == OT_SM || cat == OT_VD)
-  {
-    pos = POS_SMVD;
-  }
-
-  if (unlikely (u == 0x0B01)) pos = POS_BEFORE_SUB; /* Oriya Bindu is BeforeSub in the spec. */
-
-
-
-  info.indic_category() = cat;
-  info.indic_position() = pos;
-}
-
-
+HB_INTERNAL INDIC_TABLE_ELEMENT_TYPE
+hb_indic_get_categories (hb_codepoint_t u);
 
 #endif /* HB_OT_SHAPE_COMPLEX_INDIC_PRIVATE_HH */
diff --git a/src/hb-ot-shape-complex-indic-table.hh b/src/hb-ot-shape-complex-indic-table.cc
similarity index 99%
rename from src/hb-ot-shape-complex-indic-table.hh
rename to src/hb-ot-shape-complex-indic-table.cc
index 70765b6..18a022b 100644
--- a/src/hb-ot-shape-complex-indic-table.hh
+++ b/src/hb-ot-shape-complex-indic-table.cc
@@ -14,8 +14,7 @@
  * # Date: 2012-05-14, 22:42:00 GMT [KW, LI]
  */
 
-#ifndef HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH
-#define HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH
+#include "hb-ot-shape-complex-indic-private.hh"
 
 
 #define ISC_A	INDIC_SYLLABIC_CATEGORY_AVAGRAHA		/*  11 chars; Avagraha */
@@ -807,8 +806,8 @@
 
 }; /* Table occupancy: 60% */
 
-static INDIC_TABLE_ELEMENT_TYPE
-get_indic_categories (hb_codepoint_t u)
+INDIC_TABLE_ELEMENT_TYPE
+hb_indic_get_categories (hb_codepoint_t u)
 {
   if (0x0900 <= u && u <= 0x10A0) return indic_table[u - 0x0900 + indic_offset_0x0900];
   if (0x1700 <= u && u <= 0x1800) return indic_table[u - 0x1700 + indic_offset_0x1700];
@@ -867,6 +866,4 @@
 #undef IMC_TR
 #undef IMC_VOL
 
-#endif /* HB_OT_SHAPE_COMPLEX_INDIC_TABLE_HH */
-
 /* == End of generated table == */
diff --git a/src/hb-ot-shape-complex-indic.cc b/src/hb-ot-shape-complex-indic.cc
index 1bc8a77..eb1e0be 100644
--- a/src/hb-ot-shape-complex-indic.cc
+++ b/src/hb-ot-shape-complex-indic.cc
@@ -27,49 +27,245 @@
 #include "hb-ot-shape-complex-indic-private.hh"
 #include "hb-ot-layout-private.hh"
 
+/* buffer var allocations */
+#define indic_category() complex_var_u8_0() /* indic_category_t */
+#define indic_position() complex_var_u8_1() /* indic_position_t */
+
 
 /*
- * Global Indic shaper options.
+ * Indic shaper.
  */
 
-struct indic_options_t
+
+#define IN_HALF_BLOCK(u, Base) (((u) & ~0x7F) == (Base))
+
+#define IS_DEVA(u) (IN_HALF_BLOCK (u, 0x0900))
+#define IS_BENG(u) (IN_HALF_BLOCK (u, 0x0980))
+#define IS_GURU(u) (IN_HALF_BLOCK (u, 0x0A00))
+#define IS_GUJR(u) (IN_HALF_BLOCK (u, 0x0A80))
+#define IS_ORYA(u) (IN_HALF_BLOCK (u, 0x0B00))
+#define IS_TAML(u) (IN_HALF_BLOCK (u, 0x0B80))
+#define IS_TELU(u) (IN_HALF_BLOCK (u, 0x0C00))
+#define IS_KNDA(u) (IN_HALF_BLOCK (u, 0x0C80))
+#define IS_MLYM(u) (IN_HALF_BLOCK (u, 0x0D00))
+#define IS_SINH(u) (IN_HALF_BLOCK (u, 0x0D80))
+#define IS_KHMR(u) (IN_HALF_BLOCK (u, 0x1780))
+
+
+#define MATRA_POS_LEFT(u)	POS_PRE_M
+#define MATRA_POS_RIGHT(u)	( \
+				  IS_DEVA(u) ? POS_AFTER_SUB  : \
+				  IS_BENG(u) ? POS_AFTER_POST : \
+				  IS_GURU(u) ? POS_AFTER_POST : \
+				  IS_GUJR(u) ? POS_AFTER_POST : \
+				  IS_ORYA(u) ? POS_AFTER_POST : \
+				  IS_TAML(u) ? POS_AFTER_POST : \
+				  IS_TELU(u) ? (u <= 0x0C42 ? POS_BEFORE_SUB : POS_AFTER_SUB) : \
+				  IS_KNDA(u) ? (u < 0x0CC3 || u > 0xCD6 ? POS_BEFORE_SUB : POS_AFTER_SUB) : \
+				  IS_MLYM(u) ? POS_AFTER_POST : \
+				  IS_SINH(u) ? POS_AFTER_SUB  : \
+				  IS_KHMR(u) ? POS_AFTER_POST : \
+				  /*default*/  POS_AFTER_SUB    \
+				)
+#define MATRA_POS_TOP(u)	( /* BENG and MLYM don't have top matras. */ \
+				  IS_DEVA(u) ? POS_AFTER_SUB  : \
+				  IS_GURU(u) ? POS_AFTER_POST : /* Deviate from spec */ \
+				  IS_GUJR(u) ? POS_AFTER_SUB  : \
+				  IS_ORYA(u) ? POS_AFTER_MAIN : \
+				  IS_TAML(u) ? POS_AFTER_SUB  : \
+				  IS_TELU(u) ? POS_BEFORE_SUB : \
+				  IS_KNDA(u) ? POS_BEFORE_SUB : \
+				  IS_SINH(u) ? POS_AFTER_SUB  : \
+				  IS_KHMR(u) ? POS_AFTER_POST : \
+				  /*default*/  POS_AFTER_SUB    \
+				)
+#define MATRA_POS_BOTTOM(u)	( \
+				  IS_DEVA(u) ? POS_AFTER_SUB  : \
+				  IS_BENG(u) ? POS_AFTER_SUB  : \
+				  IS_GURU(u) ? POS_AFTER_POST : \
+				  IS_GUJR(u) ? POS_AFTER_POST : \
+				  IS_ORYA(u) ? POS_AFTER_SUB  : \
+				  IS_TAML(u) ? POS_AFTER_POST : \
+				  IS_TELU(u) ? POS_BEFORE_SUB : \
+				  IS_KNDA(u) ? POS_BEFORE_SUB : \
+				  IS_MLYM(u) ? POS_AFTER_POST : \
+				  IS_SINH(u) ? POS_AFTER_SUB  : \
+				  IS_KHMR(u) ? POS_AFTER_POST : \
+				  /*default*/  POS_AFTER_SUB    \
+				)
+
+static inline indic_position_t
+matra_position (hb_codepoint_t u, indic_position_t side)
 {
-  int initialized : 1;
-  int uniscribe_bug_compatible : 1;
-};
-
-union indic_options_union_t {
-  int i;
-  indic_options_t opts;
-};
-ASSERT_STATIC (sizeof (int) == sizeof (indic_options_union_t));
-
-static indic_options_union_t
-indic_options_init (void)
-{
-  indic_options_union_t u;
-  u.i = 0;
-  u.opts.initialized = 1;
-
-  char *c = getenv ("HB_OT_INDIC_OPTIONS");
-  u.opts.uniscribe_bug_compatible = c && strstr (c, "uniscribe-bug-compatible");
-
-  return u;
+  switch ((int) side)
+  {
+    case POS_PRE_C:	return MATRA_POS_LEFT (u);
+    case POS_POST_C:	return MATRA_POS_RIGHT (u);
+    case POS_ABOVE_C:	return MATRA_POS_TOP (u);
+    case POS_BELOW_C:	return MATRA_POS_BOTTOM (u);
+  };
+  return side;
 }
 
-static inline indic_options_t
-indic_options (void)
-{
-  static indic_options_union_t options;
+/* XXX
+ * This is a hack for now.  We should move this data into the main Indic table.
+ * Or completely remove it and just check in the tables.
+ */
+static const hb_codepoint_t ra_chars[] = {
+  0x0930, /* Devanagari */
+  0x09B0, /* Bengali */
+  0x09F0, /* Bengali */
+  0x0A30, /* Gurmukhi */	/* No Reph */
+  0x0AB0, /* Gujarati */
+  0x0B30, /* Oriya */
+  0x0BB0, /* Tamil */		/* No Reph */
+  0x0C30, /* Telugu */		/* Reph formed only with ZWJ */
+  0x0CB0, /* Kannada */
+  0x0D30, /* Malayalam */	/* No Reph, Logical Repha */
 
-  if (unlikely (!options.i)) {
-    /* This is idempotent and threadsafe. */
-    options = indic_options_init ();
+  0x0DBB, /* Sinhala */		/* Reph formed only with ZWJ */
+
+  0x179A, /* Khmer */		/* No Reph, Visual Repha */
+};
+
+static inline indic_position_t
+consonant_position (hb_codepoint_t  u)
+{
+  if ((u & ~0x007F) == 0x1780)
+    return POS_BELOW_C; /* In Khmer coeng model, post and below forms should not be reordered. */
+  return POS_BASE_C; /* Will recategorize later based on font lookups. */
+}
+
+static inline bool
+is_ra (hb_codepoint_t u)
+{
+  for (unsigned int i = 0; i < ARRAY_LENGTH (ra_chars); i++)
+    if (u == ra_chars[i])
+      return true;
+  return false;
+}
+
+static inline bool
+is_one_of (const hb_glyph_info_t &info, unsigned int flags)
+{
+  /* If it ligated, all bets are off. */
+  if (is_a_ligature (info)) return false;
+  return !!(FLAG (info.indic_category()) & flags);
+}
+
+#define JOINER_FLAGS (FLAG (OT_ZWJ) | FLAG (OT_ZWNJ))
+static inline bool
+is_joiner (const hb_glyph_info_t &info)
+{
+  return is_one_of (info, JOINER_FLAGS);
+}
+
+/* Note:
+ *
+ * We treat Vowels and placeholders as if they were consonants.  This is safe because Vowels
+ * cannot happen in a consonant syllable.  The plus side however is, we can call the
+ * consonant syllable logic from the vowel syllable function and get it all right! */
+#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CM) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP) | FLAG (OT_DOTTEDCIRCLE))
+static inline bool
+is_consonant (const hb_glyph_info_t &info)
+{
+  return is_one_of (info, CONSONANT_FLAGS);
+}
+
+#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)
+{
+  hb_codepoint_t u = info.codepoint;
+  unsigned int type = hb_indic_get_categories (u);
+  indic_category_t cat = (indic_category_t) (type & 0x7F);
+  indic_position_t pos = (indic_position_t) (type >> 8);
+
+
+  /*
+   * Re-assign category
+   */
+
+
+  /* The spec says U+0952 is OT_A.  However, testing shows that Uniscribe
+   * treats U+0951..U+0952 all as OT_VD.
+   * TESTS:
+   * U+092E,U+0947,U+0952
+   * U+092E,U+0952,U+0947
+   * U+092E,U+0947,U+0951
+   * U+092E,U+0951,U+0947
+   * */
+  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0x0951, 0x0954)))
+    cat = OT_VD;
+
+  if (unlikely (u == 0x17D1))
+    cat = OT_X;
+  if (cat == OT_X &&
+      unlikely (hb_in_range<hb_codepoint_t> (u, 0x17CB, 0x17D3))) /* Khmer Various signs */
+  {
+    /* These are like Top Matras. */
+    cat = OT_M;
+    pos = POS_ABOVE_C;
+  }
+  if (u == 0x17C6) /* Khmer Bindu doesn't like to be repositioned. */
+    cat = OT_N;
+
+  if (unlikely (u == 0x17D2)) cat = OT_Coeng; /* Khmer coeng */
+  else if (unlikely (u == 0x200C)) cat = OT_ZWNJ;
+  else if (unlikely (u == 0x200D)) cat = OT_ZWJ;
+  else if (unlikely (u == 0x25CC)) cat = OT_DOTTEDCIRCLE;
+  else if (unlikely (u == 0x0A71)) cat = OT_SM; /* GURMUKHI ADDAK.  More like consonant medial. like 0A75. */
+
+  if (cat == OT_Repha) {
+    /* There are two kinds of characters marked as Repha:
+     * - The ones that are GenCat=Mn are already positioned visually, ie. after base. (eg. Khmer)
+     * - The ones that are GenCat=Lo is encoded logically, ie. beginning of syllable. (eg. Malayalam)
+     *
+     * We recategorize the first kind to look like a Nukta and attached to the base directly.
+     */
+    if (_hb_glyph_info_get_general_category (&info) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
+      cat = OT_N;
   }
 
-  return options.opts;
+
+
+  /*
+   * Re-assign position.
+   */
+
+  if ((FLAG (cat) & CONSONANT_FLAGS))
+  {
+    pos = consonant_position (u);
+    if (is_ra (u))
+      cat = OT_Ra;
+  }
+  else if (cat == OT_M)
+  {
+    pos = matra_position (u, pos);
+  }
+  else if (cat == OT_SM || cat == OT_VD)
+  {
+    pos = POS_SMVD;
+  }
+
+  if (unlikely (u == 0x0B01)) pos = POS_BEFORE_SUB; /* Oriya Bindu is BeforeSub in the spec. */
+
+
+
+  info.indic_category() = cat;
+  info.indic_position() = pos;
 }
 
+/*
+ * Things above this line should ideally be moved to the Indic table itself.
+ */
+
 
 /*
  * Indic configurations.  Note that we do not want to keep every single script-specific
@@ -123,8 +319,6 @@
   {HB_SCRIPT_MALAYALAM,	true, 0x0D4D,BASE_POS_LAST, REPH_POS_AFTER_MAIN, REPH_MODE_LOG_REPHA},
   {HB_SCRIPT_SINHALA,	false,0x0DCA,BASE_POS_FIRST,REPH_POS_AFTER_MAIN, REPH_MODE_EXPLICIT},
   {HB_SCRIPT_KHMER,	false,0x17D2,BASE_POS_FIRST,REPH_POS_DEFAULT,    REPH_MODE_VIS_REPHA},
-  /* Myanmar does not have the "old_indic" behavior, even though it has a "new" tag. */
-  {HB_SCRIPT_MYANMAR,	false,0x1039,BASE_POS_LAST, REPH_POS_DEFAULT,    REPH_MODE_EXPLICIT},
 };
 
 
@@ -135,7 +329,7 @@
 
 struct feature_list_t {
   hb_tag_t tag;
-  hb_bool_t is_global;
+  hb_ot_map_feature_flags_t flags;
 };
 
 static const feature_list_t
@@ -145,32 +339,32 @@
    * Basic features.
    * These features are applied in order, one at a time, after initial_reordering.
    */
-  {HB_TAG('n','u','k','t'), true},
-  {HB_TAG('a','k','h','n'), true},
-  {HB_TAG('r','p','h','f'), false},
-  {HB_TAG('r','k','r','f'), true},
-  {HB_TAG('p','r','e','f'), false},
-  {HB_TAG('h','a','l','f'), false},
-  {HB_TAG('b','l','w','f'), false},
-  {HB_TAG('a','b','v','f'), false},
-  {HB_TAG('p','s','t','f'), false},
-  {HB_TAG('c','f','a','r'), false},
-  {HB_TAG('c','j','c','t'), true},
-  {HB_TAG('v','a','t','u'), true},
+  {HB_TAG('n','u','k','t'), F_GLOBAL},
+  {HB_TAG('a','k','h','n'), F_GLOBAL},
+  {HB_TAG('r','p','h','f'), F_NONE},
+  {HB_TAG('r','k','r','f'), F_GLOBAL},
+  {HB_TAG('p','r','e','f'), F_NONE},
+  {HB_TAG('b','l','w','f'), F_NONE},
+  {HB_TAG('h','a','l','f'), F_NONE},
+  {HB_TAG('a','b','v','f'), F_NONE},
+  {HB_TAG('p','s','t','f'), F_NONE},
+  {HB_TAG('c','f','a','r'), F_NONE},
+  {HB_TAG('v','a','t','u'), F_GLOBAL},
+  {HB_TAG('c','j','c','t'), F_GLOBAL},
   /*
    * Other features.
    * These features are applied all at once, after final_reordering.
    */
-  {HB_TAG('i','n','i','t'), false},
-  {HB_TAG('p','r','e','s'), true},
-  {HB_TAG('a','b','v','s'), true},
-  {HB_TAG('b','l','w','s'), true},
-  {HB_TAG('p','s','t','s'), true},
-  {HB_TAG('h','a','l','n'), true},
+  {HB_TAG('i','n','i','t'), F_NONE},
+  {HB_TAG('p','r','e','s'), F_GLOBAL},
+  {HB_TAG('a','b','v','s'), F_GLOBAL},
+  {HB_TAG('b','l','w','s'), F_GLOBAL},
+  {HB_TAG('p','s','t','s'), F_GLOBAL},
+  {HB_TAG('h','a','l','n'), F_GLOBAL},
   /* Positioning features, though we don't care about the types. */
-  {HB_TAG('d','i','s','t'), true},
-  {HB_TAG('a','b','v','m'), true},
-  {HB_TAG('b','l','w','m'), true},
+  {HB_TAG('d','i','s','t'), F_GLOBAL},
+  {HB_TAG('a','b','v','m'), F_GLOBAL},
+  {HB_TAG('b','l','w','m'), F_GLOBAL},
 };
 
 /*
@@ -182,13 +376,13 @@
   RPHF,
   _RKRF,
   PREF,
-  HALF,
   BLWF,
+  HALF,
   ABVF,
   PSTF,
   CFAR,
-  _CJCT,
   _VATU,
+  _CJCT,
 
   INIT,
   _PRES,
@@ -225,21 +419,21 @@
   /* Do this before any lookups have been applied. */
   map->add_gsub_pause (setup_syllables);
 
-  map->add_bool_feature (HB_TAG('l','o','c','l'));
+  map->add_global_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'));
+  map->add_global_bool_feature (HB_TAG('c','c','m','p'));
 
 
   unsigned int i = 0;
   map->add_gsub_pause (initial_reordering);
   for (; i < INDIC_BASIC_FEATURES; i++) {
-    map->add_bool_feature (indic_features[i].tag, indic_features[i].is_global);
+    map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ);
     map->add_gsub_pause (NULL);
   }
   map->add_gsub_pause (final_reordering);
   for (; i < INDIC_NUM_FEATURES; i++) {
-    map->add_bool_feature (indic_features[i].tag, indic_features[i].is_global);
+    map->add_feature (indic_features[i].tag, 1, indic_features[i].flags | F_MANUAL_ZWJ);
   }
 }
 
@@ -247,10 +441,10 @@
 override_features_indic (hb_ot_shape_planner_t *plan)
 {
   /* Uniscribe does not apply 'kern'. */
-  if (indic_options ().uniscribe_bug_compatible)
-    plan->map.add_feature (HB_TAG('k','e','r','n'), 0, true);
+  if (hb_options ().uniscribe_bug_compatible)
+    plan->map.add_feature (HB_TAG('k','e','r','n'), 0, F_GLOBAL);
 
-  plan->map.add_feature (HB_TAG('l','i','g','a'), 0, true);
+  plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL);
 }
 
 
@@ -263,10 +457,10 @@
 			    &lookups, &count);
   }
 
-  inline bool would_substitute (hb_codepoint_t    *glyphs,
-				unsigned int       glyphs_count,
-				bool               zero_context,
-				hb_face_t         *face) const
+  inline bool would_substitute (const 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_lookup_would_substitute_fast (face, lookups[i].index, glyphs, glyphs_count, zero_context))
@@ -338,7 +532,8 @@
   indic_plan->pstf.init (&plan->map, HB_TAG('p','s','t','f'));
 
   for (unsigned int i = 0; i < ARRAY_LENGTH (indic_plan->mask_array); i++)
-    indic_plan->mask_array[i] = indic_features[i].is_global ? 0 : plan->map.get_1_mask (indic_features[i].tag);
+    indic_plan->mask_array[i] = (indic_features[i].flags & F_GLOBAL) ?
+				 0 : plan->map.get_1_mask (indic_features[i].tag);
 
   return indic_plan;
 }
@@ -351,13 +546,30 @@
 
 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)
+			      const hb_codepoint_t glyphs[2],
+			      hb_face_t *face)
 {
+  /* For old-spec, the order of glyphs is Consonant,Virama,
+   * whereas for new-spec, it's Virama,Consonant.  However,
+   * some broken fonts (like Free Sans) simply copied lookups
+   * from old-spec to new-spec without modification.
+   * And oddly enough, Uniscribe seems to respect those lookups.
+   * Eg. in the sequence U+0924,U+094D,U+0930, Uniscribe finds
+   * base at 0.  The font however, only has lookups matching
+   * 930,94D in 'blwf', not the expected 94D,930 (with new-spec
+   * table).  As such, we simply match both sequences.  Seems
+   * to work. */
   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;
+  hb_codepoint_t glyphs_r[2] = {glyphs[1], glyphs[0]};
+  if (indic_plan->pref.would_substitute (glyphs  , 2, zero_context, face) ||
+      indic_plan->pref.would_substitute (glyphs_r, 2, zero_context, face))
+    return POS_POST_C;
+  if (indic_plan->blwf.would_substitute (glyphs  , 2, zero_context, face) ||
+      indic_plan->blwf.would_substitute (glyphs_r, 2, zero_context, face))
+    return POS_BELOW_C;
+  if (indic_plan->pstf.would_substitute (glyphs  , 2, zero_context, face) ||
+      indic_plan->pstf.would_substitute (glyphs_r, 2, zero_context, face))
+    return POS_POST_C;
   return POS_BASE_C;
 }
 
@@ -415,16 +627,15 @@
 {
   const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data;
 
-  unsigned int consonant_pos = indic_plan->is_old_spec ? 0 : 1;
   hb_codepoint_t glyphs[2];
-  if (indic_plan->get_virama_glyph (font, &glyphs[1 - consonant_pos]))
+  if (indic_plan->get_virama_glyph (font, &glyphs[0]))
   {
     hb_face_t *face = font->face;
     unsigned int count = buffer->len;
     for (unsigned int i = 0; i < count; i++)
       if (buffer->info[i].indic_position() == POS_BASE_C) {
-	glyphs[consonant_pos] = buffer->info[i].codepoint;
-	buffer->info[i].indic_position() = consonant_position_from_face (indic_plan, glyphs, 2, face);
+	glyphs[1] = buffer->info[i].codepoint;
+	buffer->info[i].indic_position() = consonant_position_from_face (indic_plan, glyphs, face);
       }
   }
 }
@@ -522,7 +733,7 @@
 	     *
 	     * IMPLEMENTATION NOTES:
 	     *
-	     * Our pre-base reordering Ra's are marked POS_BELOW, so will be skipped
+	     * Our pre-base reordering Ra's are marked POS_POST_C, so will be skipped
 	     * by the logic above already.
 	     */
 
@@ -577,15 +788,12 @@
      *    base consonants.
      *
      *  Only do this for unforced Reph. (ie. not for Ra,H,ZWJ. */
-    if (has_reph && base == start && start - limit <= 2) {
+    if (has_reph && base == start && limit - base <= 2) {
       /* Have no other consonant, so Reph is not formed and Ra becomes base. */
       has_reph = false;
     }
   }
 
-  if (base < end)
-    info[base].indic_position() = POS_BASE_C;
-
 
   /* 2. Decompose and reorder Matras:
    *
@@ -644,15 +852,16 @@
     info[start].indic_position() = POS_RA_TO_BECOME_REPH;
 
   /* For old-style Indic script tags, move the first post-base Halant after
-   * last consonant. */
+   * last consonant.  Only do this if there is *not* a Halant after last
+   * consonant.  Otherwise it becomes messy. */
   if (indic_plan->is_old_spec) {
     for (unsigned int i = base + 1; i < end; i++)
       if (info[i].indic_category() == OT_H) {
         unsigned int j;
         for (j = end - 1; j > i; j--)
-	  if (is_consonant (info[j]))
+	  if (is_consonant (info[j]) || info[j].indic_category() == OT_H)
 	    break;
-	if (j > i) {
+	if (info[j].indic_category() != OT_H && j > i) {
 	  /* Move Halant to after last consonant. */
 	  hb_glyph_info_t t = info[i];
 	  memmove (&info[i], &info[i + 1], (j - i) * sizeof (info[0]));
@@ -745,6 +954,38 @@
       info[i].mask  |= mask;
   }
 
+  if (indic_plan->is_old_spec &&
+      buffer->props.script == HB_SCRIPT_DEVANAGARI)
+  {
+    /* Old-spec eye-lash Ra needs special handling.  From the
+     * spec:
+     *
+     * "The feature 'below-base form' is applied to consonants
+     * having below-base forms and following the base consonant.
+     * The exception is vattu, which may appear below half forms
+     * as well as below the base glyph. The feature 'below-base
+     * form' will be applied to all such occurrences of Ra as well."
+     *
+     * Test case: U+0924,U+094D,U+0930,U+094d,U+0915
+     * with Sanskrit 2003 font.
+     *
+     * However, note that Ra,Halant,ZWJ is the correct way to
+     * request eyelash form of Ra, so we wouldbn't inhibit it
+     * in that sequence.
+     *
+     * Test case: U+0924,U+094D,U+0930,U+094d,U+200D,U+0915
+     */
+    for (unsigned int i = start; i + 1 < base; i++)
+      if (info[i  ].indic_category() == OT_Ra &&
+	  info[i+1].indic_category() == OT_H  &&
+	  (i + 2 == base ||
+	   info[i+2].indic_category() != OT_ZWJ))
+      {
+	info[i  ].mask |= indic_plan->mask_array[BLWF];
+	info[i+1].mask |= indic_plan->mask_array[BLWF];
+      }
+  }
+
   if (indic_plan->mask_array[PREF] && base + 2 < end)
   {
     /* Find a Halant,Ra sequence and mark it for pre-base reordering processing. */
@@ -778,8 +1019,9 @@
       do {
 	j--;
 
-	/* A ZWJ disables CJCT, however, it's mere presence is enough
-	 * to disable ligation.  No explicit action needed. */
+	/* ZWJ/ZWNJ should disable CJCT.  They do that by simply
+	 * being there, since we don't skip them for the CJCT
+	 * feature (ie. F_MANUAL_ZWJ) */
 
 	/* A ZWNJ disables HALF. */
 	if (non_joiner)
@@ -809,7 +1051,7 @@
   /* We treat NBSP/dotted-circle as if they are consonants, so we should just chain.
    * Only if not in compatibility mode that is... */
 
-  if (indic_options ().uniscribe_bug_compatible)
+  if (hb_options ().uniscribe_bug_compatible)
   {
     /* For dotted-circle, this is what Uniscribe does:
      * If dotted-circle is the last glyph, it just does nothing.
@@ -962,6 +1204,13 @@
         base--;
       break;
     }
+  if (base == end && start < base &&
+      info[base - 1].indic_category() != OT_ZWJ)
+    base--;
+  while (start < base &&
+	 (info[base].indic_category() == OT_H ||
+	  info[base].indic_category() == OT_N))
+    base--;
 
 
   /*   o Reorder matras:
@@ -1013,6 +1262,8 @@
 	  hb_glyph_info_t tmp = info[old_pos];
 	  memmove (&info[old_pos], &info[old_pos + 1], (new_pos - old_pos) * sizeof (info[0]));
 	  info[new_pos] = tmp;
+	  if (old_pos < base && base <= new_pos) /* Shouldn't actually happen. */
+	    base--;
 	  new_pos--;
 	}
       buffer->merge_clusters (new_pos, MIN (end, base + 1));
@@ -1071,7 +1322,8 @@
       while (new_reph_pos < base && !is_halant_or_coeng (info[new_reph_pos]))
 	new_reph_pos++;
 
-      if (new_reph_pos < base && is_halant_or_coeng (info[new_reph_pos])) {
+      if (new_reph_pos < base && is_halant_or_coeng (info[new_reph_pos]))
+      {
 	/* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
 	if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1]))
 	  new_reph_pos++;
@@ -1123,7 +1375,8 @@
       while (new_reph_pos < base && !is_halant_or_coeng (info[new_reph_pos]))
 	new_reph_pos++;
 
-      if (new_reph_pos < base && is_halant_or_coeng (info[new_reph_pos])) {
+      if (new_reph_pos < base && is_halant_or_coeng (info[new_reph_pos]))
+      {
 	/* ->If ZWJ or ZWNJ are following this halant, position is moved after it. */
 	if (new_reph_pos + 1 < base && is_joiner (info[new_reph_pos + 1]))
 	  new_reph_pos++;
@@ -1145,7 +1398,7 @@
        * Uniscribe doesn't do this.
        * TEST: U+0930,U+094D,U+0915,U+094B,U+094D
        */
-      if (!indic_options ().uniscribe_bug_compatible &&
+      if (!hb_options ().uniscribe_bug_compatible &&
 	  unlikely (is_halant_or_coeng (info[new_reph_pos]))) {
 	for (unsigned int i = base + 1; i < new_reph_pos; i++)
 	  if (info[i].indic_category() == OT_M) {
@@ -1165,6 +1418,8 @@
       hb_glyph_info_t reph = info[start];
       memmove (&info[start], &info[start + 1], (new_reph_pos - start) * sizeof (info[0]));
       info[new_reph_pos] = reph;
+      if (start < base && base <= new_reph_pos)
+	base--;
     }
   }
 
@@ -1220,9 +1475,11 @@
 	  }
 
 	  if (new_pos > start && is_halant_or_coeng (info[new_pos - 1]))
+	  {
 	    /* -> If ZWJ or ZWNJ follow this halant, position is moved after it. */
 	    if (new_pos < end && is_joiner (info[new_pos]))
 	      new_pos++;
+	  }
 
 	  {
 	    unsigned int old_pos = i;
@@ -1230,6 +1487,8 @@
 	    hb_glyph_info_t tmp = info[old_pos];
 	    memmove (&info[new_pos + 1], &info[new_pos], (old_pos - new_pos) * sizeof (info[0]));
 	    info[new_pos] = tmp;
+	    if (new_pos <= base && base < old_pos)
+	      base++;
 	  }
 	}
 
@@ -1249,7 +1508,7 @@
   /*
    * Finish off the clusters and go home!
    */
-  if (indic_options ().uniscribe_bug_compatible)
+  if (hb_options ().uniscribe_bug_compatible)
   {
     /* Uniscribe merges the entire cluster.
      * This means, half forms are submerged into the main consonants cluster.
@@ -1365,7 +1624,7 @@
 
     hb_codepoint_t glyph;
 
-    if (indic_options ().uniscribe_bug_compatible ||
+    if (hb_options ().uniscribe_bug_compatible ||
 	(c->font->get_glyph (ab, 0, &glyph) &&
 	 indic_plan->pstf.would_substitute (&glyph, 1, true, c->font->face)))
     {
@@ -1408,6 +1667,6 @@
   decompose_indic,
   compose_indic,
   setup_masks_indic,
-  false, /* zero_width_attached_marks */
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
   false, /* fallback_position */
 };
diff --git a/src/hb-ot-shape-complex-myanmar-machine.hh b/src/hb-ot-shape-complex-myanmar-machine.hh
new file mode 100644
index 0000000..9d6f62f
--- /dev/null
+++ b/src/hb-ot-shape-complex-myanmar-machine.hh
@@ -0,0 +1,391 @@
+
+#line 1 "hb-ot-shape-complex-myanmar-machine.rl"
+/*
+ * 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_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
+
+#include "hb-private.hh"
+
+
+#line 36 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
+static const unsigned char _myanmar_syllable_machine_trans_keys[] = {
+	1u, 30u, 3u, 30u, 5u, 29u, 5u, 8u, 5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 
+	3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 1u, 16u, 3u, 29u, 3u, 29u, 3u, 29u, 
+	3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 5u, 29u, 5u, 8u, 
+	5u, 29u, 3u, 25u, 5u, 25u, 5u, 25u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 
+	3u, 30u, 3u, 29u, 1u, 30u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 
+	3u, 29u, 3u, 29u, 3u, 29u, 3u, 29u, 0
+};
+
+static const char _myanmar_syllable_machine_key_spans[] = {
+	30, 28, 25, 4, 25, 23, 21, 21, 
+	27, 27, 27, 27, 16, 27, 27, 27, 
+	27, 27, 27, 27, 27, 27, 25, 4, 
+	25, 23, 21, 21, 27, 27, 27, 27, 
+	28, 27, 30, 27, 27, 27, 27, 27, 
+	27, 27, 27, 27
+};
+
+static const short _myanmar_syllable_machine_index_offsets[] = {
+	0, 31, 60, 86, 91, 117, 141, 163, 
+	185, 213, 241, 269, 297, 314, 342, 370, 
+	398, 426, 454, 482, 510, 538, 566, 592, 
+	597, 623, 647, 669, 691, 719, 747, 775, 
+	803, 832, 860, 891, 919, 947, 975, 1003, 
+	1031, 1059, 1087, 1115
+};
+
+static const char _myanmar_syllable_machine_indicies[] = {
+	1, 1, 2, 3, 4, 4, 0, 5, 
+	0, 6, 0, 1, 0, 0, 0, 7, 
+	0, 8, 1, 0, 9, 10, 11, 12, 
+	13, 14, 15, 16, 17, 18, 0, 20, 
+	21, 22, 22, 19, 23, 19, 24, 19, 
+	19, 19, 19, 19, 19, 19, 25, 19, 
+	19, 26, 27, 28, 29, 30, 31, 32, 
+	33, 34, 35, 19, 22, 22, 19, 23, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	19, 36, 19, 19, 19, 19, 19, 19, 
+	30, 19, 19, 19, 34, 19, 22, 22, 
+	19, 23, 19, 22, 22, 19, 23, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	19, 19, 19, 19, 19, 19, 19, 30, 
+	19, 19, 19, 34, 19, 37, 19, 22, 
+	22, 19, 23, 19, 30, 19, 19, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	19, 19, 19, 30, 19, 22, 22, 19, 
+	23, 19, 19, 19, 19, 19, 19, 19, 
+	19, 19, 38, 19, 19, 19, 19, 19, 
+	19, 30, 19, 22, 22, 19, 23, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	19, 19, 19, 19, 19, 19, 19, 30, 
+	19, 20, 19, 22, 22, 19, 23, 19, 
+	24, 19, 19, 19, 19, 19, 19, 19, 
+	39, 19, 19, 39, 19, 19, 19, 30, 
+	40, 19, 19, 34, 19, 20, 19, 22, 
+	22, 19, 23, 19, 24, 19, 19, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	19, 19, 19, 30, 19, 19, 19, 34, 
+	19, 20, 19, 22, 22, 19, 23, 19, 
+	24, 19, 19, 19, 19, 19, 19, 19, 
+	39, 19, 19, 19, 19, 19, 19, 30, 
+	40, 19, 19, 34, 19, 20, 19, 22, 
+	22, 19, 23, 19, 24, 19, 19, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	19, 19, 19, 30, 40, 19, 19, 34, 
+	19, 1, 1, 19, 19, 19, 19, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	1, 19, 20, 19, 22, 22, 19, 23, 
+	19, 24, 19, 19, 19, 19, 19, 19, 
+	19, 25, 19, 19, 26, 27, 28, 29, 
+	30, 31, 32, 33, 34, 19, 20, 19, 
+	22, 22, 19, 23, 19, 24, 19, 19, 
+	19, 19, 19, 19, 19, 33, 19, 19, 
+	19, 19, 19, 19, 30, 31, 32, 33, 
+	34, 19, 20, 19, 22, 22, 19, 23, 
+	19, 24, 19, 19, 19, 19, 19, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	30, 31, 32, 33, 34, 19, 20, 19, 
+	22, 22, 19, 23, 19, 24, 19, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	19, 19, 19, 19, 30, 31, 32, 19, 
+	34, 19, 20, 19, 22, 22, 19, 23, 
+	19, 24, 19, 19, 19, 19, 19, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	30, 19, 32, 19, 34, 19, 20, 19, 
+	22, 22, 19, 23, 19, 24, 19, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	26, 19, 28, 19, 30, 31, 32, 33, 
+	34, 19, 20, 19, 22, 22, 19, 23, 
+	19, 24, 19, 19, 19, 19, 19, 19, 
+	19, 33, 19, 19, 26, 19, 19, 19, 
+	30, 31, 32, 33, 34, 19, 20, 19, 
+	22, 22, 19, 23, 19, 24, 19, 19, 
+	19, 19, 19, 19, 19, 19, 19, 19, 
+	26, 27, 28, 19, 30, 31, 32, 33, 
+	34, 19, 20, 21, 22, 22, 19, 23, 
+	19, 24, 19, 19, 19, 19, 19, 19, 
+	19, 25, 19, 19, 26, 27, 28, 29, 
+	30, 31, 32, 33, 34, 19, 3, 3, 
+	41, 5, 41, 41, 41, 41, 41, 41, 
+	41, 41, 41, 42, 41, 41, 41, 41, 
+	41, 41, 13, 41, 41, 41, 17, 41, 
+	3, 3, 41, 5, 41, 3, 3, 41, 
+	5, 41, 41, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 13, 41, 41, 41, 17, 41, 43, 
+	41, 3, 3, 41, 5, 41, 13, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 41, 13, 41, 3, 
+	3, 41, 5, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 44, 41, 41, 41, 
+	41, 41, 41, 13, 41, 3, 3, 41, 
+	5, 41, 41, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 13, 41, 2, 41, 3, 3, 41, 
+	5, 41, 6, 41, 41, 41, 41, 41, 
+	41, 41, 45, 41, 41, 45, 41, 41, 
+	41, 13, 46, 41, 41, 17, 41, 2, 
+	41, 3, 3, 41, 5, 41, 6, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 41, 13, 41, 41, 
+	41, 17, 41, 2, 41, 3, 3, 41, 
+	5, 41, 6, 41, 41, 41, 41, 41, 
+	41, 41, 45, 41, 41, 41, 41, 41, 
+	41, 13, 46, 41, 41, 17, 41, 2, 
+	41, 3, 3, 41, 5, 41, 6, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 41, 13, 46, 41, 
+	41, 17, 41, 20, 21, 22, 22, 19, 
+	23, 19, 24, 19, 19, 19, 19, 19, 
+	19, 19, 47, 19, 19, 26, 27, 28, 
+	29, 30, 31, 32, 33, 34, 35, 19, 
+	20, 48, 22, 22, 19, 23, 19, 24, 
+	19, 19, 19, 19, 19, 19, 19, 25, 
+	19, 19, 26, 27, 28, 29, 30, 31, 
+	32, 33, 34, 19, 1, 1, 2, 3, 
+	3, 3, 41, 5, 41, 6, 41, 1, 
+	41, 41, 41, 1, 41, 8, 1, 41, 
+	9, 10, 11, 12, 13, 14, 15, 16, 
+	17, 18, 41, 2, 41, 3, 3, 41, 
+	5, 41, 6, 41, 41, 41, 41, 41, 
+	41, 41, 8, 41, 41, 9, 10, 11, 
+	12, 13, 14, 15, 16, 17, 41, 2, 
+	41, 3, 3, 41, 5, 41, 6, 41, 
+	41, 41, 41, 41, 41, 41, 16, 41, 
+	41, 41, 41, 41, 41, 13, 14, 15, 
+	16, 17, 41, 2, 41, 3, 3, 41, 
+	5, 41, 6, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 13, 14, 15, 16, 17, 41, 2, 
+	41, 3, 3, 41, 5, 41, 6, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 41, 13, 14, 15, 
+	41, 17, 41, 2, 41, 3, 3, 41, 
+	5, 41, 6, 41, 41, 41, 41, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 13, 41, 15, 41, 17, 41, 2, 
+	41, 3, 3, 41, 5, 41, 6, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 9, 41, 11, 41, 13, 14, 15, 
+	16, 17, 41, 2, 41, 3, 3, 41, 
+	5, 41, 6, 41, 41, 41, 41, 41, 
+	41, 41, 16, 41, 41, 9, 41, 41, 
+	41, 13, 14, 15, 16, 17, 41, 2, 
+	41, 3, 3, 41, 5, 41, 6, 41, 
+	41, 41, 41, 41, 41, 41, 41, 41, 
+	41, 9, 10, 11, 41, 13, 14, 15, 
+	16, 17, 41, 2, 3, 3, 3, 41, 
+	5, 41, 6, 41, 41, 41, 41, 41, 
+	41, 41, 8, 41, 41, 9, 10, 11, 
+	12, 13, 14, 15, 16, 17, 41, 0
+};
+
+static const char _myanmar_syllable_machine_trans_targs[] = {
+	0, 1, 22, 0, 0, 23, 29, 32, 
+	35, 36, 40, 41, 42, 25, 38, 39, 
+	37, 28, 43, 0, 2, 12, 0, 3, 
+	9, 13, 14, 18, 19, 20, 5, 16, 
+	17, 15, 8, 21, 4, 6, 7, 10, 
+	11, 0, 24, 26, 27, 30, 31, 33, 
+	34
+};
+
+static const char _myanmar_syllable_machine_trans_actions[] = {
+	3, 0, 0, 4, 5, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 6, 0, 0, 7, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 8, 0, 0, 0, 0, 0, 0, 
+	0
+};
+
+static const char _myanmar_syllable_machine_to_state_actions[] = {
+	1, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0
+};
+
+static const char _myanmar_syllable_machine_from_state_actions[] = {
+	2, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0, 0, 0, 0, 0, 
+	0, 0, 0, 0
+};
+
+static const short _myanmar_syllable_machine_eof_trans[] = {
+	0, 20, 20, 20, 20, 20, 20, 20, 
+	20, 20, 20, 20, 20, 20, 20, 20, 
+	20, 20, 20, 20, 20, 20, 42, 42, 
+	42, 42, 42, 42, 42, 42, 42, 42, 
+	20, 20, 42, 42, 42, 42, 42, 42, 
+	42, 42, 42, 42
+};
+
+static const int myanmar_syllable_machine_start = 0;
+static const int myanmar_syllable_machine_first_final = 0;
+static const int myanmar_syllable_machine_error = -1;
+
+static const int myanmar_syllable_machine_en_main = 0;
+
+
+#line 36 "hb-ot-shape-complex-myanmar-machine.rl"
+
+
+
+#line 90 "hb-ot-shape-complex-myanmar-machine.rl"
+
+
+#define found_syllable(syllable_type) \
+  HB_STMT_START { \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
+    for (unsigned int i = last; i < p+1; i++) \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+static void
+find_syllables (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  
+#line 288 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
+	{
+	cs = myanmar_syllable_machine_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 111 "hb-ot-shape-complex-myanmar-machine.rl"
+
+
+  p = 0;
+  pe = eof = buffer->len;
+
+  unsigned int last = 0;
+  unsigned int syllable_serial = 1;
+  
+#line 305 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+_resume:
+	switch ( _myanmar_syllable_machine_from_state_actions[cs] ) {
+	case 2:
+#line 1 "NONE"
+	{ts = p;}
+	break;
+#line 319 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
+	}
+
+	_keys = _myanmar_syllable_machine_trans_keys + (cs<<1);
+	_inds = _myanmar_syllable_machine_indicies + _myanmar_syllable_machine_index_offsets[cs];
+
+	_slen = _myanmar_syllable_machine_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].myanmar_category()) &&
+		( info[p].myanmar_category()) <= _keys[1] ?
+		( info[p].myanmar_category()) - _keys[0] : _slen ];
+
+_eof_trans:
+	cs = _myanmar_syllable_machine_trans_targs[_trans];
+
+	if ( _myanmar_syllable_machine_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _myanmar_syllable_machine_trans_actions[_trans] ) {
+	case 7:
+#line 83 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (consonant_syllable); }}
+	break;
+	case 5:
+#line 84 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (non_myanmar_cluster); }}
+	break;
+	case 4:
+#line 85 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (broken_cluster); }}
+	break;
+	case 3:
+#line 86 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p+1;{ found_syllable (non_myanmar_cluster); }}
+	break;
+	case 6:
+#line 83 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p;p--;{ found_syllable (consonant_syllable); }}
+	break;
+	case 8:
+#line 85 "hb-ot-shape-complex-myanmar-machine.rl"
+	{te = p;p--;{ found_syllable (broken_cluster); }}
+	break;
+#line 361 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
+	}
+
+_again:
+	switch ( _myanmar_syllable_machine_to_state_actions[cs] ) {
+	case 1:
+#line 1 "NONE"
+	{ts = 0;}
+	break;
+#line 370 "hb-ot-shape-complex-myanmar-machine.hh.tmp"
+	}
+
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	if ( _myanmar_syllable_machine_eof_trans[cs] > 0 ) {
+		_trans = _myanmar_syllable_machine_eof_trans[cs] - 1;
+		goto _eof_trans;
+	}
+	}
+
+	}
+
+#line 120 "hb-ot-shape-complex-myanmar-machine.rl"
+
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH */
diff --git a/src/hb-ot-shape-complex-myanmar-machine.rl b/src/hb-ot-shape-complex-myanmar-machine.rl
new file mode 100644
index 0000000..51d42dd
--- /dev/null
+++ b/src/hb-ot-shape-complex-myanmar-machine.rl
@@ -0,0 +1,125 @@
+/*
+ * 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_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH
+
+#include "hb-private.hh"
+
+%%{
+  machine myanmar_syllable_machine;
+  alphtype unsigned char;
+  write data;
+}%%
+
+%%{
+
+# Same order as enum myanmar_category_t.  Not sure how to avoid duplication.
+A    = 10;
+As   = 18;
+C    = 1;
+D    = 19;
+D0   = 20;
+DB   = 3;
+GB   = 12;
+H    = 4;
+IV   = 2;
+MH   = 21;
+MR   = 22;
+MW   = 23;
+MY   = 24;
+PT   = 25;
+V    = 8;
+VAbv = 26;
+VBlw = 27;
+VPre = 28;
+VPst = 29;
+VS   = 30;
+ZWJ  = 6;
+ZWNJ = 5;
+Ra   = 16;
+
+j = ZWJ|ZWNJ;			# Joiners
+k = (Ra As H);			# Kinzi
+
+c = C|Ra;			# is_consonant
+
+medial_group = MY? MR? ((MW MH? | MH) As?)?;
+main_vowel_group = VPre* VAbv* VBlw* A* (DB As?)?;
+post_vowel_group = VPst MH? As* VAbv* A* (DB As?)?;
+pwo_tone_group = PT A* (DB As?)?;
+
+complex_syllable_tail = As* medial_group main_vowel_group post_vowel_group* pwo_tone_group* V* j?;
+syllable_tail = (H | complex_syllable_tail);
+
+consonant_syllable =	k? (c|IV|D|GB).VS? (H (c|IV).VS?)* syllable_tail;
+broken_cluster =	k? VS? syllable_tail;
+other =			any;
+
+main := |*
+	consonant_syllable	=> { found_syllable (consonant_syllable); };
+	j			=> { found_syllable (non_myanmar_cluster); };
+	broken_cluster		=> { found_syllable (broken_cluster); };
+	other			=> { found_syllable (non_myanmar_cluster); };
+*|;
+
+
+}%%
+
+#define found_syllable(syllable_type) \
+  HB_STMT_START { \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
+    for (unsigned int i = last; i < p+1; i++) \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+static void
+find_syllables (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  %%{
+    write init;
+    getkey info[p].myanmar_category();
+  }%%
+
+  p = 0;
+  pe = eof = buffer->len;
+
+  unsigned int last = 0;
+  unsigned int syllable_serial = 1;
+  %%{
+    write exec;
+  }%%
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPE_COMPLEX_MYANMAR_MACHINE_HH */
diff --git a/src/hb-ot-shape-complex-myanmar.cc b/src/hb-ot-shape-complex-myanmar.cc
new file mode 100644
index 0000000..ff13bdd
--- /dev/null
+++ b/src/hb-ot-shape-complex-myanmar.cc
@@ -0,0 +1,545 @@
+/*
+ * Copyright © 2011,2012,2013  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
+ */
+
+#include "hb-ot-shape-complex-indic-private.hh"
+
+/* buffer var allocations */
+#define myanmar_category() complex_var_u8_0() /* myanmar_category_t */
+#define myanmar_position() complex_var_u8_1() /* myanmar_position_t */
+
+
+/*
+ * Myanmar shaper.
+ */
+
+static const hb_tag_t
+basic_features[] =
+{
+  /*
+   * Basic features.
+   * These features are applied in order, one at a time, after initial_reordering.
+   */
+  HB_TAG('r','p','h','f'),
+  HB_TAG('p','r','e','f'),
+  HB_TAG('b','l','w','f'),
+  HB_TAG('p','s','t','f'),
+};
+static const hb_tag_t
+other_features[] =
+{
+  /*
+   * Other features.
+   * These features are applied all at once, after final_reordering.
+   */
+  HB_TAG('p','r','e','s'),
+  HB_TAG('a','b','v','s'),
+  HB_TAG('b','l','w','s'),
+  HB_TAG('p','s','t','s'),
+  /* Positioning features, though we don't care about the types. */
+  HB_TAG('d','i','s','t'),
+};
+
+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_myanmar (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_global_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_global_bool_feature (HB_TAG('c','c','m','p'));
+
+
+  map->add_gsub_pause (initial_reordering);
+  for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++)
+  {
+    map->add_feature (basic_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
+    map->add_gsub_pause (NULL);
+  }
+  map->add_gsub_pause (final_reordering);
+  for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
+    map->add_feature (other_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
+}
+
+static void
+override_features_myanmar (hb_ot_shape_planner_t *plan)
+{
+  plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL);
+
+  /*
+   * Note:
+   *
+   * Spec says 'mark' is used, and the mmrtext.ttf font from
+   * Windows 8 has lookups for it.  But testing suggests that
+   * Windows 8 Uniscribe is NOT applying it.  It *is* applying
+   * 'mkmk' however.
+   */
+  if (hb_options ().uniscribe_bug_compatible)
+    plan->map.add_feature (HB_TAG('m','a','r','k'), 0, F_GLOBAL);
+}
+
+
+enum syllable_type_t {
+  consonant_syllable,
+  broken_cluster,
+  non_myanmar_cluster,
+};
+
+#include "hb-ot-shape-complex-myanmar-machine.hh"
+
+
+/* Note: This enum is duplicated in the -machine.rl source file.
+ * Not sure how to avoid duplication. */
+enum myanmar_category_t {
+  OT_As  = 18, /* Asat */
+  OT_D   = 19, /* Digits except zero */
+  OT_D0  = 20, /* Digit zero */
+  OT_DB  = OT_N, /* Dot below */
+  OT_GB  = OT_DOTTEDCIRCLE,
+  OT_MH  = 21, /* Various consonant medial types */
+  OT_MR  = 22, /* Various consonant medial types */
+  OT_MW  = 23, /* Various consonant medial types */
+  OT_MY  = 24, /* Various consonant medial types */
+  OT_PT  = 25, /* Pwo and other tones */
+  OT_VAbv = 26,
+  OT_VBlw = 27,
+  OT_VPre = 28,
+  OT_VPst = 29,
+  OT_VS   = 30 /* Variation selectors */
+};
+
+
+static inline bool
+is_one_of (const hb_glyph_info_t &info, unsigned int flags)
+{
+  /* If it ligated, all bets are off. */
+  if (is_a_ligature (info)) return false;
+  return !!(FLAG (info.myanmar_category()) & flags);
+}
+
+/* Note:
+ *
+ * We treat Vowels and placeholders as if they were consonants.  This is safe because Vowels
+ * cannot happen in a consonant syllable.  The plus side however is, we can call the
+ * consonant syllable logic from the vowel syllable function and get it all right! */
+#define CONSONANT_FLAGS (FLAG (OT_C) | FLAG (OT_CM) | FLAG (OT_Ra) | FLAG (OT_V) | FLAG (OT_NBSP) | FLAG (OT_GB))
+static inline bool
+is_consonant (const hb_glyph_info_t &info)
+{
+  return is_one_of (info, CONSONANT_FLAGS);
+}
+
+
+static inline void
+set_myanmar_properties (hb_glyph_info_t &info)
+{
+  hb_codepoint_t u = info.codepoint;
+  unsigned int type = hb_indic_get_categories (u);
+  indic_category_t cat = (indic_category_t) (type & 0x7F);
+  indic_position_t pos = (indic_position_t) (type >> 8);
+
+  /* Myanmar
+   * http://www.microsoft.com/typography/OpenTypeDev/myanmar/intro.htm#analyze
+   */
+  if (unlikely (hb_in_range<hb_codepoint_t> (u, 0xFE00, 0xFE0F)))
+    cat = (indic_category_t) OT_VS;
+  else if (unlikely (u == 0x200C)) cat = (indic_category_t) OT_ZWNJ;
+  else if (unlikely (u == 0x200D)) cat = (indic_category_t) OT_ZWJ;
+
+  switch (u)
+  {
+    case 0x002D: case 0x00A0: case 0x00D7: case 0x2012:
+    case 0x2013: case 0x2014: case 0x2015: case 0x2022:
+    case 0x25CC: case 0x25FB: case 0x25FC: case 0x25FD:
+    case 0x25FE:
+      cat = (indic_category_t) OT_GB;
+      break;
+
+    case 0x1004: case 0x101B: case 0x105A:
+      cat = (indic_category_t) OT_Ra;
+      break;
+
+    case 0x1032: case 0x1036:
+      cat = (indic_category_t) OT_A;
+      break;
+
+    case 0x103A:
+      cat = (indic_category_t) OT_As;
+      break;
+
+    case 0x1041: case 0x1042: case 0x1043: case 0x1044:
+    case 0x1045: case 0x1046: case 0x1047: case 0x1048:
+    case 0x1049: case 0x1090: case 0x1091: case 0x1092:
+    case 0x1093: case 0x1094: case 0x1095: case 0x1096:
+    case 0x1097: case 0x1098: case 0x1099:
+      cat = (indic_category_t) OT_D;
+      break;
+
+    case 0x1040:
+      cat = (indic_category_t) OT_D; /* XXX The spec says D0, but Uniscribe doesn't seem to do. */
+      break;
+
+    case 0x103E: case 0x1060:
+      cat = (indic_category_t) OT_MH;
+      break;
+
+    case 0x103C:
+      cat = (indic_category_t) OT_MR;
+      break;
+
+    case 0x103D: case 0x1082:
+      cat = (indic_category_t) OT_MW;
+      break;
+
+    case 0x103B: case 0x105E: case 0x105F:
+      cat = (indic_category_t) OT_MY;
+      break;
+
+    case 0x1063: case 0x1064: case 0x1069: case 0x106A:
+    case 0x106B: case 0x106C: case 0x106D: case 0xAA7B:
+      cat = (indic_category_t) OT_PT;
+      break;
+
+    case 0x1038: case 0x1087: case 0x1088: case 0x1089:
+    case 0x108A: case 0x108B: case 0x108C: case 0x108D:
+    case 0x108F: case 0x109A: case 0x109B: case 0x109C:
+      cat = (indic_category_t) OT_SM;
+      break;
+  }
+
+  if (cat == OT_M)
+  {
+    switch ((int) pos)
+    {
+      case POS_PRE_C:	cat = (indic_category_t) OT_VPre;
+			pos = POS_PRE_M;                  break;
+      case POS_ABOVE_C:	cat = (indic_category_t) OT_VAbv; break;
+      case POS_BELOW_C:	cat = (indic_category_t) OT_VBlw; break;
+      case POS_POST_C:	cat = (indic_category_t) OT_VPst; break;
+    }
+  }
+
+  info.myanmar_category() = (myanmar_category_t) cat;
+  info.myanmar_position() = pos;
+}
+
+
+
+static void
+setup_masks_myanmar (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		   hb_buffer_t              *buffer,
+		   hb_font_t                *font HB_UNUSED)
+{
+  HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_category);
+  HB_BUFFER_ALLOCATE_VAR (buffer, myanmar_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_myanmar_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_myanmar_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
+{
+  int a = pa->myanmar_position();
+  int b = pb->myanmar_position();
+
+  return a < b ? -1 : a == b ? 0 : +1;
+}
+
+
+/* Rules from:
+ * http://www.microsoft.com/typography/OpenTypeDev/myanmar/intro.htm */
+
+static void
+initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
+				       hb_face_t *face,
+				       hb_buffer_t *buffer,
+				       unsigned int start, unsigned int end)
+{
+  hb_glyph_info_t *info = buffer->info;
+
+  unsigned int base = end;
+  bool has_reph = false;
+
+  {
+    unsigned int limit = start;
+    if (start + 3 <= end &&
+	info[start  ].myanmar_category() == OT_Ra &&
+	info[start+1].myanmar_category() == OT_As &&
+	info[start+2].myanmar_category() == OT_H)
+    {
+      limit += 3;
+      base = start;
+      has_reph = true;
+    }
+
+    {
+      if (!has_reph)
+	base = limit;
+
+      for (unsigned int i = limit; i < end; i++)
+	if (is_consonant (info[i]))
+	{
+	  base = i;
+	  break;
+	}
+    }
+  }
+
+  /* Reorder! */
+  {
+    unsigned int i = start;
+    for (; i < start + (has_reph ? 3 : 0); i++)
+      info[i].myanmar_position() = POS_AFTER_MAIN;
+    for (; i < base; i++)
+      info[i].myanmar_position() = POS_PRE_C;
+    if (i < end)
+    {
+      info[i].myanmar_position() = POS_BASE_C;
+      i++;
+    }
+    indic_position_t pos = POS_AFTER_MAIN;
+    /* The following loop may be ugly, but it implements all of
+     * Myanmar reordering! */
+    for (; i < end; i++)
+    {
+      if (info[i].myanmar_category() == OT_MR) /* Pre-base reordering */
+      {
+	info[i].myanmar_position() = POS_PRE_C;
+	continue;
+      }
+      if (info[i].myanmar_position() < POS_BASE_C) /* Left matra */
+      {
+	continue;
+      }
+
+      if (pos == POS_AFTER_MAIN && info[i].myanmar_category() == OT_VBlw)
+      {
+	pos = POS_BELOW_C;
+	info[i].myanmar_position() = pos;
+	continue;
+      }
+
+      if (pos == POS_BELOW_C && info[i].myanmar_category() == OT_A)
+      {
+	info[i].myanmar_position() = POS_BEFORE_SUB;
+	continue;
+      }
+      if (pos == POS_BELOW_C && info[i].myanmar_category() == OT_VBlw)
+      {
+	info[i].myanmar_position() = pos;
+	continue;
+      }
+      if (pos == POS_BELOW_C && info[i].myanmar_category() != OT_A)
+      {
+        pos = POS_AFTER_SUB;
+	info[i].myanmar_position() = pos;
+	continue;
+      }
+      info[i].myanmar_position() = pos;
+    }
+  }
+
+  buffer->merge_clusters (start, end);
+  /* Sit tight, rock 'n roll! */
+  hb_bubble_sort (info + start, end - start, compare_myanmar_order);
+}
+
+static void
+initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan,
+				   hb_face_t *face,
+				   hb_buffer_t *buffer,
+				   unsigned int start, unsigned int end)
+{
+  /* We already inserted dotted-circles, so just call the consonant_syllable. */
+  initial_reordering_consonant_syllable (plan, face, buffer, start, end);
+}
+
+static void
+initial_reordering_non_myanmar_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
+					hb_face_t *face HB_UNUSED,
+					hb_buffer_t *buffer HB_UNUSED,
+					unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
+{
+  /* Nothing to do right now.  If we ever switch to using the output
+   * buffer in the reordering process, we'd need to next_glyph() here. */
+}
+
+
+static void
+initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
+			     hb_face_t *face,
+			     hb_buffer_t *buffer,
+			     unsigned int start, unsigned int end)
+{
+  syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
+  switch (syllable_type) {
+  case consonant_syllable:	initial_reordering_consonant_syllable  (plan, face, buffer, start, end); return;
+  case broken_cluster:		initial_reordering_broken_cluster      (plan, face, buffer, start, end); return;
+  case non_myanmar_cluster:	initial_reordering_non_myanmar_cluster (plan, face, buffer, start, end); return;
+  }
+}
+
+static inline void
+insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		       hb_font_t *font,
+		       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 = {0};
+  dottedcircle.codepoint = 0x25CC;
+  set_myanmar_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))
+    {
+      last_syllable = syllable;
+
+      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);
+    }
+    else
+      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)
+{
+  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, font->face, buffer, last, i);
+      last = i;
+      last_syllable = info[last].syllable();
+    }
+  initial_reordering_syllable (plan, font->face, buffer, last, count);
+}
+
+static void
+final_reordering (const hb_ot_shape_plan_t *plan,
+		  hb_font_t *font HB_UNUSED,
+		  hb_buffer_t *buffer)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+
+  /* Zero syllables now... */
+  for (unsigned int i = 0; i < count; i++)
+    info[i].syllable() = 0;
+
+  HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_category);
+  HB_BUFFER_DEALLOCATE_VAR (buffer, myanmar_position);
+}
+
+
+static hb_ot_shape_normalization_mode_t
+normalization_preference_myanmar (const hb_segment_properties_t *props HB_UNUSED)
+{
+  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
+}
+
+
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_myanmar =
+{
+  "myanmar",
+  collect_features_myanmar,
+  override_features_myanmar,
+  NULL, /* data_create */
+  NULL, /* data_destroy */
+  NULL, /* preprocess_text */
+  normalization_preference_myanmar,
+  NULL, /* decompose */
+  NULL, /* compose */
+  setup_masks_myanmar,
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF,
+  false, /* fallback_position */
+};
diff --git a/src/hb-ot-shape-complex-private.hh b/src/hb-ot-shape-complex-private.hh
index 26871c2..3c9922d 100644
--- a/src/hb-ot-shape-complex-private.hh
+++ b/src/hb-ot-shape-complex-private.hh
@@ -39,12 +39,20 @@
 #define complex_var_u8_1()	var2.u8[3]
 
 
+enum hb_ot_shape_zero_width_marks_type_t {
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE,
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF
+};
+
 
 /* Master OT shaper list */
 #define HB_COMPLEX_SHAPERS_IMPLEMENT_SHAPERS \
   HB_COMPLEX_SHAPER_IMPLEMENT (default) /* should be first */ \
   HB_COMPLEX_SHAPER_IMPLEMENT (arabic) \
   HB_COMPLEX_SHAPER_IMPLEMENT (indic) \
+  HB_COMPLEX_SHAPER_IMPLEMENT (myanmar) \
+  HB_COMPLEX_SHAPER_IMPLEMENT (sea) \
   HB_COMPLEX_SHAPER_IMPLEMENT (thai) \
   /* ^--- Add new shapers here */
 
@@ -130,7 +138,8 @@
 		       hb_buffer_t              *buffer,
 		       hb_font_t                *font);
 
-  bool zero_width_attached_marks;
+  hb_ot_shape_zero_width_marks_type_t zero_width_marks;
+
   bool fallback_position;
 };
 
@@ -254,13 +263,11 @@
 
     /* Unicode-4.1 additions */
     case HB_SCRIPT_BUGINESE:
-    case HB_SCRIPT_NEW_TAI_LUE:
 
     /* Unicode-5.0 additions */
     case HB_SCRIPT_BALINESE:
 
     /* Unicode-5.1 additions */
-    case HB_SCRIPT_CHAM:
     case HB_SCRIPT_LEPCHA:
     case HB_SCRIPT_REJANG:
     case HB_SCRIPT_SUNDANESE:
@@ -269,19 +276,22 @@
     case HB_SCRIPT_JAVANESE:
     case HB_SCRIPT_KAITHI:
     case HB_SCRIPT_MEETEI_MAYEK:
-    case HB_SCRIPT_TAI_THAM:
 
+    /* Unicode-6.0 additions */
 
     /* Unicode-6.1 additions */
     case HB_SCRIPT_CHAKMA:
     case HB_SCRIPT_SHARADA:
     case HB_SCRIPT_TAKRI:
 
-      /* Only use Indic shaper if the font has Indic tables. */
-      if (planner->map.found_script[0])
-	return &_hb_ot_complex_shaper_indic;
-      else
+      /* If the designer designed the font for the 'DFLT' script,
+       * use the default shaper.  Otherwise, use the Indic shaper.
+       * Note that for some simple scripts, there may not be *any*
+       * GSUB/GPOS needed, so there may be no scripts found! */
+      if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T'))
 	return &_hb_ot_complex_shaper_default;
+      else
+	return &_hb_ot_complex_shaper_indic;
 
     case HB_SCRIPT_KHMER:
       /* A number of Khmer fonts in the wild don't have a 'pref' feature,
@@ -300,12 +310,30 @@
 	return &_hb_ot_complex_shaper_default;
 
     case HB_SCRIPT_MYANMAR:
-      /* For Myanmar, we only want to use the Indic shaper if the "new" script
+      /* For Myanmar, we only want to use the Myanmar shaper if the "new" script
        * tag is found.  For "old" script tag we want to use the default shaper. */
       if (planner->map.chosen_script[0] == HB_TAG ('m','y','m','2'))
-	return &_hb_ot_complex_shaper_indic;
+	return &_hb_ot_complex_shaper_myanmar;
       else
 	return &_hb_ot_complex_shaper_default;
+
+    /* Unicode-4.1 additions */
+    case HB_SCRIPT_NEW_TAI_LUE:
+
+    /* Unicode-5.1 additions */
+    case HB_SCRIPT_CHAM:
+
+    /* Unicode-5.2 additions */
+    case HB_SCRIPT_TAI_THAM:
+
+      /* If the designer designed the font for the 'DFLT' script,
+       * use the default shaper.  Otherwise, use the Indic shaper.
+       * Note that for some simple scripts, there may not be *any*
+       * GSUB/GPOS needed, so there may be no scripts found! */
+      if (planner->map.chosen_script[0] == HB_TAG ('D','F','L','T'))
+	return &_hb_ot_complex_shaper_default;
+      else
+	return &_hb_ot_complex_shaper_sea;
   }
 }
 
diff --git a/src/hb-ot-shape-complex-sea-machine.hh b/src/hb-ot-shape-complex-sea-machine.hh
new file mode 100644
index 0000000..dace050
--- /dev/null
+++ b/src/hb-ot-shape-complex-sea-machine.hh
@@ -0,0 +1,224 @@
+
+#line 1 "hb-ot-shape-complex-sea-machine.rl"
+/*
+ * Copyright © 2011,2012,2013  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_SEA_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH
+
+#include "hb-private.hh"
+
+
+#line 36 "hb-ot-shape-complex-sea-machine.hh.tmp"
+static const unsigned char _sea_syllable_machine_trans_keys[] = {
+	1u, 1u, 1u, 1u, 1u, 29u, 3u, 29u, 3u, 29u, 1u, 1u, 0
+};
+
+static const char _sea_syllable_machine_key_spans[] = {
+	1, 1, 29, 27, 27, 1
+};
+
+static const char _sea_syllable_machine_index_offsets[] = {
+	0, 2, 4, 34, 62, 90
+};
+
+static const char _sea_syllable_machine_indicies[] = {
+	1, 0, 3, 2, 1, 1, 3, 5, 
+	4, 4, 4, 4, 4, 3, 4, 1, 
+	4, 4, 4, 4, 3, 4, 4, 4, 
+	4, 3, 4, 4, 4, 3, 3, 3, 
+	3, 4, 1, 7, 6, 6, 6, 6, 
+	6, 1, 6, 6, 6, 6, 6, 6, 
+	1, 6, 6, 6, 6, 1, 6, 6, 
+	6, 1, 1, 1, 1, 6, 3, 9, 
+	8, 8, 8, 8, 8, 3, 8, 8, 
+	8, 8, 8, 8, 3, 8, 8, 8, 
+	8, 3, 8, 8, 8, 3, 3, 3, 
+	3, 8, 3, 10, 0
+};
+
+static const char _sea_syllable_machine_trans_targs[] = {
+	2, 3, 2, 4, 2, 5, 2, 0, 
+	2, 1, 2
+};
+
+static const char _sea_syllable_machine_trans_actions[] = {
+	1, 2, 3, 2, 6, 0, 7, 0, 
+	8, 0, 9
+};
+
+static const char _sea_syllable_machine_to_state_actions[] = {
+	0, 0, 4, 0, 0, 0
+};
+
+static const char _sea_syllable_machine_from_state_actions[] = {
+	0, 0, 5, 0, 0, 0
+};
+
+static const char _sea_syllable_machine_eof_trans[] = {
+	1, 3, 0, 7, 9, 11
+};
+
+static const int sea_syllable_machine_start = 2;
+static const int sea_syllable_machine_first_final = 2;
+static const int sea_syllable_machine_error = -1;
+
+static const int sea_syllable_machine_en_main = 2;
+
+
+#line 36 "hb-ot-shape-complex-sea-machine.rl"
+
+
+
+#line 67 "hb-ot-shape-complex-sea-machine.rl"
+
+
+#define found_syllable(syllable_type) \
+  HB_STMT_START { \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
+    for (unsigned int i = last; i < p+1; i++) \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+static void
+find_syllables (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  
+#line 117 "hb-ot-shape-complex-sea-machine.hh.tmp"
+	{
+	cs = sea_syllable_machine_start;
+	ts = 0;
+	te = 0;
+	act = 0;
+	}
+
+#line 88 "hb-ot-shape-complex-sea-machine.rl"
+
+
+  p = 0;
+  pe = eof = buffer->len;
+
+  unsigned int last = 0;
+  unsigned int syllable_serial = 1;
+  
+#line 134 "hb-ot-shape-complex-sea-machine.hh.tmp"
+	{
+	int _slen;
+	int _trans;
+	const unsigned char *_keys;
+	const char *_inds;
+	if ( p == pe )
+		goto _test_eof;
+_resume:
+	switch ( _sea_syllable_machine_from_state_actions[cs] ) {
+	case 5:
+#line 1 "NONE"
+	{ts = p;}
+	break;
+#line 148 "hb-ot-shape-complex-sea-machine.hh.tmp"
+	}
+
+	_keys = _sea_syllable_machine_trans_keys + (cs<<1);
+	_inds = _sea_syllable_machine_indicies + _sea_syllable_machine_index_offsets[cs];
+
+	_slen = _sea_syllable_machine_key_spans[cs];
+	_trans = _inds[ _slen > 0 && _keys[0] <=( info[p].sea_category()) &&
+		( info[p].sea_category()) <= _keys[1] ?
+		( info[p].sea_category()) - _keys[0] : _slen ];
+
+_eof_trans:
+	cs = _sea_syllable_machine_trans_targs[_trans];
+
+	if ( _sea_syllable_machine_trans_actions[_trans] == 0 )
+		goto _again;
+
+	switch ( _sea_syllable_machine_trans_actions[_trans] ) {
+	case 2:
+#line 1 "NONE"
+	{te = p+1;}
+	break;
+	case 6:
+#line 63 "hb-ot-shape-complex-sea-machine.rl"
+	{te = p+1;{ found_syllable (non_sea_cluster); }}
+	break;
+	case 7:
+#line 61 "hb-ot-shape-complex-sea-machine.rl"
+	{te = p;p--;{ found_syllable (consonant_syllable); }}
+	break;
+	case 8:
+#line 62 "hb-ot-shape-complex-sea-machine.rl"
+	{te = p;p--;{ found_syllable (broken_cluster); }}
+	break;
+	case 9:
+#line 63 "hb-ot-shape-complex-sea-machine.rl"
+	{te = p;p--;{ found_syllable (non_sea_cluster); }}
+	break;
+	case 1:
+#line 61 "hb-ot-shape-complex-sea-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (consonant_syllable); }}
+	break;
+	case 3:
+#line 62 "hb-ot-shape-complex-sea-machine.rl"
+	{{p = ((te))-1;}{ found_syllable (broken_cluster); }}
+	break;
+#line 194 "hb-ot-shape-complex-sea-machine.hh.tmp"
+	}
+
+_again:
+	switch ( _sea_syllable_machine_to_state_actions[cs] ) {
+	case 4:
+#line 1 "NONE"
+	{ts = 0;}
+	break;
+#line 203 "hb-ot-shape-complex-sea-machine.hh.tmp"
+	}
+
+	if ( ++p != pe )
+		goto _resume;
+	_test_eof: {}
+	if ( p == eof )
+	{
+	if ( _sea_syllable_machine_eof_trans[cs] > 0 ) {
+		_trans = _sea_syllable_machine_eof_trans[cs] - 1;
+		goto _eof_trans;
+	}
+	}
+
+	}
+
+#line 97 "hb-ot-shape-complex-sea-machine.rl"
+
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH */
diff --git a/src/hb-ot-shape-complex-sea-machine.rl b/src/hb-ot-shape-complex-sea-machine.rl
new file mode 100644
index 0000000..46140fc
--- /dev/null
+++ b/src/hb-ot-shape-complex-sea-machine.rl
@@ -0,0 +1,102 @@
+/*
+ * Copyright © 2011,2012,2013  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_SEA_MACHINE_HH
+#define HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH
+
+#include "hb-private.hh"
+
+%%{
+  machine sea_syllable_machine;
+  alphtype unsigned char;
+  write data;
+}%%
+
+%%{
+
+# Same order as enum sea_category_t.  Not sure how to avoid duplication.
+C    = 1;
+GB   = 12; # Generic Base
+H    = 4;  # Halant
+IV   = 2;  # Independent Vowel
+MR   = 22; # Medial Ra
+CM   = 17; # Consonant Medial
+VAbv = 26;
+VBlw = 27;
+VPre = 28;
+VPst = 29;
+T    = 3;  # Tone Marks
+A    = 10; # Anusvara
+
+syllable_tail = (VPre|VAbv|VBlw|VPst|H.C|CM|MR|T|A)*;
+
+consonant_syllable =	(C|IV|GB) syllable_tail;
+broken_cluster =	syllable_tail;
+other =			any;
+
+main := |*
+	consonant_syllable	=> { found_syllable (consonant_syllable); };
+	broken_cluster		=> { found_syllable (broken_cluster); };
+	other			=> { found_syllable (non_sea_cluster); };
+*|;
+
+
+}%%
+
+#define found_syllable(syllable_type) \
+  HB_STMT_START { \
+    if (0) fprintf (stderr, "syllable %d..%d %s\n", last, p+1, #syllable_type); \
+    for (unsigned int i = last; i < p+1; i++) \
+      info[i].syllable() = (syllable_serial << 4) | syllable_type; \
+    last = p+1; \
+    syllable_serial++; \
+    if (unlikely (syllable_serial == 16)) syllable_serial = 1; \
+  } HB_STMT_END
+
+static void
+find_syllables (hb_buffer_t *buffer)
+{
+  unsigned int p, pe, eof, ts HB_UNUSED, te HB_UNUSED, act HB_UNUSED;
+  int cs;
+  hb_glyph_info_t *info = buffer->info;
+  %%{
+    write init;
+    getkey info[p].sea_category();
+  }%%
+
+  p = 0;
+  pe = eof = buffer->len;
+
+  unsigned int last = 0;
+  unsigned int syllable_serial = 1;
+  %%{
+    write exec;
+  }%%
+}
+
+#undef found_syllable
+
+#endif /* HB_OT_SHAPE_COMPLEX_SEA_MACHINE_HH */
diff --git a/src/hb-ot-shape-complex-sea.cc b/src/hb-ot-shape-complex-sea.cc
new file mode 100644
index 0000000..9c0c303
--- /dev/null
+++ b/src/hb-ot-shape-complex-sea.cc
@@ -0,0 +1,384 @@
+/*
+ * Copyright © 2011,2012,2013  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
+ */
+
+#include "hb-ot-shape-complex-indic-private.hh"
+
+/* buffer var allocations */
+#define sea_category() complex_var_u8_0() /* indic_category_t */
+#define sea_position() complex_var_u8_1() /* indic_position_t */
+
+
+/*
+ * South-East Asian shaper.
+ * Loosely based on the Myanmar spec / shaper.
+ * There is no OpenType spec for this.
+ */
+
+static const hb_tag_t
+basic_features[] =
+{
+  /*
+   * Basic features.
+   * These features are applied in order, one at a time, after initial_reordering.
+   */
+  HB_TAG('p','r','e','f'),
+  HB_TAG('a','b','v','f'),
+  HB_TAG('b','l','w','f'),
+  HB_TAG('p','s','t','f'),
+};
+static const hb_tag_t
+other_features[] =
+{
+  /*
+   * Other features.
+   * These features are applied all at once, after final_reordering.
+   */
+  HB_TAG('p','r','e','s'),
+  HB_TAG('a','b','v','s'),
+  HB_TAG('b','l','w','s'),
+  HB_TAG('p','s','t','s'),
+  /* Positioning features, though we don't care about the types. */
+  HB_TAG('d','i','s','t'),
+};
+
+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_sea (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_global_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_global_bool_feature (HB_TAG('c','c','m','p'));
+
+  map->add_gsub_pause (initial_reordering);
+  for (unsigned int i = 0; i < ARRAY_LENGTH (basic_features); i++)
+  {
+    map->add_feature (basic_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
+    map->add_gsub_pause (NULL);
+  }
+  map->add_gsub_pause (final_reordering);
+  for (unsigned int i = 0; i < ARRAY_LENGTH (other_features); i++)
+    map->add_feature (other_features[i], 1, F_GLOBAL | F_MANUAL_ZWJ);
+}
+
+static void
+override_features_sea (hb_ot_shape_planner_t *plan)
+{
+  plan->map.add_feature (HB_TAG('l','i','g','a'), 0, F_GLOBAL);
+}
+
+
+enum syllable_type_t {
+  consonant_syllable,
+  broken_cluster,
+  non_sea_cluster,
+};
+
+#include "hb-ot-shape-complex-sea-machine.hh"
+
+
+/* Note: This enum is duplicated in the -machine.rl source file.
+ * Not sure how to avoid duplication. */
+enum sea_category_t {
+//  OT_C    = 1,
+  OT_GB   = 12, /* Generic Base XXX DOTTED CIRCLE only for now */
+//  OT_H    = 4,  /* Halant */
+  OT_IV   = 2,  /* Independent Vowel */
+  OT_MR   = 22, /* Medial Ra */
+//  OT_CM   = 17, /* Consonant Medial */
+  OT_VAbv = 26,
+  OT_VBlw = 27,
+  OT_VPre = 28,
+  OT_VPst = 29,
+  OT_T    = 3,  /* Tone Marks */
+//  OT_A    = 10, /* Anusvara */
+};
+
+static inline void
+set_sea_properties (hb_glyph_info_t &info)
+{
+  hb_codepoint_t u = info.codepoint;
+  unsigned int type = hb_indic_get_categories (u);
+  indic_category_t cat = (indic_category_t) (type & 0x7F);
+  indic_position_t pos = (indic_position_t) (type >> 8);
+
+  /* Medial Ra */
+  if (u == 0x1A55 || u == 0xAA34)
+    cat = (indic_category_t) OT_MR;
+
+  if (cat == OT_M)
+  {
+    switch ((int) pos)
+    {
+      case POS_PRE_C:	cat = (indic_category_t) OT_VPre; break;
+      case POS_ABOVE_C:	cat = (indic_category_t) OT_VAbv; break;
+      case POS_BELOW_C:	cat = (indic_category_t) OT_VBlw; break;
+      case POS_POST_C:	cat = (indic_category_t) OT_VPst; break;
+    }
+  }
+
+  info.sea_category() = (sea_category_t) cat;
+  info.sea_position() = pos;
+}
+
+
+static void
+setup_masks_sea (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		 hb_buffer_t              *buffer,
+		 hb_font_t                *font HB_UNUSED)
+{
+  HB_BUFFER_ALLOCATE_VAR (buffer, sea_category);
+  HB_BUFFER_ALLOCATE_VAR (buffer, sea_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_sea_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_sea_order (const hb_glyph_info_t *pa, const hb_glyph_info_t *pb)
+{
+  int a = pa->sea_position();
+  int b = pb->sea_position();
+
+  return a < b ? -1 : a == b ? 0 : +1;
+}
+
+
+static void
+initial_reordering_consonant_syllable (const hb_ot_shape_plan_t *plan,
+				       hb_face_t *face,
+				       hb_buffer_t *buffer,
+				       unsigned int start, unsigned int end)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int base = start;
+
+  /* Reorder! */
+  unsigned int i = start;
+  for (; i < base; i++)
+    info[i].sea_position() = POS_PRE_C;
+  if (i < end)
+  {
+    info[i].sea_position() = POS_BASE_C;
+    i++;
+  }
+  for (; i < end; i++)
+  {
+    if (info[i].sea_category() == OT_MR) /* Pre-base reordering */
+    {
+      info[i].sea_position() = POS_PRE_C;
+      continue;
+    }
+    if (info[i].sea_category() == OT_VPre) /* Left matra */
+    {
+      info[i].sea_position() = POS_PRE_M;
+      continue;
+    }
+
+    info[i].sea_position() = POS_AFTER_MAIN;
+  }
+
+  buffer->merge_clusters (start, end);
+  /* Sit tight, rock 'n roll! */
+  hb_bubble_sort (info + start, end - start, compare_sea_order);
+}
+
+static void
+initial_reordering_broken_cluster (const hb_ot_shape_plan_t *plan,
+				   hb_face_t *face,
+				   hb_buffer_t *buffer,
+				   unsigned int start, unsigned int end)
+{
+  /* We already inserted dotted-circles, so just call the consonant_syllable. */
+  initial_reordering_consonant_syllable (plan, face, buffer, start, end);
+}
+
+static void
+initial_reordering_non_sea_cluster (const hb_ot_shape_plan_t *plan HB_UNUSED,
+				    hb_face_t *face HB_UNUSED,
+				    hb_buffer_t *buffer HB_UNUSED,
+				    unsigned int start HB_UNUSED, unsigned int end HB_UNUSED)
+{
+  /* Nothing to do right now.  If we ever switch to using the output
+   * buffer in the reordering process, we'd need to next_glyph() here. */
+}
+
+
+static void
+initial_reordering_syllable (const hb_ot_shape_plan_t *plan,
+			     hb_face_t *face,
+			     hb_buffer_t *buffer,
+			     unsigned int start, unsigned int end)
+{
+  syllable_type_t syllable_type = (syllable_type_t) (buffer->info[start].syllable() & 0x0F);
+  switch (syllable_type) {
+  case consonant_syllable:	initial_reordering_consonant_syllable  (plan, face, buffer, start, end); return;
+  case broken_cluster:		initial_reordering_broken_cluster      (plan, face, buffer, start, end); return;
+  case non_sea_cluster:	initial_reordering_non_sea_cluster (plan, face, buffer, start, end); return;
+  }
+}
+
+static inline void
+insert_dotted_circles (const hb_ot_shape_plan_t *plan HB_UNUSED,
+		       hb_font_t *font,
+		       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 = {0};
+  dottedcircle.codepoint = 0x25CC;
+  set_sea_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))
+    {
+      last_syllable = syllable;
+
+      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);
+    }
+    else
+      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)
+{
+  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, font->face, buffer, last, i);
+      last = i;
+      last_syllable = info[last].syllable();
+    }
+  initial_reordering_syllable (plan, font->face, buffer, last, count);
+}
+
+static void
+final_reordering (const hb_ot_shape_plan_t *plan,
+		  hb_font_t *font HB_UNUSED,
+		  hb_buffer_t *buffer)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+
+  /* Zero syllables now... */
+  for (unsigned int i = 0; i < count; i++)
+    info[i].syllable() = 0;
+
+  HB_BUFFER_DEALLOCATE_VAR (buffer, sea_category);
+  HB_BUFFER_DEALLOCATE_VAR (buffer, sea_position);
+}
+
+
+static hb_ot_shape_normalization_mode_t
+normalization_preference_sea (const hb_segment_properties_t *props HB_UNUSED)
+{
+  return HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_DIACRITICS_NO_SHORT_CIRCUIT;
+}
+
+
+const hb_ot_complex_shaper_t _hb_ot_complex_shaper_sea =
+{
+  "sea",
+  collect_features_sea,
+  override_features_sea,
+  NULL, /* data_create */
+  NULL, /* data_destroy */
+  NULL, /* preprocess_text */
+  normalization_preference_sea,
+  NULL, /* decompose */
+  NULL, /* compose */
+  setup_masks_sea,
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE,
+  false, /* fallback_position */
+};
diff --git a/src/hb-ot-shape-complex-thai.cc b/src/hb-ot-shape-complex-thai.cc
index 24d476a..5cbb6e3 100644
--- a/src/hb-ot-shape-complex-thai.cc
+++ b/src/hb-ot-shape-complex-thai.cc
@@ -373,6 +373,6 @@
   NULL, /* decompose */
   NULL, /* compose */
   NULL, /* setup_masks */
-  true, /* zero_width_attached_marks */
+  HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE,
   false,/* fallback_position */
 };
diff --git a/src/hb-ot-shape-fallback-private.hh b/src/hb-ot-shape-fallback-private.hh
index 5e9cb06..ec65351 100644
--- a/src/hb-ot-shape-fallback-private.hh
+++ b/src/hb-ot-shape-fallback-private.hh
@@ -41,4 +41,9 @@
 								    hb_buffer_t  *buffer);
 
 
+HB_INTERNAL void _hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan,
+					     hb_font_t *font,
+					     hb_buffer_t  *buffer);
+
+
 #endif /* HB_OT_SHAPE_FALLBACK_PRIVATE_HH */
diff --git a/src/hb-ot-shape-fallback.cc b/src/hb-ot-shape-fallback.cc
index 6f3426e..3341825 100644
--- a/src/hb-ot-shape-fallback.cc
+++ b/src/hb-ot-shape-fallback.cc
@@ -25,6 +25,7 @@
  */
 
 #include "hb-ot-shape-fallback-private.hh"
+#include "hb-ot-layout-gsubgpos-private.hh"
 
 static unsigned int
 recategorize_combining_class (hb_codepoint_t u,
@@ -407,3 +408,48 @@
     }
   position_cluster (plan, font, buffer, start, count);
 }
+
+
+/* Performs old-style TrueType kerning. */
+void
+_hb_ot_shape_fallback_kern (const hb_ot_shape_plan_t *plan,
+			    hb_font_t *font,
+			    hb_buffer_t  *buffer)
+{
+  unsigned int count = buffer->len;
+  hb_mask_t kern_mask = plan->map.get_1_mask (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction) ?
+					      HB_TAG ('k','e','r','n') : HB_TAG ('v','k','r','n'));
+
+  OT::hb_apply_context_t c (1, font, buffer, kern_mask, true/*auto_zwj*/);
+  c.set_lookup_props (OT::LookupFlag::IgnoreMarks);
+
+  for (buffer->idx = 0; buffer->idx < count;)
+  {
+    OT::hb_apply_context_t::skipping_forward_iterator_t skippy_iter (&c, buffer->idx, 1);
+    if (!skippy_iter.next ())
+    {
+      buffer->idx++;
+      continue;
+    }
+
+    hb_position_t x_kern, y_kern, kern1, kern2;
+    font->get_glyph_kerning_for_direction (buffer->info[buffer->idx].codepoint,
+					   buffer->info[skippy_iter.idx].codepoint,
+					   buffer->props.direction,
+					   &x_kern, &y_kern);
+
+    kern1 = x_kern >> 1;
+    kern2 = x_kern - kern1;
+    buffer->pos[buffer->idx].x_advance += kern1;
+    buffer->pos[skippy_iter.idx].x_advance += kern2;
+    buffer->pos[skippy_iter.idx].x_offset += kern2;
+
+    kern1 = y_kern >> 1;
+    kern2 = y_kern - kern1;
+    buffer->pos[buffer->idx].y_advance += kern1;
+    buffer->pos[skippy_iter.idx].y_advance += kern2;
+    buffer->pos[skippy_iter.idx].y_offset += kern2;
+
+    buffer->idx = skippy_iter.idx;
+  }
+}
diff --git a/src/hb-ot-shape-normalize.cc b/src/hb-ot-shape-normalize.cc
index c5325e4..344c0ff 100644
--- a/src/hb-ot-shape-normalize.cc
+++ b/src/hb-ot-shape-normalize.cc
@@ -192,30 +192,23 @@
 }
 
 /* Returns true if recomposition may be benefitial. */
-static inline bool
+static inline void
 decompose_current_character (const hb_ot_shape_normalize_context_t *c, bool shortest)
 {
   hb_buffer_t * const buffer = c->buffer;
   hb_codepoint_t glyph;
-  unsigned int len = 1;
 
   /* Kind of a cute waterfall here... */
   if (shortest && c->font->get_glyph (buffer->cur().codepoint, 0, &glyph))
     next_char (buffer, glyph);
-  else if ((len = decompose (c, shortest, buffer->cur().codepoint)))
+  else if (decompose (c, shortest, buffer->cur().codepoint))
     skip_char (buffer);
   else if (!shortest && c->font->get_glyph (buffer->cur().codepoint, 0, &glyph))
     next_char (buffer, glyph);
-  else if ((len = decompose_compatibility (c, buffer->cur().codepoint)))
+  else if (decompose_compatibility (c, buffer->cur().codepoint))
     skip_char (buffer);
   else
     next_char (buffer, glyph); /* glyph is initialized in earlier branches. */
-
-  /*
-   * A recomposition would only be useful if we decomposed into at least three
-   * characters...
-   */
-  return len > 2;
 }
 
 static inline void
@@ -239,7 +232,7 @@
 }
 
 /* Returns true if recomposition may be benefitial. */
-static inline bool
+static inline void
 decompose_multi_char_cluster (const hb_ot_shape_normalize_context_t *c, unsigned int end)
 {
   hb_buffer_t * const buffer = c->buffer;
@@ -247,23 +240,20 @@
   for (unsigned int i = buffer->idx; i < end; i++)
     if (unlikely (buffer->unicode->is_variation_selector (buffer->info[i].codepoint))) {
       handle_variation_selector_cluster (c, end);
-      return false;
+      return;
     }
 
   while (buffer->idx < end)
     decompose_current_character (c, 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
+static inline void
 decompose_cluster (const hb_ot_shape_normalize_context_t *c, bool short_circuit, unsigned int end)
 {
   if (likely (c->buffer->idx + 1 == end))
-    return decompose_current_character (c, short_circuit);
+    decompose_current_character (c, short_circuit);
   else
-    return decompose_multi_char_cluster (c, end);
+    decompose_multi_char_cluster (c, end);
 }
 
 
@@ -296,7 +286,6 @@
 
   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
@@ -317,15 +306,11 @@
       if (buffer->cur().cluster != buffer->info[end].cluster)
         break;
 
-    can_use_recompose = decompose_cluster (&c, short_circuit, end) || can_use_recompose;
+    decompose_cluster (&c, short_circuit, end);
   }
   buffer->swap_buffers ();
 
 
-  if (mode != HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL && !can_use_recompose)
-    return; /* Done! */
-
-
   /* Second round, reorder (inplace) */
 
   count = buffer->len;
@@ -369,9 +354,11 @@
   {
     hb_codepoint_t composed, glyph;
     if (/* If mode is NOT COMPOSED_FULL (ie. it's COMPOSED_DIACRITICS), we don't try to
-	 * compose a CCC=0 character with it's preceding starter. */
+	 * compose a non-mark character with it's preceding starter.  This is just an
+	 * optimization to avoid trying to compose every two neighboring glyphs in most
+	 * scripts. */
 	(mode == HB_OT_SHAPE_NORMALIZATION_MODE_COMPOSED_FULL ||
-	 _hb_glyph_info_get_modified_combining_class (&buffer->cur()) != 0) &&
+	 HB_UNICODE_GENERAL_CATEGORY_IS_MARK (_hb_glyph_info_get_general_category (&buffer->cur()))) &&
 	/* If there's anything between the starter and this char, they should have CCC
 	 * smaller than this character's. */
 	(starter == buffer->out_len - 1 ||
diff --git a/src/hb-ot-shape-private.hh b/src/hb-ot-shape-private.hh
index 23e80b7..9599f8e 100644
--- a/src/hb-ot-shape-private.hh
+++ b/src/hb-ot-shape-private.hh
@@ -33,10 +33,6 @@
 
 
 
-/* buffer var allocations, used during the entire shaping process */
-#define unicode_props0()	var2.u8[0]
-#define unicode_props1()	var2.u8[1]
-
 
 
 struct hb_ot_shape_plan_t
@@ -89,37 +85,4 @@
 };
 
 
-
-inline void
-_hb_glyph_info_set_unicode_props (hb_glyph_info_t *info, hb_unicode_funcs_t *unicode)
-{
-  info->unicode_props0() = ((unsigned int) unicode->general_category (info->codepoint)) |
-			   (unicode->is_default_ignorable (info->codepoint) ? 0x80 : 0);
-  info->unicode_props1() = unicode->modified_combining_class (info->codepoint);
-}
-
-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_default_ignorable (const hb_glyph_info_t *info)
-{
-  return !!(info->unicode_props0() & 0x80);
-}
-
 #endif /* HB_OT_SHAPE_PRIVATE_HH */
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index 96461d7..f65861f 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -83,12 +83,12 @@
 
   switch (props->direction) {
     case HB_DIRECTION_LTR:
-      map->add_bool_feature (HB_TAG ('l','t','r','a'));
-      map->add_bool_feature (HB_TAG ('l','t','r','m'));
+      map->add_global_bool_feature (HB_TAG ('l','t','r','a'));
+      map->add_global_bool_feature (HB_TAG ('l','t','r','m'));
       break;
     case HB_DIRECTION_RTL:
-      map->add_bool_feature (HB_TAG ('r','t','l','a'));
-      map->add_bool_feature (HB_TAG ('r','t','l','m'), false);
+      map->add_global_bool_feature (HB_TAG ('r','t','l','a'));
+      map->add_feature (HB_TAG ('r','t','l','m'), 1, F_NONE);
       break;
     case HB_DIRECTION_TTB:
     case HB_DIRECTION_BTT:
@@ -97,30 +97,31 @@
       break;
   }
 
-#define ADD_FEATURES(array) \
-  HB_STMT_START { \
-    for (unsigned int i = 0; i < ARRAY_LENGTH (array); i++) \
-      map->add_bool_feature (array[i]); \
-  } HB_STMT_END
-
   if (planner->shaper->collect_features)
     planner->shaper->collect_features (planner);
 
-  ADD_FEATURES (common_features);
+  for (unsigned int i = 0; i < ARRAY_LENGTH (common_features); i++)
+    map->add_global_bool_feature (common_features[i]);
 
   if (HB_DIRECTION_IS_HORIZONTAL (props->direction))
-    ADD_FEATURES (horizontal_features);
+    for (unsigned int i = 0; i < ARRAY_LENGTH (horizontal_features); i++)
+      map->add_feature (horizontal_features[i], 1, F_GLOBAL |
+			(horizontal_features[i] == HB_TAG('k','e','r','n') ?
+			 F_HAS_FALLBACK : F_NONE));
   else
-    ADD_FEATURES (vertical_features);
+    for (unsigned int i = 0; i < ARRAY_LENGTH (vertical_features); i++)
+      map->add_feature (vertical_features[i], 1, F_GLOBAL |
+			(vertical_features[i] == HB_TAG('v','k','r','n') ?
+			 F_HAS_FALLBACK : F_NONE));
 
   if (planner->shaper->override_features)
     planner->shaper->override_features (planner);
 
-#undef ADD_FEATURES
-
   for (unsigned int i = 0; i < num_user_features; i++) {
     const hb_feature_t *feature = &user_features[i];
-    map->add_feature (feature->tag, feature->value, (feature->start == 0 && feature->end == (unsigned int) -1));
+    map->add_feature (feature->tag, feature->value,
+		      (feature->start == 0 && feature->end == (unsigned int) -1) ?
+		       F_GLOBAL : F_NONE);
   }
 }
 
@@ -405,7 +406,8 @@
   hb_ot_layout_position_start (c->font, c->buffer);
 
   unsigned int count = c->buffer->len;
-  for (unsigned int i = 0; i < count; i++) {
+  for (unsigned int i = 0; i < count; i++)
+  {
     c->font->get_glyph_advance_for_direction (c->buffer->info[i].codepoint,
 					      c->buffer->props.direction,
 					      &c->buffer->pos[i].x_advance,
@@ -414,6 +416,26 @@
 						  c->buffer->props.direction,
 						  &c->buffer->pos[i].x_offset,
 						  &c->buffer->pos[i].y_offset);
+
+  }
+
+  /* Zero'ing mark widths by GDEF (as used in Myanmar spec) happens
+   * *before* GPOS. */
+  switch (c->plan->shaper->zero_width_marks)
+  {
+    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF:
+      for (unsigned int i = 0; i < count; i++)
+	if ((c->buffer->info[i].glyph_props() & HB_OT_LAYOUT_GLYPH_PROPS_MARK))
+	{
+	  c->buffer->pos[i].x_advance = 0;
+	  c->buffer->pos[i].y_advance = 0;
+	}
+      break;
+
+    default:
+    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE:
+    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE:
+      break;
   }
 }
 
@@ -421,12 +443,12 @@
 hb_ot_position_complex (hb_ot_shape_context_t *c)
 {
   bool ret = false;
+  unsigned int count = c->buffer->len;
 
   if (hb_ot_layout_has_positioning (c->face))
   {
     /* Change glyph origin to what GPOS expects, apply GPOS, change it back. */
 
-    unsigned int count = c->buffer->len;
     for (unsigned int i = 0; i < count; i++) {
       c->font->add_glyph_origin_for_direction (c->buffer->info[i].codepoint,
 					       HB_DIRECTION_LTR,
@@ -446,37 +468,31 @@
     ret = true;
   }
 
-  hb_ot_layout_position_finish (c->font, c->buffer, c->plan->shaper->zero_width_attached_marks);
+  /* Zero'ing mark widths by Unicode happens
+   * *after* GPOS. */
+  switch (c->plan->shaper->zero_width_marks)
+  {
+    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_UNICODE:
+      for (unsigned int i = 0; i < count; i++)
+	if (_hb_glyph_info_get_general_category (&c->buffer->info[i]) == HB_UNICODE_GENERAL_CATEGORY_NON_SPACING_MARK)
+	{
+	  c->buffer->pos[i].x_advance = 0;
+	  c->buffer->pos[i].y_advance = 0;
+	}
+      break;
+
+    default:
+    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_NONE:
+    case HB_OT_SHAPE_ZERO_WIDTH_MARKS_BY_GDEF:
+      break;
+  }
+
+  hb_ot_layout_position_finish (c->font, c->buffer);
 
   return ret;
 }
 
 static inline void
-hb_ot_truetype_kern (hb_ot_shape_context_t *c)
-{
-  /* TODO Check for kern=0 */
-  unsigned int count = c->buffer->len;
-  for (unsigned int i = 1; i < count; i++) {
-    hb_position_t x_kern, y_kern, kern1, kern2;
-    c->font->get_glyph_kerning_for_direction (c->buffer->info[i - 1].codepoint, c->buffer->info[i].codepoint,
-					      c->buffer->props.direction,
-					      &x_kern, &y_kern);
-
-    kern1 = x_kern >> 1;
-    kern2 = x_kern - kern1;
-    c->buffer->pos[i - 1].x_advance += kern1;
-    c->buffer->pos[i].x_advance += kern2;
-    c->buffer->pos[i].x_offset += kern2;
-
-    kern1 = y_kern >> 1;
-    kern2 = y_kern - kern1;
-    c->buffer->pos[i - 1].y_advance += kern1;
-    c->buffer->pos[i].y_advance += kern2;
-    c->buffer->pos[i].y_offset += kern2;
-  }
-}
-
-static inline void
 hb_ot_position (hb_ot_shape_context_t *c)
 {
   hb_ot_position_default (c);
@@ -492,7 +508,7 @@
   /* Visual fallback goes here. */
 
   if (fallback)
-    hb_ot_truetype_kern (c);
+    _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer);
 }
 
 
@@ -611,8 +627,6 @@
 {
   hb_ot_shape_plan_t plan;
 
-  buffer->guess_segment_properties ();
-
   const char *shapers[] = {"ot", NULL};
   hb_shape_plan_t *shape_plan = hb_shape_plan_create_cached (font->face, &buffer->props,
 							     features, num_features, shapers);
diff --git a/src/hb-private.hh b/src/hb-private.hh
index be0d505..ff1e85d 100644
--- a/src/hb-private.hh
+++ b/src/hb-private.hh
@@ -376,6 +376,14 @@
   }
 };
 
+#define HB_AUTO_ARRAY_PREALLOCED 64
+template <typename Type>
+struct hb_auto_array_t : hb_prealloced_array_t <Type, HB_AUTO_ARRAY_PREALLOCED>
+{
+  hb_auto_array_t (void) { hb_prealloced_array_t<Type, HB_AUTO_ARRAY_PREALLOCED>::init (); }
+  ~hb_auto_array_t (void) { hb_prealloced_array_t<Type, HB_AUTO_ARRAY_PREALLOCED>::finish (); }
+};
+
 
 #define HB_LOCKABLE_SET_INIT {HB_PREALLOCED_ARRAY_INIT}
 template <typename item_t, typename lock_t>
@@ -516,10 +524,12 @@
 
 /* ASCII tag/character handling */
 
-static inline unsigned char ISALPHA (unsigned char c)
+static inline bool ISALPHA (unsigned char c)
 { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); }
-static inline unsigned char ISALNUM (unsigned char c)
+static inline bool ISALNUM (unsigned char c)
 { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9'); }
+static inline bool ISSPACE (unsigned char c)
+{ return c == ' ' || c =='\f'|| c =='\n'|| c =='\r'|| c =='\t'|| c =='\v'; }
 static inline unsigned char TOUPPER (unsigned char c)
 { return (c >= 'a' && c <= 'z') ? c - 'a' + 'A' : c; }
 static inline unsigned char TOLOWER (unsigned char c)
@@ -594,6 +604,8 @@
 
   if (func)
   {
+    unsigned int func_len = strlen (func);
+#ifndef HB_DEBUG_VERBOSE
     /* Skip "typename" */
     if (0 == strncmp (func, "typename ", 9))
       func += 9;
@@ -603,7 +615,9 @@
       func = space + 1;
     /* Skip parameter list */
     const char *paren = strchr (func, '(');
-    unsigned int func_len = paren ? paren - func : strlen (func);
+    if (paren)
+      func_len = paren - func;
+#endif
     fprintf (stderr, "%.*s: ", func_len, func);
   }
 
@@ -841,8 +855,9 @@
 {
   /* Pain because we don't know whether s is nul-terminated. */
   char buf[64];
-  strncpy (buf, s, MIN (ARRAY_LENGTH (buf) - 1, len));
-  buf[MIN (ARRAY_LENGTH (buf) - 1, len)] = '\0';
+  len = MIN (ARRAY_LENGTH (buf) - 1, len);
+  strncpy (buf, s, len);
+  buf[len] = '\0';
 
   char *end;
   errno = 0;
@@ -854,4 +869,33 @@
 }
 
 
+/* Global runtime options. */
+
+struct hb_options_t
+{
+  int initialized : 1;
+  int uniscribe_bug_compatible : 1;
+};
+
+union hb_options_union_t {
+  int i;
+  hb_options_t opts;
+};
+ASSERT_STATIC (sizeof (int) == sizeof (hb_options_union_t));
+
+HB_INTERNAL void
+_hb_options_init (void);
+
+extern HB_INTERNAL hb_options_union_t _hb_options;
+
+static inline hb_options_t
+hb_options (void)
+{
+  if (unlikely (!_hb_options.i))
+    _hb_options_init ();
+
+  return _hb_options.opts;
+}
+
+
 #endif /* HB_PRIVATE_HH */
diff --git a/src/hb-shape-plan.cc b/src/hb-shape-plan.cc
index 22a226f..a6d2d26 100644
--- a/src/hb-shape-plan.cc
+++ b/src/hb-shape-plan.cc
@@ -27,6 +27,7 @@
 #include "hb-shape-plan-private.hh"
 #include "hb-shaper-private.hh"
 #include "hb-font-private.hh"
+#include "hb-buffer-private.hh"
 
 #define HB_SHAPER_IMPLEMENT(shaper) \
 	HB_SHAPER_DATA_ENSURE_DECLARE(shaper, face) \
@@ -178,9 +179,14 @@
 		       const hb_feature_t *features,
 		       unsigned int        num_features)
 {
-  if (unlikely (shape_plan->face != font->face))
+  if (unlikely (hb_object_is_inert (shape_plan) ||
+		hb_object_is_inert (font) ||
+		hb_object_is_inert (buffer)))
     return false;
 
+  assert (shape_plan->face == font->face);
+  assert (hb_segment_properties_equal (&shape_plan->props, &buffer->props));
+
 #define HB_SHAPER_EXECUTE(shaper) \
 	HB_STMT_START { \
 	  return HB_SHAPER_DATA (shaper, shape_plan) && \
diff --git a/src/hb-shape.cc b/src/hb-shape.cc
index 389ce3e..67ef7e6 100644
--- a/src/hb-shape.cc
+++ b/src/hb-shape.cc
@@ -38,10 +38,8 @@
 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
@@ -60,16 +58,19 @@
 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';
+  unsigned int len = MIN (ARRAY_LENGTH (buf) - 1, (unsigned int) (end - *pp));
+  strncpy (buf, *pp, len);
+  buf[len] = '\0';
 
   char *p = buf;
   char *pend = p;
   unsigned int v;
 
+  /* Intentionally use strtol instead of strtoul, such that
+   * -1 turns into "big number"... */
+  errno = 0;
   v = strtol (p, &pend, 0);
-
-  if (p == pend)
+  if (errno || p == pend)
     return false;
 
   *pv = v;
@@ -202,7 +203,7 @@
 
 static const char **static_shaper_list;
 
-static
+static inline
 void free_static_shaper_list (void)
 {
   free (static_shaper_list);
@@ -255,8 +256,6 @@
 
   assert (buffer->content_type == HB_BUFFER_CONTENT_TYPE_UNICODE);
 
-  buffer->guess_segment_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);
diff --git a/src/hb-shaper-private.hh b/src/hb-shaper-private.hh
index 9d30c1e..29c4493 100644
--- a/src/hb-shaper-private.hh
+++ b/src/hb-shaper-private.hh
@@ -95,7 +95,10 @@
     if (unlikely (!data)) \
       data = (HB_SHAPER_DATA_TYPE (shaper, object) *) HB_SHAPER_DATA_INVALID; \
     if (!hb_atomic_ptr_cmpexch (&HB_SHAPER_DATA (shaper, object), NULL, data)) { \
-      HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (data); \
+      if (data && \
+	  data != HB_SHAPER_DATA_INVALID && \
+	  data != HB_SHAPER_DATA_SUCCEEDED) \
+	HB_SHAPER_DATA_DESTROY_FUNC (shaper, object) (data); \
       goto retry; \
     } \
   } \
diff --git a/src/hb-shaper.cc b/src/hb-shaper.cc
index 1c1aed9..44f718a 100644
--- a/src/hb-shaper.cc
+++ b/src/hb-shaper.cc
@@ -40,7 +40,7 @@
 
 static const hb_shaper_pair_t *static_shapers;
 
-static
+static inline
 void free_static_shapers (void)
 {
   if (unlikely (static_shapers != all_shapers))
diff --git a/src/hb-unicode-private.hh b/src/hb-unicode-private.hh
index 7be4b04..155a8a3 100644
--- a/src/hb-unicode-private.hh
+++ b/src/hb-unicode-private.hh
@@ -107,6 +107,9 @@
   unsigned int
   modified_combining_class (hb_codepoint_t unicode)
   {
+    /* XXX This hack belongs to the Myanmar shaper. */
+    if (unicode == 0x1037) unicode = 0x103A;
+
     return _hb_modified_combining_class[combining_class (unicode)];
   }
 
@@ -126,6 +129,11 @@
    * be.  That has been reported to the Unicode Technical Committee for
    * consideration.  As such, we include it here, since Uniscribe removes it.
    *
+   * Note: While U+115F and U+1160 are Default_Ignorable, we do NOT want to
+   * hide them, as the way Uniscribe has implemented them is with regular
+   * spacing glyphs, and that's the way fonts are made to work.  As such,
+   * we make exceptions for those two.
+   *
    * Gathered from:
    * http://unicode.org/cldr/utility/list-unicodeset.jsp?a=[:DI:]&abb=on&ucd=on&esc=on
    *
@@ -138,8 +146,8 @@
    *
    * 00AD ;SOFT HYPHEN
    * 034F ;COMBINING GRAPHEME JOINER
-   * 115F ;HANGUL CHOSEONG FILLER
-   * 1160 ;HANGUL JUNGSEONG FILLER
+   * #115F ;HANGUL CHOSEONG FILLER
+   * #1160 ;HANGUL JUNGSEONG FILLER
    * 17B4 ;KHMER VOWEL INHERENT AQ
    * 17B5 ;KHMER VOWEL INHERENT AA
    * 180B..180D ;MONGOLIAN FREE VARIATION SELECTOR THREE
@@ -165,7 +173,6 @@
       switch (page) {
 	case 0x00: return unlikely (ch == 0x00AD);
 	case 0x03: return unlikely (ch == 0x034F);
-	case 0x11: return hb_in_range<hb_codepoint_t> (ch, 0x115F, 0x1160);
 	case 0x17: return hb_in_range<hb_codepoint_t> (ch, 0x17B4, 0x17B5);
 	case 0x18: return hb_in_range<hb_codepoint_t> (ch, 0x180B, 0x180E);
 	case 0x20: return hb_in_ranges<hb_codepoint_t> (ch, 0x200B, 0x200F,
diff --git a/src/hb-warning.cc b/src/hb-warning.cc
index 01adcea..4f1f65f 100644
--- a/src/hb-warning.cc
+++ b/src/hb-warning.cc
@@ -29,11 +29,38 @@
 
 
 #if defined(HB_ATOMIC_INT_NIL)
-#pragma message("Could not find any system to define atomic_int macros, library may NOT be thread-safe.")
+#ifdef _MSC_VER
+#pragma message("Could not find any system to define atomic_int macros, library may NOT be thread-safe")
+#else
+#warning "Could not find any system to define atomic_int macros, library may NOT be thread-safe"
 #endif
+#endif
+
 #if defined(HB_MUTEX_IMPL_NIL)
-#pragma message("Could not find any system to define mutex macros, library may NOT be thread-safe.")
+#ifdef _MSC_VER
+#pragma message("Could not find any system to define mutex macros, library may NOT be thread-safe")
+#else
+#warning "Could not find any system to define mutex macros, library may NOT be thread-safe"
 #endif
+#endif
+
 #if defined(HB_ATOMIC_INT_NIL) || defined(HB_MUTEX_IMPL_NIL)
-#pragma message("To suppress these warnings, define HB_NO_MT.")
+#ifdef _MSC_VER
+#pragma message("To suppress these warnings, define HB_NO_MT")
+#else
+#warning "To suppress these warnings, define HB_NO_MT"
+#endif
+#endif
+
+
+#include "hb-unicode-private.hh"
+
+#if !defined(HB_NO_UNICODE_FUNCS) && defined(HB_UNICODE_FUNCS_NIL)
+#ifdef _MSC_VER
+#pragma message("Could not find any Unicode functions implementation, you have to provide your own")
+#pragma message("To suppress this warnings, define HB_NO_UNICODE_FUNCS")
+#else
+#warning "Could not find any Unicode functions implementation, you have to provide your own"
+#warning "To suppress this warning, define HB_NO_UNICODE_FUNCS"
+#endif
 #endif
diff --git a/src/test-buffer-serialize.cc b/src/test-buffer-serialize.cc
new file mode 100644
index 0000000..3577dbf
--- /dev/null
+++ b/src/test-buffer-serialize.cc
@@ -0,0 +1,126 @@
+/*
+ * Copyright © 2010,2011,2013  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
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "hb.h"
+#ifdef HAVE_FREETYPE
+#include "hb-ft.h"
+#endif
+
+#ifdef HAVE_GLIB
+#include <glib.h>
+#endif
+#include <stdlib.h>
+#include <stdio.h>
+
+int
+main (int argc, char **argv)
+{
+  hb_blob_t *blob = NULL;
+
+  if (argc != 2) {
+    fprintf (stderr, "usage: %s font-file\n", argv[0]);
+    exit (1);
+  }
+
+  /* Create the blob */
+  {
+    const char *font_data;
+    unsigned int len;
+    hb_destroy_func_t destroy;
+    void *user_data;
+    hb_memory_mode_t mm;
+
+#ifdef HAVE_GLIB
+    GMappedFile *mf = g_mapped_file_new (argv[1], false, NULL);
+    font_data = g_mapped_file_get_contents (mf);
+    len = g_mapped_file_get_length (mf);
+    destroy = (hb_destroy_func_t) g_mapped_file_unref;
+    user_data = (void *) mf;
+    mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE;
+#else
+    FILE *f = fopen (argv[1], "rb");
+    fseek (f, 0, SEEK_END);
+    len = ftell (f);
+    fseek (f, 0, SEEK_SET);
+    font_data = (const char *) malloc (len);
+    if (!font_data) len = 0;
+    len = fread ((char *) font_data, 1, len, f);
+    destroy = free;
+    user_data = (void *) font_data;
+    fclose (f);
+    mm = HB_MEMORY_MODE_WRITABLE;
+#endif
+
+    blob = hb_blob_create (font_data, len, mm, user_data, destroy);
+  }
+
+  hb_face_t *face = hb_face_create (blob, 0 /* first face */);
+  hb_blob_destroy (blob);
+  blob = NULL;
+
+  unsigned int upem = hb_face_get_upem (face);
+  hb_font_t *font = hb_font_create (face);
+  hb_face_destroy (face);
+  hb_font_set_scale (font, upem, upem);
+#ifdef HAVE_FREETYPE
+  hb_ft_font_set_funcs (font);
+#endif
+
+  hb_buffer_t *buf;
+  buf = hb_buffer_create ();
+
+  bool ret = true;
+  char line[BUFSIZ], out[BUFSIZ];
+  while (fgets (line, sizeof(line), stdin) != 0)
+  {
+    hb_buffer_clear_contents (buf);
+
+    const char *p = line;
+    while (hb_buffer_deserialize_glyphs (buf,
+					 p, -1, &p,
+					 font,
+					 HB_BUFFER_SERIALIZE_FORMAT_JSON))
+      ;
+    if (*p && *p != '\n')
+      ret = false;
+
+    hb_buffer_serialize_glyphs (buf, 0, hb_buffer_get_length (buf),
+				out, sizeof (out), NULL,
+				font, HB_BUFFER_SERIALIZE_FORMAT_JSON,
+				HB_BUFFER_SERIALIZE_FLAGS_DEFAULT);
+    puts (out);
+  }
+
+  hb_buffer_destroy (buf);
+
+  hb_font_destroy (font);
+
+  return !ret;
+}
diff --git a/test/.valgrind-suppressions b/test/api/.valgrind-suppressions
similarity index 100%
rename from test/.valgrind-suppressions
rename to test/api/.valgrind-suppressions
diff --git a/test/api/Makefile.am b/test/api/Makefile.am
index 237f92c..0ed943b 100644
--- a/test/api/Makefile.am
+++ b/test/api/Makefile.am
@@ -67,7 +67,7 @@
 	G_DEBUG=gc-friendly \
 	G_SLICE=always-malloc \
 	srcdir=$(srcdir) \
-	$(ENV)
+	$(NULL)
 
 
 # check-tool: Run tests under $(TOOL)
@@ -95,8 +95,8 @@
 	$(EXTRA_VALGRIND_FLAGS)
 #	Can't do for now: --show-reachable=yes
 CLEANFILES +=  log-valgrind.txt
-valgrind_verbose = $(valgrind_verbose_$(V))
-valgrind_verbose_ = $(valgrind_verbose_$(AM_DEFAULT_VERBOSITY))
+valgrind_verbose = $(valgrind_verbose_@AM_V@)
+valgrind_verbose_ = $(valgrind_verbose_@AM_DEFAULT_V@)
 valgrind_verbose_0 = | \
 	grep '\(^[^=]\|ERROR SUMMARY\|definitely lost\|indirectly lost\)' | grep -v ': 0'
 # TODO: The following check does not fail if valgrind finds error.  It should.
diff --git a/test/api/test-blob.c b/test/api/test-blob.c
index 0e65e2f..6759920 100644
--- a/test/api/test-blob.c
+++ b/test/api/test-blob.c
@@ -262,16 +262,61 @@
 test_blob_subblob (fixture_t *fixture, gconstpointer user_data)
 {
   hb_blob_t *b = fixture->blob;
+  hb_memory_mode_t mm = GPOINTER_TO_INT (user_data);
+  unsigned int len;
+  const char *data;
+  char *data_writable;
+  unsigned int i;
 
-  fixture->len -= 2;
-  fixture->data++;
-  fixture->blob = hb_blob_create_sub_blob (b, 1, fixture->len);
+  if (mm == HB_MEMORY_MODE_DUPLICATE) {
+    g_assert_cmpint (fixture->freed, ==, 1);
+    fixture->data = hb_blob_get_data (b, NULL);
+  } else {
+    g_assert_cmpint (fixture->freed, ==, 0);
+  }
+  fixture->blob = hb_blob_create_sub_blob (b, 1, fixture->len - 2);
   hb_blob_destroy (b);
+  b = fixture->blob;
 
-  test_blob (fixture, user_data);
+  /* A sub-blob is always created READONLY. */
 
-  fixture->data--;
-  fixture->len += 2;
+  g_assert (b);
+
+  len = hb_blob_get_length (b);
+  g_assert_cmpint (len, ==, fixture->len - 2);
+
+  data = hb_blob_get_data (b, &len);
+  g_assert_cmpint (len, ==, fixture->len - 2);
+  g_assert (data == fixture->data + 1);
+
+  data_writable = hb_blob_get_data_writable (b, &len);
+  g_assert_cmpint (len, ==, fixture->len - 2);
+  g_assert (data_writable);
+  if (mm == HB_MEMORY_MODE_READONLY)
+    g_assert (0 == memcmp (data_writable, fixture->data + 1, fixture->len - 2));
+  g_assert (data_writable != data);
+  g_assert_cmpint (fixture->freed, ==, 1);
+
+  data = hb_blob_get_data (b, &len);
+  g_assert_cmpint (len, ==, fixture->len - 2);
+  g_assert (data == data_writable);
+
+  memset (data_writable, 0, fixture->len - 2);
+
+  /* Now, make it immutable and watch get_data_writable() fail */
+
+  g_assert (!hb_blob_is_immutable (b));
+  hb_blob_make_immutable (b);
+  g_assert (hb_blob_is_immutable (b));
+
+  data_writable = hb_blob_get_data_writable (b, &len);
+  g_assert (!data_writable);
+  g_assert_cmpint (len, ==, 0);
+
+  data = hb_blob_get_data (b, &len);
+  g_assert_cmpint (len, ==, fixture->len - 2);
+  for (i = 0; i < len; i++)
+    g_assert ('\0' == data[i]);
 }
 
 
diff --git a/test/shaping/texts/in-tree/MANIFEST b/test/shaping/texts/in-tree/MANIFEST
index 41aa748..5fd8eb9 100644
--- a/test/shaping/texts/in-tree/MANIFEST
+++ b/test/shaping/texts/in-tree/MANIFEST
@@ -1,5 +1,6 @@
 shaper-arabic
 shaper-default
-shaper-hangul
 shaper-indic
+shaper-myanmar
+shaper-sea
 shaper-thai
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-syriac/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-arabic/script-syriac/misc/MANIFEST
index e69de29..ae45bdf 100644
--- a/test/shaping/texts/in-tree/shaper-arabic/script-syriac/misc/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-arabic/script-syriac/misc/MANIFEST
@@ -0,0 +1 @@
+alaph.txt
diff --git a/test/shaping/texts/in-tree/shaper-arabic/script-syriac/misc/alaph.txt b/test/shaping/texts/in-tree/shaper-arabic/script-syriac/misc/alaph.txt
new file mode 100644
index 0000000..27b035f
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-arabic/script-syriac/misc/alaph.txt
@@ -0,0 +1,98 @@
+ ܐ
+ ܐܘ
+ ܐܪ
+ ܐܖ
+ ܐܕ
+ ܐܯ
+ ܐܒ
+ܘܐ
+ܘܐܘ
+ܘܐܪ
+ܘܐܖ
+ܘܐܕ
+ܘܐܯ
+ܘܐܒ
+ܪܐ
+ܪܐܘ
+ܪܐܪ
+ܪܐܖ
+ܪܐܕ
+ܪܐܯ
+ܪܐܒ
+ܖܐ
+ܖܐܘ
+ܖܐܪ
+ܖܐܖ
+ܖܐܕ
+ܖܐܯ
+ܖܐܒ
+ܕܐ
+ܕܐܘ
+ܕܐܪ
+ܕܐܖ
+ܕܐܕ
+ܕܐܯ
+ܕܐܒ
+ܯܐ
+ܯܐܘ
+ܯܐܪ
+ܯܐܖ
+ܯܐܕ
+ܯܐܯ
+ܯܐܒ
+ܒܐ
+ܒܐܘ
+ܒܐܪ
+ܒܐܖ
+ܒܐܕ
+ܒܐܯ
+ܒܐܒ
+ ܐܐ
+ ܐܐܘ
+ ܐܐܪ
+ ܐܐܖ
+ ܐܐܕ
+ ܐܐܯ
+ ܐܐܒ
+ܘܐܐ
+ܘܐܐܘ
+ܘܐܐܪ
+ܘܐܐܖ
+ܘܐܐܕ
+ܘܐܐܯ
+ܘܐܐܒ
+ܪܐܐ
+ܪܐܐܘ
+ܪܐܐܪ
+ܪܐܐܖ
+ܪܐܐܕ
+ܪܐܐܯ
+ܪܐܐܒ
+ܖܐܐ
+ܖܐܐܘ
+ܖܐܐܪ
+ܖܐܐܖ
+ܖܐܐܕ
+ܖܐܐܯ
+ܖܐܐܒ
+ܕܐܐ
+ܕܐܐܘ
+ܕܐܐܪ
+ܕܐܐܖ
+ܕܐܐܕ
+ܕܐܐܯ
+ܕܐܐܒ
+ܯܐܐ
+ܯܐܐܘ
+ܯܐܐܪ
+ܯܐܐܖ
+ܯܐܐܕ
+ܯܐܐܯ
+ܯܐܐܒ
+ܒܐܐ
+ܒܐܐܘ
+ܒܐܐܪ
+ܒܐܐܖ
+ܒܐܐܕ
+ܒܐܐܯ
+ܒܐܐܒ
diff --git a/test/shaping/texts/in-tree/shaper-default/MANIFEST b/test/shaping/texts/in-tree/shaper-default/MANIFEST
index f2a6e7d..7682db4 100644
--- a/test/shaping/texts/in-tree/shaper-default/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-default/MANIFEST
@@ -1,6 +1,8 @@
 script-ethiopic
 script-han
+script-hangul
 script-hebrew
 script-hiragana
 script-linear-b
+script-tibetan
 script-tifinagh
diff --git a/test/shaping/texts/in-tree/shaper-hangul/script-hangul/MANIFEST b/test/shaping/texts/in-tree/shaper-default/script-hangul/MANIFEST
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-hangul/script-hangul/MANIFEST
rename to test/shaping/texts/in-tree/shaper-default/script-hangul/MANIFEST
diff --git a/test/shaping/texts/in-tree/shaper-hangul/script-hangul/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-default/script-hangul/misc/MANIFEST
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-hangul/script-hangul/misc/MANIFEST
rename to test/shaping/texts/in-tree/shaper-default/script-hangul/misc/MANIFEST
diff --git a/test/shaping/texts/in-tree/shaper-hangul/script-hangul/misc/misc.txt b/test/shaping/texts/in-tree/shaper-default/script-hangul/misc/misc.txt
similarity index 82%
rename from test/shaping/texts/in-tree/shaper-hangul/script-hangul/misc/misc.txt
rename to test/shaping/texts/in-tree/shaper-default/script-hangul/misc/misc.txt
index 5b6edcc..797b1c6 100644
--- a/test/shaping/texts/in-tree/shaper-hangul/script-hangul/misc/misc.txt
+++ b/test/shaping/texts/in-tree/shaper-default/script-hangul/misc/misc.txt
@@ -1,3 +1,4 @@
 휴가 가-- (오--)
 휴가 가-- (오--)
 ᄒᆞᆫ
+ᅟᅡᄫᅠ
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-asian/script-tibetan/MANIFEST b/test/shaping/texts/in-tree/shaper-default/script-tibetan/MANIFEST
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/south-asian/script-tibetan/MANIFEST
rename to test/shaping/texts/in-tree/shaper-default/script-tibetan/MANIFEST
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-asian/script-tibetan/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-default/script-tibetan/misc/MANIFEST
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/south-asian/script-tibetan/misc/MANIFEST
rename to test/shaping/texts/in-tree/shaper-default/script-tibetan/misc/MANIFEST
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-asian/script-tibetan/misc/misc.txt b/test/shaping/texts/in-tree/shaper-default/script-tibetan/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/south-asian/script-tibetan/misc/misc.txt
rename to test/shaping/texts/in-tree/shaper-default/script-tibetan/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-hangul/MANIFEST b/test/shaping/texts/in-tree/shaper-hangul/MANIFEST
deleted file mode 100644
index ea81716..0000000
--- a/test/shaping/texts/in-tree/shaper-hangul/MANIFEST
+++ /dev/null
@@ -1 +0,0 @@
-script-hangul
diff --git a/test/shaping/texts/in-tree/shaper-indic/MANIFEST b/test/shaping/texts/in-tree/shaper-indic/MANIFEST
index 5e0651b..3f2011f 100644
--- a/test/shaping/texts/in-tree/shaper-indic/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-indic/MANIFEST
@@ -1,3 +1,2 @@
 indic
-south-asian
 south-east-asian
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/misc/misc.txt b/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/misc/misc.txt
index 35ce952..aa43590 100644
--- a/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/misc/misc.txt
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-bengali/misc/misc.txt
@@ -50,3 +50,4 @@
 ন্ত্র
 ত্যু
 চ্য্র
+ক্‍ষ
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
index a68b307..c384b38 100644
--- a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/MANIFEST
@@ -1,4 +1,5 @@
 dottedcircle.txt
+eyelash.txt
 joiners.txt
 misc.txt
 spec-deviations.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/eyelash.txt b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/eyelash.txt
new file mode 100644
index 0000000..8e11955
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-devanagari/misc/eyelash.txt
@@ -0,0 +1,3 @@
+त्र्क
+त्र्‍क
+त्र्‌क
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/misc/misc.txt b/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/misc/misc.txt
index 19bec8c..a8a6325 100644
--- a/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/misc/misc.txt
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-kannada/misc/misc.txt
@@ -17,3 +17,4 @@
 ಕೋ
 ಕ್ಷ
 ಕ್ಷಿ
+ಚ್ಚ್
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/MANIFEST
index a6f4f00..48800d4 100644
--- a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/MANIFEST
@@ -1,2 +1,3 @@
+cibu.txt
 dot-reph.txt
 misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/cibu.txt b/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/cibu.txt
new file mode 100644
index 0000000..3d53867
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/cibu.txt
@@ -0,0 +1,188 @@
+2ാം
+2-ാം
+ല്ം
+എ്ന
+9-൹
+₹100
+൦
+൧
+൨
+൩
+൪
+൫
+൬
+൭
+൮
+൯
+൰
+൱
+൲
+൳
+൴
+൵
+അങ്ങ്
+അത്
+അർത്ഥം
+അന്ധൻ
+അന്യം
+അന്വയം
+അൽപ്പം
+അമ്മ
+അമ്ലം
+അല്പം
+അല
+അവൻ
+അവന്
+അവനു്
+അസോഽസൗ
+അഹല്യ
+അഺ്
+ആമ്പിൿ
+ആല
+ആാ
+ആാാാ
+ഇൻക
+ഇല്ല
+ഇല
+ഇള
+ഇഴ
+ഈറ
+ഈൗ
+ഉമ
+ഉള്ള
+ഊമ
+ഊൗ
+ഋതു
+ൠന്ന്
+ഌകാരം
+ൡതം
+എന്ന
+എന്റെ
+എലി
+എൻറോൾ
+ഏലം
+ഐക്യം
+ഒരു
+ഓരം
+ഓാാാ
+ഔഷധം
+ഔൗ
+കണ്ഢം
+കണ്ണ്
+കണ്വൻ
+കഥ
+കമ്പം
+കമ്രം
+കല്മഷം
+കല
+കാാ
+കീീ
+കുണ്ഠിതം
+കൂൂ
+കൄന്ന്
+കൢപ്തം
+കൣതം
+കൌതുകം
+ക്രൌഞ്ചം
+ഗങ്ഗ
+ഗരം
+ങഞ
+അച്ഛൻ
+ങ്യാവൂ
+ചരം
+ഛായ
+ജലം
+ജാള്യം
+ഝാൻസി
+ഞാൻ
+ടിപ്പു
+ഡപ്പി
+തത്ത
+തെരഞ്ഞെടുപ്പിന്‍െറ
+ദയ
+ദുഃഖം
+ദൃഢം
+ധനം
+നഖം
+നന്ദി
+നന്ന്
+നന്മ
+നാണ്യം
+തന്ത
+ന്രസ്ഥി
+പച്ച
+പട്ട
+പണ്ടു്
+പല
+പറ
+പാഠം
+പാണ്ഡു
+പാണ്ഡ്യൻ
+പാന്ഥൻ
+പാറ്റ
+പിന്നെ
+പുച്ഛം
+പുഞ്ച
+പൊൻനാണ്യം
+ഫലം
+ബലം
+ഭയം
+ഭാൎയ്യ
+ഭാര്യ
+മങ്ക
+മണം
+മണ്ട
+മ്അദനി
+മയം
+മേഘം
+മോഹന്‍ലാല്‍
+യതി
+രണ്ട്
+രമ്യം
+ലത
+അറബ്‌ബസ്സാർ
+ലോക്‌സഭ
+വഅള്
+വരം
+വാഞ്ഛ
+വില്വാദ്രി
+വെണ്മ
+ഷാരം
+ശ്രുതി
+ശരം
+ശാർങ്ഗപക്ഷി
+സമ്യക്
+സംയോഗം
+സംരംഭം
+സമ്രാട്ട്
+സസ്യം
+സാരം
+സ്രാവം
+സ്ലാവിക്
+സ്വരം
+സ്വാതന്ത്ര്യം
+സ്ട്രാപ്പ്
+സ്റ്റിംഗ്
+സ്റ്റ്രീം
+ഹാരം
+റിപ്പോര്‍ട്ട്
+  ന്‍റെ
+ന്റെ
+ൻ്റെ
+ച്ച്യൂ
+യ്ക്ക്യൂ
+ട്ട്യൂ
+യ‍്യ
+വ‍്വ
+ഹൈലൈറ്റ്സ്
+ ച്ല്‍സി
+മലയാളത്തില്‍
+ഡിപ്പാർട്ട്മെന്റിന്റെ
+യ്യ്ര
+ യ്യ്ര
+ ്യ്ര
+ ്യ്യ്ര
+വ്വ്ര
+ വ്വ്ര
+ ്വ്ര
+ ്വ്വ്ര
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/dot-reph.txt b/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/dot-reph.txt
index fc17a9b..d158b8f 100644
--- a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/dot-reph.txt
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/dot-reph.txt
@@ -1 +1,12 @@
 ൎക
+ൎക്ക്ര
+ൎന്ന
+ൎഗ്ഗ്രോ
+ൎഗ്രോ
+ൎഗോ
+ൎഗ
+ഗ്ഗ്രോ
+ഗ്ഗ്ര
+ഗ്ഗോ
+ഗ്ഗ
+ഗ്രോ
diff --git a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/misc.txt b/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/misc.txt
index 78fdeb8..c5144f0 100644
--- a/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/misc.txt
+++ b/test/shaping/texts/in-tree/shaper-indic/indic/script-malayalam/misc/misc.txt
@@ -61,3 +61,4 @@
 ല്‍പ്പേ
 ശിം‌
 കോം‌
+യ‍്യ
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-asian/MANIFEST b/test/shaping/texts/in-tree/shaper-indic/south-asian/MANIFEST
deleted file mode 100644
index 3ed6c85..0000000
--- a/test/shaping/texts/in-tree/shaper-indic/south-asian/MANIFEST
+++ /dev/null
@@ -1 +0,0 @@
-script-tibetan
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/MANIFEST b/test/shaping/texts/in-tree/shaper-indic/south-east-asian/MANIFEST
index 86c5b79..9627b9e 100644
--- a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-indic/south-east-asian/MANIFEST
@@ -1,3 +1 @@
 script-khmer
-script-myanmar
-script-thai
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-thai/MANIFEST b/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-thai/MANIFEST
deleted file mode 100644
index b8752e7..0000000
--- a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-thai/MANIFEST
+++ /dev/null
@@ -1 +0,0 @@
-misc
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-thai/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-thai/misc/MANIFEST
deleted file mode 100644
index 29cfb2f..0000000
--- a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-thai/misc/MANIFEST
+++ /dev/null
@@ -1 +0,0 @@
-misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-myanmar/MANIFEST b/test/shaping/texts/in-tree/shaper-myanmar/MANIFEST
new file mode 100644
index 0000000..895bcea
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-myanmar/MANIFEST
@@ -0,0 +1 @@
+script-myanmar
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/MANIFEST b/test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/MANIFEST
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/MANIFEST
rename to test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/MANIFEST
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/MANIFEST
similarity index 61%
rename from test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/misc/MANIFEST
rename to test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/MANIFEST
index 7f461ee..a2c86e3 100644
--- a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/misc/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/MANIFEST
@@ -1,2 +1,3 @@
 misc.txt
+torture.txt
 utn11.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/misc/misc.txt b/test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/misc/misc.txt
rename to test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/misc.txt
diff --git a/test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/torture.txt b/test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/torture.txt
new file mode 100644
index 0000000..faee302
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/torture.txt
@@ -0,0 +1,23 @@
+ᨣᩕ᩵ᩣᩴᨣᩕ᩠ᩅᩁ
+ᨣᩕ᩵ᩮ᩠ᨦᨣᩕᩢ᩠ᨯ
+ᩅᩥᨣᩅᩰᩬᩡ
+ᩋᩫᨶ᩠ᨲᩕᩣ᩠ᨿ
+ᨶᩫᨶ᩠ᨲᩕᩧ
+ᩈ᩠ᨾ᩵ᩣᩴᩈ᩠ᨾᩮᩬᩥ
+ᩀᩢ᩠᩵ᨦᨶ᩶ᩣᩴ
+ᩉᩫ᩠ᨯᩉ᩠ᨿᩬᩴ᩶
+ᩅᩥᨱ᩠ᨬᩣ᩠ᨱ
+ᨠᩕᩫ᩠ᨮᩉᩫ᩠ᩅᨷᩫ᩠ᩅ
+ᨷᩴ᩠᩵ᨯᩲ᩶
+ᨷᩴ᩠᩶ᨯᩲ᩵
+ᨺᩮᩥᩢ᩠ᨠ
+ᨡᩧ᩠᩶ᨦ᩻
+ᨸᩢ᩠᩶ᨶ
+ᨵᩥᩢ᩠᩶ᨶ
+ᨷᩣ᩠ᨦ
+ᨷᩤ᩠ᨦ
+ᨻᩮᩬᩧ᩵ᩋᩉᩲ᩶
+ᨾᩨᨤᩕᩢ᩠᩵ᨦ
+ᨴᩣᩴᩋᩁᩲ
+ᩈᩢᨬ᩠ᨬᩣ
+◌ᩲ
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/misc/utn11.txt b/test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/utn11.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-myanmar/misc/utn11.txt
rename to test/shaping/texts/in-tree/shaper-myanmar/script-myanmar/misc/utn11.txt
diff --git a/test/shaping/texts/in-tree/shaper-sea/MANIFEST b/test/shaping/texts/in-tree/shaper-sea/MANIFEST
new file mode 100644
index 0000000..ba95488
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-sea/MANIFEST
@@ -0,0 +1,3 @@
+script-cham
+script-new-tai-lue
+script-tai-tham
diff --git a/test/shaping/texts/in-tree/shaper-hangul/script-hangul/MANIFEST b/test/shaping/texts/in-tree/shaper-sea/script-cham/MANIFEST
similarity index 100%
copy from test/shaping/texts/in-tree/shaper-hangul/script-hangul/MANIFEST
copy to test/shaping/texts/in-tree/shaper-sea/script-cham/MANIFEST
diff --git a/test/shaping/texts/in-tree/shaper-hangul/script-hangul/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-sea/script-cham/misc/MANIFEST
similarity index 100%
copy from test/shaping/texts/in-tree/shaper-hangul/script-hangul/misc/MANIFEST
copy to test/shaping/texts/in-tree/shaper-sea/script-cham/misc/MANIFEST
diff --git a/test/shaping/texts/in-tree/shaper-sea/script-cham/misc/misc.txt b/test/shaping/texts/in-tree/shaper-sea/script-cham/misc/misc.txt
new file mode 100644
index 0000000..32b793a
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-sea/script-cham/misc/misc.txt
@@ -0,0 +1,3 @@
+ꩀꨴ
+ꨗꨪꨇꨮꩃꨯꨗꨱꨧꨩꩂꨯꨨꨱꩃꨨꨮ
+ꨆꨴꨯ
diff --git a/test/shaping/texts/in-tree/shaper-hangul/script-hangul/MANIFEST b/test/shaping/texts/in-tree/shaper-sea/script-new-tai-lue/MANIFEST
similarity index 100%
copy from test/shaping/texts/in-tree/shaper-hangul/script-hangul/MANIFEST
copy to test/shaping/texts/in-tree/shaper-sea/script-new-tai-lue/MANIFEST
diff --git a/test/shaping/texts/in-tree/shaper-hangul/script-hangul/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-sea/script-new-tai-lue/misc/MANIFEST
similarity index 100%
copy from test/shaping/texts/in-tree/shaper-hangul/script-hangul/misc/MANIFEST
copy to test/shaping/texts/in-tree/shaper-sea/script-new-tai-lue/misc/MANIFEST
diff --git a/test/shaping/texts/in-tree/shaper-sea/script-new-tai-lue/misc/misc.txt b/test/shaping/texts/in-tree/shaper-sea/script-new-tai-lue/misc/misc.txt
new file mode 100644
index 0000000..11224a1
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-sea/script-new-tai-lue/misc/misc.txt
@@ -0,0 +1 @@
+ᦀᦷᧃᧈ
diff --git a/test/shaping/texts/in-tree/shaper-hangul/script-hangul/MANIFEST b/test/shaping/texts/in-tree/shaper-sea/script-tai-tham/MANIFEST
similarity index 100%
copy from test/shaping/texts/in-tree/shaper-hangul/script-hangul/MANIFEST
copy to test/shaping/texts/in-tree/shaper-sea/script-tai-tham/MANIFEST
diff --git a/test/shaping/texts/in-tree/shaper-hangul/script-hangul/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-sea/script-tai-tham/misc/MANIFEST
similarity index 100%
copy from test/shaping/texts/in-tree/shaper-hangul/script-hangul/misc/MANIFEST
copy to test/shaping/texts/in-tree/shaper-sea/script-tai-tham/misc/MANIFEST
diff --git a/test/shaping/texts/in-tree/shaper-sea/script-tai-tham/misc/misc.txt b/test/shaping/texts/in-tree/shaper-sea/script-tai-tham/misc/misc.txt
new file mode 100644
index 0000000..62e9317
--- /dev/null
+++ b/test/shaping/texts/in-tree/shaper-sea/script-tai-tham/misc/misc.txt
@@ -0,0 +1,2 @@
+ᨠ᩠ᨠᩮᩕ
+ᩋᩫᨶ᩠ᨲᩕᩣ᩠ᨿ
diff --git a/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/MANIFEST b/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/MANIFEST
index 911099e..6b5ca6f 100644
--- a/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/MANIFEST
+++ b/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/MANIFEST
@@ -1,3 +1,4 @@
+misc.txt
 phinthu.txt
 pua-shaping.txt
 sara-am.txt
diff --git a/test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-thai/misc/misc.txt b/test/shaping/texts/in-tree/shaper-thai/script-thai/misc/misc.txt
similarity index 100%
rename from test/shaping/texts/in-tree/shaper-indic/south-east-asian/script-thai/misc/misc.txt
rename to test/shaping/texts/in-tree/shaper-thai/script-thai/misc/misc.txt
diff --git a/util/options.cc b/util/options.cc
index 17ad8e6..5e57548 100644
--- a/util/options.cc
+++ b/util/options.cc
@@ -413,8 +413,8 @@
       /* read it */
       GString *gs = g_string_new (NULL);
       char buf[BUFSIZ];
-#ifdef HAVE__SETMODE
-      _setmode (fileno (stdin), _O_BINARY);
+#if defined(_WIN32) || defined(__CYGWIN__)
+      setmode (fileno (stdin), _O_BINARY);
 #endif
       while (!feof (stdin)) {
 	size_t ret = fread (buf, 1, sizeof (buf), stdin);
@@ -557,8 +557,8 @@
   if (output_file)
     fp = fopen (output_file, "wb");
   else {
-#ifdef HAVE__SETMODE
-    _setmode (fileno (stdout), _O_BINARY);
+#if defined(_WIN32) || defined(__CYGWIN__)
+    setmode (fileno (stdout), _O_BINARY);
 #endif
     fp = stdout;
   }
diff --git a/util/options.hh b/util/options.hh
index ad925b2..35ea0bc 100644
--- a/util/options.hh
+++ b/util/options.hh
@@ -43,8 +43,8 @@
 #ifdef HAVE_UNISTD_H
 #include <unistd.h> /* for isatty() */
 #endif
-#ifdef HAVE_IO_H
-#include <io.h> /* for _setmode() under Windows */
+#if defined(_WIN32) || defined(__CYGWIN__)
+#include <io.h> /* for setmode() under Windows */
 #endif
 
 #include <hb.h>
@@ -170,6 +170,7 @@
 			 (bot ? HB_BUFFER_FLAG_BOT : 0) |
 			 (eot ? HB_BUFFER_FLAG_EOT : 0) |
 			 (preserve_default_ignorables ? HB_BUFFER_FLAG_PRESERVE_DEFAULT_IGNORABLES : 0)));
+    hb_buffer_guess_segment_properties (buffer);
   }
 
   void populate_buffer (hb_buffer_t *buffer, const char *text, int text_len,